diff options
author | Zahary Karadjov <zahary@gmail.com> | 2017-04-07 23:18:14 +0300 |
---|---|---|
committer | Zahary Karadjov <zahary@gmail.com> | 2017-04-08 17:28:19 +0300 |
commit | e9a3ffbc3d318911da5c46582a70288dd16275f3 (patch) | |
tree | 8b9f8c8b1cc9afc25cb7d0a9b218ea7ee4f6bcdd /compiler | |
parent | fceef773015bd7f764c51549ec20768b5e9f5eb1 (diff) | |
download | Nim-e9a3ffbc3d318911da5c46582a70288dd16275f3.tar.gz |
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
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/ast.nim | 3 | ||||
-rw-r--r-- | compiler/sem.nim | 27 | ||||
-rw-r--r-- | compiler/semexprs.nim | 2 | ||||
-rw-r--r-- | compiler/seminst.nim | 3 | ||||
-rw-r--r-- | compiler/semstmts.nim | 3 | ||||
-rw-r--r-- | compiler/semtypinst.nim | 59 | ||||
-rw-r--r-- | compiler/sigmatch.nim | 87 | ||||
-rw-r--r-- | compiler/vm.nim | 6 |
8 files changed, 135 insertions, 55 deletions
diff --git a/compiler/ast.nim b/compiler/ast.nim index 49ca1c5e0..1791c742d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -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: diff --git a/compiler/sem.nim b/compiler/sem.nim index ab3c5cec8..9f80e1399 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -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() diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 1b2222b2e..fff16092c 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -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 diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 874be8dd6..b5dca4c1b 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -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 diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index a1c3bd001..13670b0d3 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -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: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 27b154d62..9408a6d3d 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -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) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 4661abda0..56e12df03 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -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: diff --git a/compiler/vm.nim b/compiler/vm.nim index 6a9545193..14800fb13 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -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, |