more robust handling of proc signatures containing inter-param type references
This commit is contained in:
parent
7dcf6ff50b
commit
ac271e76b1
6 changed files with 106 additions and 30 deletions
|
|
@ -899,10 +899,13 @@ proc makeDeref(n: PNode): PNode =
|
||||||
addSon(result, a)
|
addSon(result, a)
|
||||||
t = skipTypes(t.sons[0], {tyGenericInst})
|
t = skipTypes(t.sons[0], {tyGenericInst})
|
||||||
|
|
||||||
proc readTypeParameter(c: PContext, ty: PType,
|
const tyTypeParamsHolders = {tyGenericInst, tyCompositeTypeClass}
|
||||||
|
|
||||||
|
proc readTypeParameter(c: PContext, typ: PType,
|
||||||
paramName: PIdent, info: TLineInfo): PNode =
|
paramName: PIdent, info: TLineInfo): PNode =
|
||||||
internalAssert ty.kind == tyGenericInst
|
let ty = if typ.kind == tyGenericInst: typ.skipGenericAlias
|
||||||
let ty = ty.skipGenericAlias
|
else: (internalAssert typ.kind == tyCompositeTypeClass; typ.sons[1])
|
||||||
|
|
||||||
let tbody = ty.sons[0]
|
let tbody = ty.sons[0]
|
||||||
for s in countup(0, tbody.len-2):
|
for s in countup(0, tbody.len-2):
|
||||||
let tParam = tbody.sons[s]
|
let tParam = tbody.sons[s]
|
||||||
|
|
@ -946,7 +949,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
result.typ = ty
|
result.typ = ty
|
||||||
markUsed(n, f)
|
markUsed(n, f)
|
||||||
return
|
return
|
||||||
of tyGenericInst:
|
of tyTypeParamsHolders:
|
||||||
return readTypeParameter(c, ty, i, n.info)
|
return readTypeParameter(c, ty, i, n.info)
|
||||||
of tyObject, tyTuple:
|
of tyObject, tyTuple:
|
||||||
if ty.n.kind == nkRecList:
|
if ty.n.kind == nkRecList:
|
||||||
|
|
@ -996,7 +999,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode =
|
||||||
result = n
|
result = n
|
||||||
|
|
||||||
# we didn't find any field, let's look for a generic param
|
# we didn't find any field, let's look for a generic param
|
||||||
if result == nil and n.sons[0].typ.kind == tyGenericInst:
|
if result == nil and n.sons[0].typ.kind in tyTypeParamsHolders:
|
||||||
result = readTypeParameter(c, n.sons[0].typ, i, n.info)
|
result = readTypeParameter(c, n.sons[0].typ, i, n.info)
|
||||||
|
|
||||||
proc dotTransformation(c: PContext, n: PNode): PNode =
|
proc dotTransformation(c: PContext, n: PNode): PNode =
|
||||||
|
|
|
||||||
|
|
@ -85,19 +85,21 @@ proc freshGenSyms(n: PNode, owner: PSym, symMap: var TIdTable) =
|
||||||
|
|
||||||
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
|
proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind)
|
||||||
|
|
||||||
|
proc addProcDecls(c: PContext, fn: PSym) =
|
||||||
|
# get the proc itself in scope (e.g. for recursion)
|
||||||
|
addDecl(c, fn)
|
||||||
|
|
||||||
|
for i in 1 .. <fn.typ.n.len:
|
||||||
|
var param = fn.typ.n.sons[i].sym
|
||||||
|
param.owner = fn
|
||||||
|
addParamOrResult(c, param, fn.kind)
|
||||||
|
|
||||||
|
maybeAddResult(c, fn, fn.ast)
|
||||||
|
|
||||||
proc instantiateBody(c: PContext, n: PNode, result: PSym) =
|
proc instantiateBody(c: PContext, n: PNode, result: PSym) =
|
||||||
if n.sons[bodyPos].kind != nkEmpty:
|
if n.sons[bodyPos].kind != nkEmpty:
|
||||||
inc c.inGenericInst
|
inc c.inGenericInst
|
||||||
# add it here, so that recursive generic procs are possible:
|
# add it here, so that recursive generic procs are possible:
|
||||||
addDecl(c, result)
|
|
||||||
pushProcCon(c, result)
|
|
||||||
# add params to scope
|
|
||||||
for i in 1 .. <result.typ.n.len:
|
|
||||||
var param = result.typ.n.sons[i].sym
|
|
||||||
param.owner = result
|
|
||||||
addParamOrResult(c, param, result.kind)
|
|
||||||
# debug result.typ.n
|
|
||||||
maybeAddResult(c, result, n)
|
|
||||||
var b = n.sons[bodyPos]
|
var b = n.sons[bodyPos]
|
||||||
var symMap: TIdTable
|
var symMap: TIdTable
|
||||||
initIdTable symMap
|
initIdTable symMap
|
||||||
|
|
@ -107,7 +109,6 @@ proc instantiateBody(c: PContext, n: PNode, result: PSym) =
|
||||||
n.sons[bodyPos] = transformBody(c.module, b, result)
|
n.sons[bodyPos] = transformBody(c.module, b, result)
|
||||||
#echo "code instantiated ", result.name.s
|
#echo "code instantiated ", result.name.s
|
||||||
excl(result.flags, sfForward)
|
excl(result.flags, sfForward)
|
||||||
popProcCon(c)
|
|
||||||
dec c.inGenericInst
|
dec c.inGenericInst
|
||||||
|
|
||||||
proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
|
proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
|
||||||
|
|
@ -116,9 +117,12 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) =
|
||||||
var oldPrc = c.generics[i].inst.sym
|
var oldPrc = c.generics[i].inst.sym
|
||||||
pushInfoContext(oldPrc.info)
|
pushInfoContext(oldPrc.info)
|
||||||
openScope(c)
|
openScope(c)
|
||||||
|
pushProcCon(c, oldPrc)
|
||||||
|
addProcDecls(c, oldPrc)
|
||||||
var n = oldPrc.ast
|
var n = oldPrc.ast
|
||||||
n.sons[bodyPos] = copyTree(s.getBody)
|
n.sons[bodyPos] = copyTree(s.getBody)
|
||||||
instantiateBody(c, n, oldPrc)
|
instantiateBody(c, n, oldPrc)
|
||||||
|
popProcCon(c)
|
||||||
closeScope(c)
|
closeScope(c)
|
||||||
popInfoContext()
|
popInfoContext()
|
||||||
|
|
||||||
|
|
@ -144,6 +148,47 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType,
|
||||||
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
|
proc instGenericContainer(c: PContext, n: PNode, header: PType): PType =
|
||||||
result = instGenericContainer(c, n.info, header)
|
result = instGenericContainer(c, n.info, header)
|
||||||
|
|
||||||
|
proc instantiateProcType(c: PContext, pt: TIdTable,
|
||||||
|
prc: PSym, info: TLineInfo) =
|
||||||
|
# XXX: Instantiates a generic proc signature, while at the same
|
||||||
|
# time adding the instantiated proc params into the current scope.
|
||||||
|
# This is necessary, because the instantiation process may refer to
|
||||||
|
# these params in situations like this:
|
||||||
|
# proc foo[Container](a: Container, b: a.type.Item): type(b.x)
|
||||||
|
#
|
||||||
|
# Alas, doing this here is probably not enough, because another
|
||||||
|
# proc signature could appear in the params:
|
||||||
|
# proc foo[T](a: proc (x: T, b: type(x.y))
|
||||||
|
#
|
||||||
|
# The solution would be to move this logic into semtypinst, but
|
||||||
|
# at this point semtypinst have to become part of sem, because it
|
||||||
|
# will need to use openScope, addDecl, etc
|
||||||
|
#
|
||||||
|
pushInfoContext(info)
|
||||||
|
var cl = initTypeVars(c, pt, info)
|
||||||
|
var result = instCopyType(cl, prc.typ)
|
||||||
|
addDecl(c, prc)
|
||||||
|
let originalParams = result.n
|
||||||
|
result.n = originalParams.shallowCopy
|
||||||
|
|
||||||
|
for i in 1 .. <result.len:
|
||||||
|
result.sons[i] = replaceTypeVarsT(cl, result.sons[i])
|
||||||
|
propagateToOwner(result, result.sons[i])
|
||||||
|
let param = replaceTypeVarsN(cl, originalParams[i])
|
||||||
|
internalAssert param.kind == nkSym
|
||||||
|
result.n.sons[i] = param
|
||||||
|
addDecl(c, param.sym)
|
||||||
|
|
||||||
|
result.sons[0] = replaceTypeVarsT(cl, result.sons[0])
|
||||||
|
result.n.sons[0] = originalParams[0].copyTree
|
||||||
|
|
||||||
|
eraseVoidParams(result)
|
||||||
|
skipIntLiteralParams(result)
|
||||||
|
|
||||||
|
prc.typ = result
|
||||||
|
maybeAddResult(c, prc, prc.ast)
|
||||||
|
popInfoContext()
|
||||||
|
|
||||||
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||||
info: TLineInfo): PSym =
|
info: TLineInfo): PSym =
|
||||||
# no need to instantiate generic templates/macros:
|
# no need to instantiate generic templates/macros:
|
||||||
|
|
@ -171,7 +216,8 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||||
var entry = TInstantiation.new
|
var entry = TInstantiation.new
|
||||||
entry.sym = result
|
entry.sym = result
|
||||||
instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[])
|
instantiateGenericParamList(c, n.sons[genericParamsPos], pt, entry[])
|
||||||
result.typ = generateTypeInstance(c, pt, info, fn.typ)
|
pushProcCon(c, result)
|
||||||
|
instantiateProcType(c, pt, result, info)
|
||||||
n.sons[genericParamsPos] = ast.emptyNode
|
n.sons[genericParamsPos] = ast.emptyNode
|
||||||
var oldPrc = genericCacheGet(fn, entry[])
|
var oldPrc = genericCacheGet(fn, entry[])
|
||||||
if oldPrc == nil:
|
if oldPrc == nil:
|
||||||
|
|
@ -186,6 +232,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable,
|
||||||
paramsTypeCheck(c, result.typ)
|
paramsTypeCheck(c, result.typ)
|
||||||
else:
|
else:
|
||||||
result = oldPrc
|
result = oldPrc
|
||||||
|
popProcCon(c)
|
||||||
popInfoContext()
|
popInfoContext()
|
||||||
closeScope(c) # close scope for parameters
|
closeScope(c) # close scope for parameters
|
||||||
popOwner()
|
popOwner()
|
||||||
|
|
|
||||||
|
|
@ -680,6 +680,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
s.position = genericParams.len
|
s.position = genericParams.len
|
||||||
genericParams.addSon(newSymNode(s))
|
genericParams.addSon(newSymNode(s))
|
||||||
result = typeClass
|
result = typeClass
|
||||||
|
addDecl(c, s)
|
||||||
|
|
||||||
# XXX: There are codegen errors if this is turned into a nested proc
|
# XXX: There are codegen errors if this is turned into a nested proc
|
||||||
template liftingWalk(typ: PType, anonFlag = false): expr =
|
template liftingWalk(typ: PType, anonFlag = false): expr =
|
||||||
|
|
@ -802,10 +803,11 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode,
|
||||||
result = addImplicitGeneric(newTypeS(tyAnything, c))
|
result = addImplicitGeneric(newTypeS(tyAnything, c))
|
||||||
|
|
||||||
of tyGenericParam:
|
of tyGenericParam:
|
||||||
|
markUsed(genericParams, paramType.sym)
|
||||||
if tfWildcard in paramType.flags:
|
if tfWildcard in paramType.flags:
|
||||||
paramType.flags.excl tfWildcard
|
paramType.flags.excl tfWildcard
|
||||||
paramType.sym.kind = skType
|
paramType.sym.kind = skType
|
||||||
|
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
# result = liftingWalk(paramType)
|
# result = liftingWalk(paramType)
|
||||||
|
|
@ -1108,13 +1110,17 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType =
|
||||||
for i in countup(1, n.len - 1):
|
for i in countup(1, n.len - 1):
|
||||||
result.rawAddSon(semTypeNode(c, n.sons[i], nil))
|
result.rawAddSon(semTypeNode(c, n.sons[i], nil))
|
||||||
else: result = semGeneric(c, n, s, prev)
|
else: result = semGeneric(c, n, s, prev)
|
||||||
of nkIdent, nkDotExpr, nkAccQuoted:
|
of nkDotExpr:
|
||||||
if n.kind == nkDotExpr:
|
var typeExpr = semExpr(c, n)
|
||||||
let head = qualifiedLookUp(c, n[0], {checkAmbiguity, checkUndeclared})
|
if typeExpr.typ.kind != tyTypeDesc:
|
||||||
if head.kind in {skType}:
|
localError(n.info, errTypeExpected)
|
||||||
var toBind = initIntSet()
|
return errorType(c)
|
||||||
var preprocessed = semGenericStmt(c, n, {}, toBind)
|
result = typeExpr.typ.base
|
||||||
return makeTypeFromExpr(c, preprocessed)
|
if result.isMetaType:
|
||||||
|
var toBind = initIntSet()
|
||||||
|
var preprocessed = semGenericStmt(c, n, {}, toBind)
|
||||||
|
return makeTypeFromExpr(c, preprocessed)
|
||||||
|
of nkIdent, nkAccQuoted:
|
||||||
var s = semTypeIdent(c, n)
|
var s = semTypeIdent(c, n)
|
||||||
if s.typ == nil:
|
if s.typ == nil:
|
||||||
if s.kind != skError: localError(n.info, errTypeExpected)
|
if s.kind != skError: localError(n.info, errTypeExpected)
|
||||||
|
|
|
||||||
|
|
@ -80,7 +80,7 @@ type
|
||||||
|
|
||||||
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
|
proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType
|
||||||
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
|
proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym
|
||||||
proc replaceTypeVarsN(cl: var TReplTypeVars, n: PNode): PNode
|
proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode): PNode
|
||||||
|
|
||||||
template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
|
template checkMetaInvariants(cl: TReplTypeVars, t: PType) =
|
||||||
when false:
|
when false:
|
||||||
|
|
@ -210,7 +210,7 @@ proc lookupTypeVar(cl: TReplTypeVars, t: PType): PType =
|
||||||
elif result.kind == tyGenericParam and not cl.allowMetaTypes:
|
elif result.kind == tyGenericParam and not cl.allowMetaTypes:
|
||||||
internalError(cl.info, "substitution with generic parameter")
|
internalError(cl.info, "substitution with generic parameter")
|
||||||
|
|
||||||
proc instCopyType(cl: var TReplTypeVars, t: PType): PType =
|
proc instCopyType*(cl: var TReplTypeVars, t: PType): PType =
|
||||||
# XXX: relying on allowMetaTypes is a kludge
|
# XXX: relying on allowMetaTypes is a kludge
|
||||||
result = copyType(t, t.owner, cl.allowMetaTypes)
|
result = copyType(t, t.owner, cl.allowMetaTypes)
|
||||||
result.flags.incl tfFromGeneric
|
result.flags.incl tfFromGeneric
|
||||||
|
|
@ -281,7 +281,7 @@ proc handleGenericInvokation(cl: var TReplTypeVars, t: PType): PType =
|
||||||
rawAddSon(result, newbody)
|
rawAddSon(result, newbody)
|
||||||
checkPartialConstructedType(cl.info, newbody)
|
checkPartialConstructedType(cl.info, newbody)
|
||||||
|
|
||||||
proc eraseVoidParams(t: PType) =
|
proc eraseVoidParams*(t: PType) =
|
||||||
if t.sons[0] != nil and t.sons[0].kind == tyEmpty:
|
if t.sons[0] != nil and t.sons[0].kind == tyEmpty:
|
||||||
t.sons[0] = nil
|
t.sons[0] = nil
|
||||||
|
|
||||||
|
|
@ -298,7 +298,7 @@ proc eraseVoidParams(t: PType) =
|
||||||
setLen t.n.sons, pos
|
setLen t.n.sons, pos
|
||||||
return
|
return
|
||||||
|
|
||||||
proc skipIntLiteralParams(t: PType) =
|
proc skipIntLiteralParams*(t: PType) =
|
||||||
for i in 0 .. <t.sonsLen:
|
for i in 0 .. <t.sonsLen:
|
||||||
let p = t.sons[i]
|
let p = t.sons[i]
|
||||||
if p == nil: continue
|
if p == nil: continue
|
||||||
|
|
@ -409,7 +409,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType =
|
||||||
|
|
||||||
else: discard
|
else: discard
|
||||||
|
|
||||||
proc initTypeVars(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars =
|
proc initTypeVars*(p: PContext, pt: TIdTable, info: TLineInfo): TReplTypeVars =
|
||||||
initIdTable(result.symMap)
|
initIdTable(result.symMap)
|
||||||
copyIdTable(result.typeMap, pt)
|
copyIdTable(result.typeMap, pt)
|
||||||
initIdTable(result.localCache)
|
initIdTable(result.localCache)
|
||||||
|
|
|
||||||
|
|
@ -944,8 +944,19 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
|
||||||
|
|
||||||
of tyProxy:
|
of tyProxy:
|
||||||
result = isEqual
|
result = isEqual
|
||||||
|
|
||||||
|
of tyFromExpr:
|
||||||
|
# fix the expression, so it contains the already instantiated types
|
||||||
|
let instantiated = replaceTypesInBody(c.c, c.bindings, f.n)
|
||||||
|
let reevaluted = c.c.semExpr(c.c, instantiated)
|
||||||
|
if reevaluted.typ.kind != tyTypeDesc:
|
||||||
|
localError(f.n.info, errTypeExpected)
|
||||||
|
result = isNone
|
||||||
|
else:
|
||||||
|
result = typeRel(c, a, reevaluted.typ.base)
|
||||||
|
|
||||||
else: internalAssert false
|
else:
|
||||||
|
internalAssert false
|
||||||
|
|
||||||
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
|
proc cmpTypes*(c: PContext, f, a: PType): TTypeRelation =
|
||||||
var m: TCandidate
|
var m: TCandidate
|
||||||
|
|
|
||||||
9
tests/generics/tsigtypeop.nim
Normal file
9
tests/generics/tsigtypeop.nim
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
type
|
||||||
|
Vec3[T] = array[3, T]
|
||||||
|
|
||||||
|
proc foo(x: Vec3, y: Vec3.T, z: x.T): x.type.T =
|
||||||
|
return 10
|
||||||
|
|
||||||
|
var y: Vec3[int] = [1, 2, 3]
|
||||||
|
var z: int = foo(y, 3, 4)
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue