Restore the Nim's 0.14 proper handling of generic aliases

A more efficient implementation is possible by restoring the old
lifting ot tyGenericInvocation to tyGenericInst in liftTypeParam,
but this fix will suffice for now.

fixes #5087
fixes #5602
fixes #5641
fixes #5570
This commit is contained in:
Zahary Karadjov 2017-04-07 23:18:14 +03:00
commit e9a3ffbc3d
12 changed files with 236 additions and 55 deletions

View file

@ -1346,6 +1346,9 @@ proc initIdTable*(x: var TIdTable) =
x.counter = 0 x.counter = 0
newSeq(x.data, StartSize) newSeq(x.data, StartSize)
proc newIdTable*: TIdTable =
initIdTable(result)
proc resetIdTable*(x: var TIdTable) = proc resetIdTable*(x: var TIdTable) =
x.counter = 0 x.counter = 0
# clear and set to old initial size: # clear and set to old initial size:

View file

@ -351,8 +351,8 @@ when false:
for i in 0 ..< n.safeLen: for i in 0 ..< n.safeLen:
resetSemFlag(n[i]) resetSemFlag(n[i])
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym, proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
flags: TExprFlags): PNode = s: PSym, flags: TExprFlags): PNode =
## Semantically check the output of a macro. ## Semantically check the output of a macro.
## This involves processes such as re-checking the macro output for type ## This involves processes such as re-checking the macro output for type
## coherence, making sure that variables declared with 'let' aren't ## coherence, making sure that variables declared with 'let' aren't
@ -363,8 +363,8 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
globalError(s.info, errTemplateInstantiationTooNested) globalError(s.info, errTemplateInstantiationTooNested)
c.friendModules.add(s.owner.getModule) c.friendModules.add(s.owner.getModule)
result = n result = macroResult
excl(n.flags, nfSem) excl(result.flags, nfSem)
#resetSemFlag n #resetSemFlag n
if s.typ.sons[0] == nil: if s.typ.sons[0] == nil:
result = semStmt(c, result) result = semStmt(c, result)
@ -378,13 +378,26 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
of tyStmt: of tyStmt:
result = semStmt(c, result) result = semStmt(c, result)
of tyTypeDesc: of tyTypeDesc:
if n.kind == nkStmtList: result.kind = nkStmtListType if result.kind == nkStmtList: result.kind = nkStmtListType
var typ = semTypeNode(c, result, nil) var typ = semTypeNode(c, result, nil)
result.typ = makeTypeDesc(c, typ) result.typ = makeTypeDesc(c, typ)
#result = symNodeFromType(c, typ, n.info) #result = symNodeFromType(c, typ, n.info)
else: else:
var retType = s.typ.sons[0]
if s.ast[genericParamsPos] != nil and retType.isMetaType:
# The return type may depend on the Macro arguments
# e.g. template foo(T: typedesc): seq[T]
# We will instantiate the return type here, because
# we now know the supplied arguments
var paramTypes = newIdTable()
for param, value in genericParamsInMacroCall(s, call):
idTablePut(paramTypes, param.typ, value.typ)
retType = generateTypeInstance(c, paramTypes,
macroResult.info, retType)
result = semExpr(c, result, flags) result = semExpr(c, result, flags)
result = fitNode(c, s.typ.sons[0], result, result.info) result = fitNode(c, retType, result, result.info)
#GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0])) #GlobalError(s.info, errInvalidParamKindX, typeToString(s.typ.sons[0]))
dec(evalTemplateCounter) dec(evalTemplateCounter)
discard c.friendModules.pop() discard c.friendModules.pop()
@ -409,7 +422,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
# c.evalContext = c.createEvalContext(emStatic) # c.evalContext = c.createEvalContext(emStatic)
result = evalMacroCall(c.module, c.cache, n, nOrig, sym) result = evalMacroCall(c.module, c.cache, n, nOrig, sym)
if efNoSemCheck notin flags: if efNoSemCheck notin flags:
result = semAfterMacroCall(c, result, sym, flags) result = semAfterMacroCall(c, n, result, sym, flags)
result = wrapInComesFrom(nOrig.info, result) result = wrapInComesFrom(nOrig.info, result)
popInfoContext() popInfoContext()

View file

@ -16,7 +16,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
styleCheckUse(n.info, s) styleCheckUse(n.info, s)
pushInfoContext(n.info) pushInfoContext(n.info)
result = evalTemplate(n, s, getCurrOwner(c), efFromHlo in flags) result = evalTemplate(n, s, getCurrOwner(c), efFromHlo in flags)
if efNoSemCheck notin flags: result = semAfterMacroCall(c, result, s, flags) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags)
popInfoContext() popInfoContext()
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode

View file

@ -36,7 +36,8 @@ proc rawPushProcCon(c: PContext, owner: PSym) =
c.p = x c.p = x
proc rawHandleSelf(c: PContext; owner: PSym) = proc rawHandleSelf(c: PContext; owner: PSym) =
if c.selfName != nil and owner.kind in {skProc, skMethod, skConverter, skIterator, skMacro} and owner.typ != nil: const callableSymbols = {skProc, skMethod, skConverter, skIterator, skMacro}
if c.selfName != nil and owner.kind in callableSymbols and owner.typ != nil:
let params = owner.typ.n let params = owner.typ.n
if params.len > 1: if params.len > 1:
let arg = params[1].sym let arg = params[1].sym

View file

@ -1177,7 +1177,8 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
var objB = t.sons[2] var objB = t.sons[2]
while true: while true:
if objB.kind == tyGenericBody: objB = objB.lastSon if objB.kind == tyGenericBody: objB = objB.lastSon
elif objB.kind == tyGenericInvocation: objB = objB.sons[0] elif objB.kind in {tyGenericInvocation, tyGenericInst}:
objB = objB.sons[0]
else: break else: break
if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB): if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB):
if obj.assignment.isNil: if obj.assignment.isNil:

View file

@ -307,6 +307,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
rawAddSon(result, newbody) rawAddSon(result, newbody)
checkPartialConstructedType(cl.info, newbody) checkPartialConstructedType(cl.info, newbody)
let dc = newbody.deepCopy let dc = newbody.deepCopy
if cl.allowMetaTypes == false:
if dc != nil and sfFromGeneric notin newbody.deepCopy.flags: if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
# 'deepCopy' needs to be instantiated for # 'deepCopy' needs to be instantiated for
# generics *when the type is constructed*: # generics *when the type is constructed*:
@ -526,6 +527,14 @@ proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
result = replaceTypeVarsT(cl, t) result = replaceTypeVarsT(cl, t)
popInfoContext() popInfoContext()
proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo,
t: PType): PType =
var cl = initTypeVars(p, pt, info, nil)
cl.allowMetaTypes = true
pushInfoContext(info)
result = replaceTypeVarsT(cl, t)
popInfoContext()
template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode, template generateTypeInstance*(p: PContext, pt: TIdTable, arg: PNode,
t: PType): untyped = t: PType): untyped =
generateTypeInstance(p, pt, arg.info, t) generateTypeInstance(p, pt, arg.info, t)

View file

@ -1204,9 +1204,49 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyEmpty, tyVoid: of tyEmpty, tyVoid:
if a.kind == f.kind: result = isEqual if a.kind == f.kind: result = isEqual
of tyGenericInst, tyAlias: of tyAlias:
result = typeRel(c, lastSon(f), a) result = typeRel(c, lastSon(f), a)
of tyGenericInst:
var prev = PType(idTableGet(c.bindings, f))
var f = if prev == nil: f else: prev
let roota = a.skipGenericAlias
let rootf = f.skipGenericAlias
var m = c
if a.kind == tyGenericInst:
if roota.base == rootf.base:
for i in 1 .. rootf.sonsLen-2:
let ff = rootf.sons[i]
let aa = roota.sons[i]
result = typeRel(c, ff, aa)
if result notin {isEqual, isGeneric}: return isNone
# if ff.kind == tyRange and result != isEqual: return isNone
if prev == nil: put(c, f, a)
result = isGeneric
else:
var aAsObject = roota.lastSon
if rootf.lastSon.kind in {tyAnd, tyOr}:
result = typeRel(c, lastSon(f), a)
if result != isNone: put(c, f, a)
return
if rootf.lastSon.kind == tyRef and aAsObject.kind == tyRef:
aAsObject = aAsObject.base
if aAsObject.kind == tyObject:
let baseType = aAsObject.base
if baseType != nil:
c.inheritancePenalty += 1
return typeRel(c, f, baseType)
result = isNone
else:
result = typeRel(c, lastSon(f), a)
if result != isNone: put(c, f, a)
of tyGenericBody: of tyGenericBody:
considerPreviousT: considerPreviousT:
if a.kind == tyGenericInst and a.sons[0] == f: if a.kind == tyGenericInst and a.sons[0] == f:
@ -1217,6 +1257,12 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyGenericInvocation: of tyGenericInvocation:
var x = a.skipGenericAlias var x = a.skipGenericAlias
# XXX: This is very hacky. It should be moved back into liftTypeParam
if x.kind == tyGenericInst and c.calleeSym != nil and c.calleeSym.kind == skProc:
let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f)
return typeRel(c, inst, a)
var depth = 0 var depth = 0
if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody: if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody:
#InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation") #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation")
@ -2086,6 +2132,7 @@ proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo;
localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'") localError(info, errGenerated, "cannot instantiate '" & dc.name.s & "'")
return nil return nil
var f = dc.typ.sons[col] var f = dc.typ.sons[col]
if op == attachedDeepCopy: if op == attachedDeepCopy:
if f.kind in {tyRef, tyPtr}: f = f.lastSon if f.kind in {tyRef, tyPtr}: f = f.lastSon
else: else:

View file

@ -1606,6 +1606,12 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg =
n.typ = x.typ n.typ = x.typ
result.node = n result.node = n
iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) =
let gp = macroSym.ast[genericParamsPos]
for i in 0 .. <gp.len:
let idx = macroSym.typ.len + i
yield (gp[i].sym, call.sons[idx])
var evalMacroCounter: int var evalMacroCounter: int
proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode, proc evalMacroCall*(module: PSym; cache: IdentCache, n, nOrig: PNode,

27
tests/generics/t5570.nim Normal file
View file

@ -0,0 +1,27 @@
discard """
nimout: "type uint32\ntype uint32"
output: "(weight: 17.0, color: 100)"
"""
import macros
type
BaseFruit[T] = object of RootObj
color: T
Banana[T] = object of BaseFruit[uint32]
weight: T
macro printTypeName(typ: typed): untyped =
echo "type ", getType(typ).repr
proc setColor[K](self: var BaseFruit[K], c: int) =
printTypeName(self.color)
self.color = uint32(c)
var x: Banana[float64]
x.weight = 17
printTypeName(x.color)
x.setColor(100)
echo x

View file

@ -0,0 +1,18 @@
discard """
output: "seq[float]\n0"
"""
# https://github.com/nim-lang/Nim/issues/5602
import typetraits
type
Foo[T] = object of RootObj
Bar[T] = object of Foo[seq[T]]
proc p[T](f: Foo[T]): T =
echo T.name
var s: Bar[float]
echo p(s).len # the bug was: p(s) should return seq[float], but returns float instead

View file

@ -0,0 +1,28 @@
discard """
output: '''type(c) = GenAlias[system.int]
T = int
seq[int]
'''
"""
import typetraits
type
Gen[T] = object
x: T
GenAlias[T] = Gen[seq[T]]
proc f1[T](x: Gen[T]) =
echo T.name
proc f2[T](x: GenAlias[T]) =
echo "type(c) = ", type(x).name
echo "T = ", T.name
f1 x
let
y = Gen[seq[int]](x: @[10])
f2 y

View file

@ -0,0 +1,28 @@
discard """
errormsg: "got (ref Matrix[2, 2, system.float], ref Matrix[2, 1, system.float])"
line: 27
"""
type
Matrix[M,N: static[int]; T: SomeReal] = distinct array[0..(M*N - 1), T]
let a = new Matrix[2,2,float]
let b = new Matrix[2,1,float]
proc foo[M,N: static[int],T](a: ref Matrix[M, N, T], b: ref Matrix[M, N, T])=
discard
foo(a, a)
proc bar[M,N: static[int],T](a: ref Matrix[M, M, T], b: ref Matrix[M, N, T])=
discard
bar(a, b)
bar(a, a)
proc baz[M,N: static[int],T](a: ref Matrix[N, N, T], b: ref Matrix[M, N, T])=
discard
baz(a, a)
baz(a, b)