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:
parent
fceef77301
commit
e9a3ffbc3d
12 changed files with 236 additions and 55 deletions
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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:
|
||||||
|
|
|
||||||
|
|
@ -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
27
tests/generics/t5570.nim
Normal 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
|
||||||
|
|
||||||
18
tests/generics/t5602_inheritence.nim
Normal file
18
tests/generics/t5602_inheritence.nim
Normal 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
|
||||||
|
|
||||||
28
tests/generics/tmapping_generic_alias.nim
Normal file
28
tests/generics/tmapping_generic_alias.nim
Normal 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
|
||||||
|
|
||||||
28
tests/generics/tparam_binding.nim
Normal file
28
tests/generics/tparam_binding.nim
Normal 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)
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue