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
parent fceef77301
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
newSeq(x.data, StartSize)
proc newIdTable*: TIdTable =
initIdTable(result)
proc resetIdTable*(x: var TIdTable) =
x.counter = 0
# clear and set to old initial size:

View File

@@ -351,8 +351,8 @@ when false:
for i in 0 ..< n.safeLen:
resetSemFlag(n[i])
proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
flags: TExprFlags): PNode =
proc semAfterMacroCall(c: PContext, call, macroResult: PNode,
s: PSym, flags: TExprFlags): PNode =
## Semantically check the output of a macro.
## This involves processes such as re-checking the macro output for type
## 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)
c.friendModules.add(s.owner.getModule)
result = n
excl(n.flags, nfSem)
result = macroResult
excl(result.flags, nfSem)
#resetSemFlag n
if s.typ.sons[0] == nil:
result = semStmt(c, result)
@@ -378,13 +378,26 @@ proc semAfterMacroCall(c: PContext, n: PNode, s: PSym,
of tyStmt:
result = semStmt(c, result)
of tyTypeDesc:
if n.kind == nkStmtList: result.kind = nkStmtListType
if result.kind == nkStmtList: result.kind = nkStmtListType
var typ = semTypeNode(c, result, nil)
result.typ = makeTypeDesc(c, typ)
#result = symNodeFromType(c, typ, n.info)
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 = 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]))
dec(evalTemplateCounter)
discard c.friendModules.pop()
@@ -409,7 +422,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym,
# c.evalContext = c.createEvalContext(emStatic)
result = evalMacroCall(c.module, c.cache, n, nOrig, sym)
if efNoSemCheck notin flags:
result = semAfterMacroCall(c, result, sym, flags)
result = semAfterMacroCall(c, n, result, sym, flags)
result = wrapInComesFrom(nOrig.info, result)
popInfoContext()

View File

@@ -16,7 +16,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym,
styleCheckUse(n.info, s)
pushInfoContext(n.info)
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()
proc semFieldAccess(c: PContext, n: PNode, flags: TExprFlags = {}): PNode

View File

@@ -36,7 +36,8 @@ proc rawPushProcCon(c: PContext, owner: PSym) =
c.p = x
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
if params.len > 1:
let arg = params[1].sym

View File

@@ -1177,7 +1177,8 @@ proc semOverride(c: PContext, s: PSym, n: PNode) =
var objB = t.sons[2]
while true:
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
if obj.kind in {tyObject, tyDistinct} and sameType(obj, objB):
if obj.assignment.isNil:

View File

@@ -307,31 +307,32 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType =
rawAddSon(result, newbody)
checkPartialConstructedType(cl.info, newbody)
let dc = newbody.deepCopy
if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
# 'deepCopy' needs to be instantiated for
# generics *when the type is constructed*:
newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
attachedDeepCopy, 1)
if bodyIsNew and newbody.typeInst == nil:
#doassert newbody.typeInst == nil
newbody.typeInst = result
if tfRefsAnonObj in newbody.flags and newbody.kind != tyGenericInst:
# can come here for tyGenericInst too, see tests/metatype/ttypeor.nim
# need to look into this issue later
assert newbody.kind in {tyRef, tyPtr}
assert newbody.lastSon.typeInst == nil
newbody.lastSon.typeInst = result
let asgn = newbody.assignment
if asgn != nil and sfFromGeneric notin asgn.flags:
# '=' needs to be instantiated for generics when the type is constructed:
newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info,
attachedAsgn, 1)
let methods = skipTypes(bbody, abstractPtrs).methods
for col, meth in items(methods):
# we instantiate the known methods belonging to that type, this causes
# them to be registered and that's enough, so we 'discard' the result.
discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info,
attachedAsgn, col)
if cl.allowMetaTypes == false:
if dc != nil and sfFromGeneric notin newbody.deepCopy.flags:
# 'deepCopy' needs to be instantiated for
# generics *when the type is constructed*:
newbody.deepCopy = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info,
attachedDeepCopy, 1)
if bodyIsNew and newbody.typeInst == nil:
#doassert newbody.typeInst == nil
newbody.typeInst = result
if tfRefsAnonObj in newbody.flags and newbody.kind != tyGenericInst:
# can come here for tyGenericInst too, see tests/metatype/ttypeor.nim
# need to look into this issue later
assert newbody.kind in {tyRef, tyPtr}
assert newbody.lastSon.typeInst == nil
newbody.lastSon.typeInst = result
let asgn = newbody.assignment
if asgn != nil and sfFromGeneric notin asgn.flags:
# '=' needs to be instantiated for generics when the type is constructed:
newbody.assignment = cl.c.instTypeBoundOp(cl.c, asgn, result, cl.info,
attachedAsgn, 1)
let methods = skipTypes(bbody, abstractPtrs).methods
for col, meth in items(methods):
# we instantiate the known methods belonging to that type, this causes
# them to be registered and that's enough, so we 'discard' the result.
discard cl.c.instTypeBoundOp(cl.c, meth, result, cl.info,
attachedAsgn, col)
proc eraseVoidParams*(t: PType) =
# transform '(): void' into '()' because old parts of the compiler really
@@ -526,6 +527,14 @@ proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo,
result = replaceTypeVarsT(cl, t)
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,
t: PType): untyped =
generateTypeInstance(p, pt, arg.info, t)

View File

@@ -26,7 +26,7 @@ type
sym*: PSym
unmatchedVarParam*: int
diagnostics*: seq[string]
CandidateErrors* = seq[CandidateError]
TCandidate* = object
@@ -68,7 +68,7 @@ type
# future.
mutabilityProblem*: uint8 # tyVar mismatch
inheritancePenalty: int # to prefer closest father object type
TTypeRelation* = enum # order is important!
isNone, isConvertible,
isIntConv,
@@ -639,7 +639,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
makeTypeDesc(c, typ)
typeParams.safeAdd((param, typ))
addDecl(c, param)
for param in typeClass.n[0]:
@@ -676,7 +676,7 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
flags: TExprFlags = {}
collectDiagnostics = m.diagnostics != nil or
sfExplain in typeClass.sym.flags
if collectDiagnostics:
oldWriteHook = writelnHook
# XXX: we can't write to m.diagnostics directly, because
@@ -688,13 +688,13 @@ proc matchUserTypeClass*(c: PContext, m: var TCandidate,
let msg = s.replace("Error:", errorPrefix)
if oldWriteHook != nil: oldWriteHook msg
diagnostics.add msg
var checkedBody = c.semTryExpr(c, body.copyTree, flags)
if collectDiagnostics:
writelnHook = oldWriteHook
for msg in diagnostics: m.diagnostics.safeAdd msg
if checkedBody == nil: return nil
# The inferrable type params have been identified during the semTryExpr above.
@@ -746,7 +746,7 @@ proc inferStaticParam*(lhs: PNode, rhs: BiggestInt): PType =
#
# Preconditions:
#
# * The input of this proc must be semantized
# * The input of this proc must be semantized
# - all templates should be expanded
# - aby constant folding possible should already be performed
#
@@ -769,13 +769,13 @@ proc inferStaticParam*(lhs: PNode, rhs: BiggestInt): PType =
return inferStaticParam(lhs[2], rhs - lhs[1].intVal)
elif lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], rhs - lhs[2].intVal)
of mDec, mSubI, mSubU, mPred:
if lhs[1].kind == nkIntLit:
return inferStaticParam(lhs[2], lhs[1].intVal - rhs)
elif lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], rhs + lhs[2].intVal)
of mMulI, mMulU:
if lhs[1].kind == nkIntLit:
if rhs mod lhs[1].intVal == 0:
@@ -783,34 +783,34 @@ proc inferStaticParam*(lhs: PNode, rhs: BiggestInt): PType =
elif lhs[2].kind == nkIntLit:
if rhs mod lhs[2].intVal == 0:
return inferStaticParam(lhs[1], rhs div lhs[2].intVal)
of mDivI, mDivU:
if lhs[1].kind == nkIntLit:
if lhs[1].intVal mod rhs == 0:
return inferStaticParam(lhs[2], lhs[1].intVal div rhs)
elif lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], lhs[2].intVal * rhs)
of mShlI:
if lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], rhs shr lhs[2].intVal)
of mShrI:
if lhs[2].kind == nkIntLit:
return inferStaticParam(lhs[1], rhs shl lhs[2].intVal)
of mUnaryMinusI:
return inferStaticParam(lhs[1], -rhs)
of mUnaryPlusI, mToInt, mToBiggestInt:
return inferStaticParam(lhs[1], rhs)
else: discard
elif lhs.kind == nkSym and lhs.typ.kind == tyStatic and lhs.typ.n == nil:
lhs.typ.n = newIntNode(nkIntLit, rhs)
return lhs.typ
return nil
proc failureToInferStaticParam(n: PNode) =
@@ -825,7 +825,7 @@ proc inferStaticsInRange(c: var TCandidate,
allowUnresolved = true)
let upperBound = tryResolvingStaticExpr(c, inferred.n[1],
allowUnresolved = true)
template doInferStatic(c: var TCandidate, e: PNode, r: BiggestInt) =
var exp = e
var rhs = r
@@ -1049,7 +1049,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
result = typeRel(c, f.sons[1].skipTypes({tyTypeDesc}),
a.sons[1].skipTypes({tyTypeDesc}))
if result < isGeneric: return isNone
if fRange.rangeHasUnresolvedStatic:
return inferStaticsInRange(c, fRange, a)
elif c.c.inTypeClass > 0 and aRange.rangeHasUnresolvedStatic:
@@ -1204,9 +1204,49 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, doBind = true): TTypeRelation =
of tyEmpty, tyVoid:
if a.kind == f.kind: result = isEqual
of tyGenericInst, tyAlias:
of tyAlias:
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:
considerPreviousT:
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:
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
if x.kind == tyGenericInvocation or f.sons[0].kind != tyGenericBody:
#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 & "'")
return nil
var f = dc.typ.sons[col]
if op == attachedDeepCopy:
if f.kind in {tyRef, tyPtr}: f = f.lastSon
else:

View File

@@ -1606,6 +1606,12 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg =
n.typ = x.typ
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
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)