diff options
Diffstat (limited to 'compiler/semtempl.nim')
-rw-r--r-- | compiler/semtempl.nim | 805 |
1 files changed, 478 insertions, 327 deletions
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 75c6bc4bb..817cb6249 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -34,9 +34,10 @@ type spNone, spGenSym, spInject proc symBinding(n: PNode): TSymBinding = - for i in countup(0, sonsLen(n) - 1): - var it = n.sons[i] - var key = if it.kind == nkExprColonExpr: it.sons[0] else: it + result = spNone + for i in 0..<n.len: + var it = n[i] + var key = if it.kind == nkExprColonExpr: it[0] else: it if key.kind == nkIdent: case whichKeyword(key.ident) of wGensym: return spGenSym @@ -47,10 +48,11 @@ type TSymChoiceRule = enum scClosed, scOpen, scForceOpen -proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = +proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule; + isField = false): PNode = var a: PSym - o: TOverloadIter + o: TOverloadIter = default(TOverloadIter) var i = 0 a = initOverloadIter(o, c, n) while a != nil: @@ -58,28 +60,39 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule): PNode = inc(i) if i > 1: break a = nextOverloadIter(o, c, n) + let info = getCallLineInfo(n) if i <= 1 and r != scForceOpen: # XXX this makes more sense but breaks bootstrapping for now: # (s.kind notin routineKinds or s.magic != mNone): # for instance 'nextTry' is both in tables.nim and astalgo.nim ... - result = newSymNode(s, n.info) - markUsed(c.config, n.info, s, c.graph.usageSym) + if not isField or sfGenSym notin s.flags: + result = newSymNode(s, info) + markUsed(c, info, s) + onUse(info, s) + else: + result = n + elif i == 0: + # forced open but symbol not in scope, retain information + result = n else: # semantic checking requires a type; ``fitNode`` deals with it # appropriately let kind = if r == scClosed or n.kind == nkDotExpr: nkClosedSymChoice else: nkOpenSymChoice - result = newNodeIT(kind, n.info, newTypeS(tyNone, c)) + result = newNodeIT(kind, info, newTypeS(tyNone, c)) a = initOverloadIter(o, c, n) while a != nil: - if a.kind != skModule: + if a.kind != skModule and (not isField or sfGenSym notin a.flags): incl(a.flags, sfUsed) - addSon(result, newSymNode(a, n.info)) + markOwnerModuleAsUsed(c, a) + result.add newSymNode(a, info) + onUse(info, a) a = nextOverloadIter(o, c, n) proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode = - for i in 0 ..< n.len: - var a = n.sons[i] + result = copyNode(n) + for i in 0..<n.len: + var a = n[i] # If 'a' is an overloaded symbol, we used to use the first symbol # as a 'witness' and use the fact that subsequent lookups will yield # the same symbol! @@ -91,21 +104,25 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode = let sc = symChoice(c, n, s, scClosed) if sc.kind == nkSym: toBind.incl(sc.sym.id) + result.add sc else: - for x in items(sc): toBind.incl(x.sym.id) + for x in items(sc): + toBind.incl(x.sym.id) + result.add x else: illFormedAst(a, c.config) - result = newNodeI(nkEmpty, n.info) proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode = - for i in 0 ..< n.len: - toMixin.incl(considerQuotedIdent(c, n.sons[i]).id) - result = newNodeI(nkEmpty, n.info) + result = copyNode(n) + for i in 0..<n.len: + toMixin.incl(considerQuotedIdent(c, n[i]).id) + let x = symChoice(c, n[i], nil, scForceOpen) + result.add x proc replaceIdentBySym(c: PContext; n: var PNode, s: PNode) = case n.kind - of nkPostfix: replaceIdentBySym(c, n.sons[1], s) - of nkPragmaExpr: replaceIdentBySym(c, n.sons[0], s) + of nkPostfix: replaceIdentBySym(c, n[1], s) + of nkPragmaExpr: replaceIdentBySym(c, n[0], s) of nkIdent, nkAccQuoted, nkSym: n = s else: illFormedAst(n, c.config) @@ -116,28 +133,35 @@ type owner: PSym cursorInBody: bool # only for nimsuggest scopeN: int + noGenSym: int + inTemplateHeader: int -template withBracketExpr(ctx, x, body: untyped) = - body +proc isTemplParam(c: TemplCtx, s: PSym): bool {.inline.} = + result = s.kind == skParam and + s.owner == c.owner and sfTemplateParam in s.flags -proc getIdentNode(c: var TemplCtx, n: PNode): PNode = +proc getIdentReplaceParams(c: var TemplCtx, n: var PNode): tuple[node: PNode, hasParam: bool] = case n.kind - of nkPostfix: result = getIdentNode(c, n.sons[1]) - of nkPragmaExpr: result = getIdentNode(c, n.sons[0]) + of nkPostfix: result = getIdentReplaceParams(c, n[1]) + of nkPragmaExpr: result = getIdentReplaceParams(c, n[0]) of nkIdent: - result = n + result = (n, false) let s = qualifiedLookUp(c.c, n, {}) - if s != nil: - if s.owner == c.owner and s.kind == skParam: - result = newSymNode(s, n.info) - of nkAccQuoted, nkSym: result = n + if s != nil and isTemplParam(c, s): + n = newSymNode(s, n.info) + result = (n, true) + of nkSym: + result = (n, isTemplParam(c, n.sym)) + of nkAccQuoted: + result = (n, false) + for i in 0..<n.safeLen: + let (ident, hasParam) = getIdentReplaceParams(c, n[i]) + if hasParam: + result.node[i] = ident + result.hasParam = true else: illFormedAst(n, c.c.config) - result = n - -proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} = - result = n.kind == nkSym and n.sym.kind == skParam and - n.sym.owner == c.owner and sfGenSym notin n.sym.flags + result = (n, false) proc semTemplBody(c: var TemplCtx, n: PNode): PNode @@ -152,156 +176,201 @@ proc semTemplBodyScope(c: var TemplCtx, n: PNode): PNode = result = semTemplBody(c, n) closeScope(c) -proc onlyReplaceParams(c: var TemplCtx, n: PNode): PNode = - result = n - if n.kind == nkIdent: - let s = qualifiedLookUp(c.c, n, {}) - if s != nil: - if s.owner == c.owner and s.kind == skParam: - incl(s.flags, sfUsed) - result = newSymNode(s, n.info) - styleCheckUse(n.info, s) - else: - for i in 0 ..< n.safeLen: - result.sons[i] = onlyReplaceParams(c, n.sons[i]) - proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym = - result = newSym(kind, considerQuotedIdent(c.c, n), c.owner, n.info) + result = newSym(kind, considerQuotedIdent(c.c, n), c.c.idgen, c.owner, n.info) incl(result.flags, sfGenSym) incl(result.flags, sfShadowed) proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = - # locals default to 'gensym': - if n.kind == nkPragmaExpr and symBinding(n.sons[1]) == spInject: + # locals default to 'gensym', fields default to 'inject': + if (n.kind == nkPragmaExpr and symBinding(n[1]) == spInject) or + k == skField: # even if injected, don't produce a sym choice here: #n = semTemplBody(c, n) - var x = n[0] - while true: - case x.kind - of nkPostfix: x = x[1] - of nkPragmaExpr: x = x[0] - of nkIdent: break - of nkAccQuoted: - # consider: type `T TemplParam` {.inject.} - # it suffices to return to treat it like 'inject': - n = onlyReplaceParams(c, n) - return - else: - illFormedAst(x, c.c.config) - let ident = getIdentNode(c, x) - if not isTemplParam(c, ident): - c.toInject.incl(x.ident.id) - else: - replaceIdentBySym(c.c, n, ident) + let (ident, hasParam) = getIdentReplaceParams(c, n) + if not hasParam: + if k != skField: + c.toInject.incl(considerQuotedIdent(c.c, ident).id) else: - let ident = getIdentNode(c, n) - if not isTemplParam(c, ident): - # fix #2670, consider: - # - # when b: - # var a = "hi" - # else: - # var a = 5 - # echo a - # - # We need to ensure that both 'a' produce the same gensym'ed symbol. - # So we need only check the *current* scope. - let s = localSearchInScope(c.c, considerQuotedIdent(c.c, ident)) - if s != nil and s.owner == c.owner and sfGenSym in s.flags: - styleCheckUse(n.info, s) - replaceIdentBySym(c.c, n, newSymNode(s, n.info)) - else: + if (n.kind == nkPragmaExpr and n.len >= 2 and n[1].kind == nkPragma): + let pragmaNode = n[1] + for i in 0..<pragmaNode.len: + let ni = pragmaNode[i] + # see D20210801T100514 + var found = false + if ni.kind == nkIdent: + for a in templatePragmas: + if ni.ident.id == ord(a): + found = true + break + if not found: + openScope(c) + pragmaNode[i] = semTemplBody(c, pragmaNode[i]) + closeScope(c) + let (ident, hasParam) = getIdentReplaceParams(c, n) + if not hasParam: + if n.kind != nkSym and not (n.kind == nkIdent and n.ident.id == ord(wUnderscore)): let local = newGenSym(k, ident, c) addPrelimDecl(c.c, local) - styleCheckDef(c.c.config, n.info, local) + styleCheckDef(c.c, n.info, local) + onDef(n.info, local) replaceIdentBySym(c.c, n, newSymNode(local, n.info)) - else: - replaceIdentBySym(c.c, n, ident) + if k == skParam and c.inTemplateHeader > 0: + local.flags.incl sfTemplateParam -proc semTemplSymbol(c: PContext, n: PNode, s: PSym): PNode = +proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bool): PNode = incl(s.flags, sfUsed) - # we do not call styleCheckUse here, as the identifier is not really + # bug #12885; ideally sem'checking is performed again afterwards marking + # the symbol as used properly, but the nfSem mechanism currently prevents + # that from happening, so we mark the module as used here already: + markOwnerModuleAsUsed(c.c, s) + # we do not call onUse here, as the identifier is not really # resolved here. We will fixup the used identifiers later. case s.kind of skUnknown: # Introduced in this pass! Leave it as an identifier. result = n of OverloadableSyms: - result = symChoice(c, n, s, scOpen) + result = symChoice(c.c, n, s, scOpen, isField) + if not isField and result.kind in {nkSym, nkOpenSymChoice}: + if openSym in c.c.features: + if result.kind == nkSym: + result = newOpenSym(result) + else: + result.typ = nil + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil of skGenericParam: - result = newSymNodeTypeDesc(s, n.info) + if isField and sfGenSym in s.flags: result = n + else: + result = newSymNodeTypeDesc(s, c.c.idgen, n.info) + if not isField and s.owner != c.owner: + if openSym in c.c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil of skParam: result = n of skType: - result = newSymNodeTypeDesc(s, n.info) + if isField and sfGenSym in s.flags: result = n + else: + if isAmbiguous: + # ambiguous types should be symchoices since lookup behaves + # differently for them in regular expressions + result = symChoice(c.c, n, s, scOpen, isField) + else: result = newSymNodeTypeDesc(s, c.c.idgen, n.info) + if not isField and not (s.owner == c.owner and + s.typ != nil and s.typ.kind == tyGenericParam) and + result.kind in {nkSym, nkOpenSymChoice}: + if openSym in c.c.features: + if result.kind == nkSym: + result = newOpenSym(result) + else: + result.typ = nil + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil else: - result = newSymNode(s, n.info) + if isField and sfGenSym in s.flags: result = n + else: + result = newSymNode(s, n.info) + if not isField: + if openSym in c.c.features: + result = newOpenSym(result) + else: + result.flags.incl nfDisabledOpenSym + result.typ = nil + # Issue #12832 + when defined(nimsuggest): + suggestSym(c.c.graph, n.info, s, c.c.graph.usageSym, false) + # field access (dot expr) will be handled by builtinFieldAccess + if not isField: + styleCheckUse(c.c, n.info, s) -proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode = +proc semRoutineInTemplName(c: var TemplCtx, n: PNode, explicitInject: bool): PNode = result = n if n.kind == nkIdent: let s = qualifiedLookUp(c.c, n, {}) if s != nil: - if s.owner == c.owner and (s.kind == skParam or sfGenSym in s.flags): + if s.owner == c.owner and (s.kind == skParam or + (sfGenSym in s.flags and not explicitInject)): incl(s.flags, sfUsed) result = newSymNode(s, n.info) - styleCheckUse(n.info, s) + onUse(n.info, s) else: - for i in countup(0, safeLen(n) - 1): - result.sons[i] = semRoutineInTemplName(c, n.sons[i]) + for i in 0..<n.safeLen: + result[i] = semRoutineInTemplName(c, n[i], explicitInject) proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode = result = n checkSonsLen(n, bodyPos + 1, c.c.config) - # routines default to 'inject': - if n.kind notin nkLambdaKinds and symBinding(n.sons[pragmasPos]) == spGenSym: - let ident = getIdentNode(c, n.sons[namePos]) - if not isTemplParam(c, ident): - var s = newGenSym(k, ident, c) - s.ast = n - addPrelimDecl(c.c, s) - styleCheckDef(c.c.config, n.info, s) - n.sons[namePos] = newSymNode(s, n.sons[namePos].info) + if n.kind notin nkLambdaKinds: + # routines default to 'inject': + let binding = symBinding(n[pragmasPos]) + if binding == spGenSym: + let (ident, hasParam) = getIdentReplaceParams(c, n[namePos]) + if not hasParam: + var s = newGenSym(k, ident, c) + s.ast = n + addPrelimDecl(c.c, s) + styleCheckDef(c.c, n.info, s) + onDef(n.info, s) + n[namePos] = newSymNode(s, n[namePos].info) + else: + n[namePos] = ident else: - n.sons[namePos] = ident - else: - n.sons[namePos] = semRoutineInTemplName(c, n.sons[namePos]) + n[namePos] = semRoutineInTemplName(c, n[namePos], binding == spInject) # open scope for parameters openScope(c) - for i in patternPos..miscPos: - n.sons[i] = semTemplBody(c, n.sons[i]) + for i in patternPos..paramsPos-1: + n[i] = semTemplBody(c, n[i]) + + if k == skTemplate: inc(c.inTemplateHeader) + n[paramsPos] = semTemplBody(c, n[paramsPos]) + if k == skTemplate: dec(c.inTemplateHeader) + + for i in paramsPos+1..miscPos: + n[i] = semTemplBody(c, n[i]) # open scope for locals inc c.scopeN openScope(c) - n.sons[bodyPos] = semTemplBody(c, n.sons[bodyPos]) + n[bodyPos] = semTemplBody(c, n[bodyPos]) # close scope for locals closeScope(c) dec c.scopeN # close scope for parameters closeScope(c) -proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start=0) = - for i in countup(start, sonsLen(n) - 1): - var a = n.sons[i] - if a.kind == nkCommentStmt: continue - if (a.kind != nkIdentDefs) and (a.kind != nkVarTuple): illFormedAst(a, c.c.config) - checkMinSonsLen(a, 3, c.c.config) - var L = sonsLen(a) - when defined(nimsuggest): - inc c.c.inTypeContext - a.sons[L-2] = semTemplBody(c, a.sons[L-2]) - when defined(nimsuggest): - dec c.c.inTypeContext - a.sons[L-1] = semTemplBody(c, a.sons[L-1]) - for j in countup(0, L-3): - addLocalDecl(c, a.sons[j], symKind) +proc semTemplIdentDef(c: var TemplCtx, a: PNode, symKind: TSymKind) = + checkMinSonsLen(a, 3, c.c.config) + when defined(nimsuggest): + inc c.c.inTypeContext + a[^2] = semTemplBody(c, a[^2]) + when defined(nimsuggest): + dec c.c.inTypeContext + a[^1] = semTemplBody(c, a[^1]) + for j in 0..<a.len-2: + addLocalDecl(c, a[j], symKind) + +proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start = 0) = + for i in start..<n.len: + var a = n[i] + case a.kind: + of nkCommentStmt: continue + of nkIdentDefs, nkVarTuple, nkConstDef: + semTemplIdentDef(c, a, symKind) + else: + illFormedAst(a, c.c.config) + -proc semPattern(c: PContext, n: PNode): PNode +proc semPattern(c: PContext, n: PNode; s: PSym): PNode proc semTemplBodySons(c: var TemplCtx, n: PNode): PNode = result = n - for i in 0 ..< n.len: - result.sons[i] = semTemplBody(c, n.sons[i]) + for i in 0..<n.len: + result[i] = semTemplBody(c, n[i]) proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result = n @@ -309,26 +378,29 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = case n.kind of nkIdent: if n.ident.id in c.toInject: return n + c.c.isAmbiguous = false let s = qualifiedLookUp(c.c, n, {}) if s != nil: - if s.owner == c.owner and s.kind == skParam: + if s.owner == c.owner and s.kind == skParam and sfTemplateParam in s.flags: incl(s.flags, sfUsed) result = newSymNode(s, n.info) - styleCheckUse(n.info, s) + onUse(n.info, s) elif contains(c.toBind, s.id): - result = symChoice(c.c, n, s, scClosed) + result = symChoice(c.c, n, s, scClosed, c.noGenSym > 0) elif contains(c.toMixin, s.name.id): - result = symChoice(c.c, n, s, scForceOpen) - elif s.owner == c.owner and sfGenSym in s.flags: + result = symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0) + elif s.owner == c.owner and sfGenSym in s.flags and c.noGenSym == 0: # template tmp[T](x: var seq[T]) = # var yz: T incl(s.flags, sfUsed) result = newSymNode(s, n.info) - styleCheckUse(n.info, s) + onUse(n.info, s) else: - result = semTemplSymbol(c.c, n, s) + if s.kind in {skVar, skLet, skConst}: + discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule}) + result = semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous) of nkBind: - result = semTemplBody(c, n.sons[0]) + result = semTemplBody(c, n[0]) of nkBindStmt: result = semBindStmt(c.c, n, c.toBind) of nkMixinStmt: @@ -337,105 +409,117 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = of nkEmpty, nkSym..nkNilLit, nkComesFrom: discard of nkIfStmt: - for i in countup(0, sonsLen(n)-1): - var it = n.sons[i] + for i in 0..<n.len: + var it = n[i] if it.len == 2: - when newScopeForIf: openScope(c) - it.sons[0] = semTemplBody(c, it.sons[0]) - when not newScopeForIf: openScope(c) - it.sons[1] = semTemplBody(c, it.sons[1]) + openScope(c) + it[0] = semTemplBody(c, it[0]) + it[1] = semTemplBody(c, it[1]) closeScope(c) else: - n.sons[i] = semTemplBodyScope(c, it) + n[i] = semTemplBodyScope(c, it) of nkWhileStmt: openScope(c) - for i in countup(0, sonsLen(n)-1): - n.sons[i] = semTemplBody(c, n.sons[i]) + for i in 0..<n.len: + n[i] = semTemplBody(c, n[i]) closeScope(c) of nkCaseStmt: openScope(c) - n.sons[0] = semTemplBody(c, n.sons[0]) - for i in countup(1, sonsLen(n)-1): - var a = n.sons[i] + n[0] = semTemplBody(c, n[0]) + for i in 1..<n.len: + var a = n[i] checkMinSonsLen(a, 1, c.c.config) - var L = sonsLen(a) - for j in countup(0, L-2): - a.sons[j] = semTemplBody(c, a.sons[j]) - a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1]) + for j in 0..<a.len-1: + a[j] = semTemplBody(c, a[j]) + a[^1] = semTemplBodyScope(c, a[^1]) closeScope(c) of nkForStmt, nkParForStmt: - var L = sonsLen(n) openScope(c) - n.sons[L-2] = semTemplBody(c, n.sons[L-2]) - for i in countup(0, L - 3): - addLocalDecl(c, n.sons[i], skForVar) + n[^2] = semTemplBody(c, n[^2]) + for i in 0..<n.len - 2: + if n[i].kind == nkVarTuple: + for j in 0..<n[i].len-1: + addLocalDecl(c, n[i][j], skForVar) + else: + addLocalDecl(c, n[i], skForVar) openScope(c) - n.sons[L-1] = semTemplBody(c, n.sons[L-1]) + n[^1] = semTemplBody(c, n[^1]) closeScope(c) closeScope(c) of nkBlockStmt, nkBlockExpr, nkBlockType: checkSonsLen(n, 2, c.c.config) openScope(c) - if n.sons[0].kind != nkEmpty: - addLocalDecl(c, n.sons[0], skLabel) + if n[0].kind != nkEmpty: + addLocalDecl(c, n[0], skLabel) when false: # labels are always 'gensym'ed: - let s = newGenSym(skLabel, n.sons[0], c) + let s = newGenSym(skLabel, n[0], c) addPrelimDecl(c.c, s) - styleCheckDef(c.c.config, s) - n.sons[0] = newSymNode(s, n.sons[0].info) - n.sons[1] = semTemplBody(c, n.sons[1]) + styleCheckDef(c.c, s) + onDef(n[0].info, s) + n[0] = newSymNode(s, n[0].info) + n[1] = semTemplBody(c, n[1]) closeScope(c) - of nkTryStmt: + of nkTryStmt, nkHiddenTryStmt: checkMinSonsLen(n, 2, c.c.config) - n.sons[0] = semTemplBodyScope(c, n.sons[0]) - for i in countup(1, sonsLen(n)-1): - var a = n.sons[i] + n[0] = semTemplBodyScope(c, n[0]) + for i in 1..<n.len: + var a = n[i] checkMinSonsLen(a, 1, c.c.config) - var L = sonsLen(a) openScope(c) - for j in countup(0, L-2): - if a.sons[j].isInfixAs(): - addLocalDecl(c, a.sons[j].sons[2], skLet) - a.sons[j].sons[1] = semTemplBody(c, a.sons[j][1]) + for j in 0..<a.len-1: + if a[j].isInfixAs(): + addLocalDecl(c, a[j][2], skLet) + a[j][1] = semTemplBody(c, a[j][1]) else: - a.sons[j] = semTemplBody(c, a.sons[j]) - a.sons[L-1] = semTemplBodyScope(c, a.sons[L-1]) + a[j] = semTemplBody(c, a[j]) + a[^1] = semTemplBodyScope(c, a[^1]) closeScope(c) of nkVarSection: semTemplSomeDecl(c, n, skVar) of nkLetSection: semTemplSomeDecl(c, n, skLet) of nkFormalParams: checkMinSonsLen(n, 1, c.c.config) - n.sons[0] = semTemplBody(c, n.sons[0]) semTemplSomeDecl(c, n, skParam, 1) - of nkConstSection: - for i in countup(0, sonsLen(n) - 1): - var a = n.sons[i] - if a.kind == nkCommentStmt: continue - if (a.kind != nkConstDef): illFormedAst(a, c.c.config) - checkSonsLen(a, 3, c.c.config) - addLocalDecl(c, a.sons[0], skConst) - a.sons[1] = semTemplBody(c, a.sons[1]) - a.sons[2] = semTemplBody(c, a.sons[2]) + n[0] = semTemplBody(c, n[0]) + of nkConstSection: semTemplSomeDecl(c, n, skConst) of nkTypeSection: - for i in countup(0, sonsLen(n) - 1): - var a = n.sons[i] + for i in 0..<n.len: + var a = n[i] if a.kind == nkCommentStmt: continue if (a.kind != nkTypeDef): illFormedAst(a, c.c.config) checkSonsLen(a, 3, c.c.config) - addLocalDecl(c, a.sons[0], skType) - for i in countup(0, sonsLen(n) - 1): - var a = n.sons[i] + addLocalDecl(c, a[0], skType) + for i in 0..<n.len: + var a = n[i] if a.kind == nkCommentStmt: continue if (a.kind != nkTypeDef): illFormedAst(a, c.c.config) checkSonsLen(a, 3, c.c.config) - if a.sons[1].kind != nkEmpty: + if a[1].kind != nkEmpty: openScope(c) - a.sons[1] = semTemplBody(c, a.sons[1]) - a.sons[2] = semTemplBody(c, a.sons[2]) + a[1] = semTemplBody(c, a[1]) + a[2] = semTemplBody(c, a[2]) closeScope(c) else: - a.sons[2] = semTemplBody(c, a.sons[2]) + a[2] = semTemplBody(c, a[2]) + of nkObjectTy: + openScope(c) + result = semTemplBodySons(c, n) + closeScope(c) + of nkRecList: + for i in 0..<n.len: + var a = n[i] + case a.kind: + of nkCommentStmt, nkNilLit, nkSym, nkEmpty: continue + of nkIdentDefs: + semTemplIdentDef(c, a, skField) + of nkRecCase, nkRecWhen: + n[i] = semTemplBody(c, a) + else: + illFormedAst(a, c.c.config) + of nkRecCase: + semTemplIdentDef(c, n[0], skField) + for i in 1..<n.len: + n[i] = semTemplBody(c, n[i]) of nkProcDef, nkLambdaKinds: result = semRoutineInTemplBody(c, n, skProc) of nkFuncDef: @@ -451,68 +535,109 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = of nkConverterDef: result = semRoutineInTemplBody(c, n, skConverter) of nkPragmaExpr: - result.sons[0] = semTemplBody(c, n.sons[0]) + result[0] = semTemplBody(c, n[0]) of nkPostfix: - result.sons[1] = semTemplBody(c, n.sons[1]) + result[1] = semTemplBody(c, n[1]) of nkPragma: for x in n: if x.kind == nkExprColonExpr: - x.sons[1] = semTemplBody(c, x.sons[1]) + x[1] = semTemplBody(c, x[1]) of nkBracketExpr: - result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info) - for i in 0 ..< n.len: result.add(n[i]) - let n0 = semTemplBody(c, n.sons[0]) - withBracketExpr c, n0: - result = semTemplBodySons(c, result) + if n.typ == nil: + # if a[b] is nested inside a typed expression, don't convert it + # back to `[]`(a, b), prepareOperand will not typecheck it again + # and so `[]` will not be resolved + # checking if a[b] is typed should be enough to cover this case + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent(c.c.cache, "[]"), n.info) + for i in 0..<n.len: result.add(n[i]) + result = semTemplBodySons(c, result) of nkCurlyExpr: - result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info) - for i in 0 ..< n.len: result.add(n[i]) + if n.typ == nil: + # see nkBracketExpr case for explanation + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent(c.c.cache, "{}"), n.info) + for i in 0..<n.len: result.add(n[i]) result = semTemplBodySons(c, result) - of nkAsgn, nkFastAsgn: + of nkAsgn, nkFastAsgn, nkSinkAsgn: checkSonsLen(n, 2, c.c.config) - let a = n.sons[0] - let b = n.sons[1] + let a = n[0] + let b = n[1] let k = a.kind case k of nkBracketExpr: - result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info) - for i in 0 ..< a.len: result.add(a[i]) - result.add(b) - let a0 = semTemplBody(c, a.sons[0]) - withBracketExpr c, a0: - result = semTemplBodySons(c, result) + if a.typ == nil: + # see nkBracketExpr case above for explanation + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent(c.c.cache, "[]="), n.info) + for i in 0..<a.len: result.add(a[i]) + result.add(b) + let a0 = semTemplBody(c, a[0]) + result = semTemplBodySons(c, result) of nkCurlyExpr: - result = newNodeI(nkCall, n.info) - result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info) - for i in 0 ..< a.len: result.add(a[i]) - result.add(b) + if a.typ == nil: + # see nkBracketExpr case above for explanation + result = newNodeI(nkCall, n.info) + result.add newIdentNode(getIdent(c.c.cache, "{}="), n.info) + for i in 0..<a.len: result.add(a[i]) + result.add(b) result = semTemplBodySons(c, result) else: result = semTemplBodySons(c, n) of nkCallKinds-{nkPostfix}: - result = semTemplBodySons(c, n) + # do not transform runnableExamples (bug #9143) + if not isRunnableExamples(n[0]): + result = semTemplBodySons(c, n) of nkDotExpr, nkAccQuoted: # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam', # so we use the generic code for nkDotExpr too + c.c.isAmbiguous = false let s = qualifiedLookUp(c.c, n, {}) if s != nil: + # mirror the nkIdent case # do not symchoice a quoted template parameter (bug #2390): if s.owner == c.owner and s.kind == skParam and n.kind == nkAccQuoted and n.len == 1: incl(s.flags, sfUsed) - styleCheckUse(n.info, s) + onUse(n.info, s) return newSymNode(s, n.info) elif contains(c.toBind, s.id): - return symChoice(c.c, n, s, scClosed) + return symChoice(c.c, n, s, scClosed, c.noGenSym > 0) elif contains(c.toMixin, s.name.id): - return symChoice(c.c, n, s, scForceOpen) + return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0) else: - return symChoice(c.c, n, s, scOpen) - result = semTemplBodySons(c, n) + if s.kind in {skVar, skLet, skConst}: + discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule}) + return semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous) + if n.kind == nkDotExpr: + result = n + result[0] = semTemplBody(c, n[0]) + inc c.noGenSym + result[1] = semTemplBody(c, n[1]) + dec c.noGenSym + if result[1].kind == nkSym and result[1].sym.kind in routineKinds: + # prevent `dotTransformation` from rewriting this node to `nkIdent` + # by making it a symchoice + # in generics this becomes `nkClosedSymChoice` but this breaks code + # as the old behavior here was that this became `nkIdent` + var choice = newNodeIT(nkOpenSymChoice, n[1].info, newTypeS(tyNone, c.c)) + choice.add result[1] + result[1] = choice + else: + result = semTemplBodySons(c, n) + of nkExprColonExpr, nkExprEqExpr: + if n.len == 2: + inc c.noGenSym + result[0] = semTemplBody(c, n[0]) + dec c.noGenSym + result[1] = semTemplBody(c, n[1]) + else: + result = semTemplBodySons(c, n) + of nkTableConstr: + # also transform the keys (bug #12595) + for i in 0..<n.len: + result[i] = semTemplBodySons(c, n[i]) else: result = semTemplBodySons(c, n) @@ -528,7 +653,7 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode = elif contains(c.toBind, s.id): result = symChoice(c.c, n, s, scClosed) of nkBind: - result = semTemplBodyDirty(c, n.sons[0]) + result = semTemplBodyDirty(c, n[0]) of nkBindStmt: result = semBindStmt(c.c, n, c.toBind) of nkEmpty, nkSym..nkNilLit, nkComesFrom: @@ -541,87 +666,118 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode = if s != nil and contains(c.toBind, s.id): return symChoice(c.c, n, s, scClosed) result = n - for i in countup(0, sonsLen(n) - 1): - result.sons[i] = semTemplBodyDirty(c, n.sons[i]) + for i in 0..<n.len: + result[i] = semTemplBodyDirty(c, n[i]) + +# in semstmts.nim: +proc semProcAnnotation(c: PContext, prc: PNode; validPragmas: TSpecialWords): PNode proc semTemplateDef(c: PContext, n: PNode): PNode = + result = semProcAnnotation(c, n, templatePragmas) + if result != nil: return result + result = n var s: PSym if isTopLevel(c): - s = semIdentVis(c, skTemplate, n.sons[0], {sfExported}) + s = semIdentVis(c, skTemplate, n[namePos], {sfExported}) incl(s.flags, sfGlobal) else: - s = semIdentVis(c, skTemplate, n.sons[0], {}) - styleCheckDef(c.config, s) + s = semIdentVis(c, skTemplate, n[namePos], {}) + assert s.kind == skTemplate + + styleCheckDef(c, s) + onDef(n[namePos].info, s) # check parameter list: #s.scope = c.currentScope + # push noalias flag at first to prevent unwanted recursive calls: + incl(s.flags, sfNoalias) pushOwner(c, s) openScope(c) - n.sons[namePos] = newSymNode(s, n.sons[namePos].info) - if n.sons[pragmasPos].kind != nkEmpty: - pragma(c, s, n.sons[pragmasPos], templatePragmas) - - var gp: PNode - if n.sons[genericParamsPos].kind != nkEmpty: - n.sons[genericParamsPos] = semGenericParamList(c, n.sons[genericParamsPos]) - gp = n.sons[genericParamsPos] - else: - gp = newNodeI(nkGenericParams, n.info) + n[namePos] = newSymNode(s) + s.ast = n # for implicitPragmas to use + pragmaCallable(c, s, n, templatePragmas) + implicitPragmas(c, s, n.info, templatePragmas) + + setGenericParamsMisc(c, n) # process parameters: var allUntyped = true - if n.sons[paramsPos].kind != nkEmpty: - semParamList(c, n.sons[paramsPos], gp, s) + var nullary = true + if n[paramsPos].kind != nkEmpty: + semParamList(c, n[paramsPos], n[genericParamsPos], s) # a template's parameters are not gensym'ed even if that was originally the # case as we determine whether it's a template parameter in the template # body by the absence of the sfGenSym flag: - for i in 1 .. s.typ.n.len-1: - let param = s.typ.n.sons[i].sym - param.flags.excl sfGenSym - if param.typ.kind != tyExpr: allUntyped = false - if sonsLen(gp) > 0: - if n.sons[genericParamsPos].kind == nkEmpty: - # we have a list of implicit type parameters: - n.sons[genericParamsPos] = gp - # no explicit return type? -> use tyStmt - if n.sons[paramsPos].sons[0].kind == nkEmpty: - # use ``stmt`` as implicit result type - s.typ.sons[0] = newTypeS(tyStmt, c) - s.typ.n.sons[0] = newNodeIT(nkType, n.info, s.typ.sons[0]) + let retType = s.typ.returnType + if retType != nil and retType.kind != tyUntyped: + allUntyped = false + for i in 1..<s.typ.n.len: + let param = s.typ.n[i].sym + if param.name.id != ord(wUnderscore): + param.flags.incl sfTemplateParam + param.flags.excl sfGenSym + if param.typ.kind != tyUntyped: allUntyped = false + # no default value, parameters required in call + if param.ast == nil: nullary = false else: s.typ = newTypeS(tyProc, c) - # XXX why do we need tyStmt as a return type again? + # XXX why do we need tyTyped as a return type again? s.typ.n = newNodeI(nkFormalParams, n.info) - rawAddSon(s.typ, newTypeS(tyStmt, c)) - addSon(s.typ.n, newNodeIT(nkType, n.info, s.typ.sons[0])) + rawAddSon(s.typ, newTypeS(tyTyped, c)) + s.typ.n.add newNodeIT(nkType, n.info, s.typ.returnType) + if n[genericParamsPos].safeLen == 0: + # restore original generic type params as no explicit or implicit were found + n[genericParamsPos] = n[miscPos][1] + n[miscPos] = c.graph.emptyNode if allUntyped: incl(s.flags, sfAllUntyped) - if n.sons[patternPos].kind != nkEmpty: - n.sons[patternPos] = semPattern(c, n.sons[patternPos]) - var ctx: TemplCtx - ctx.toBind = initIntSet() - ctx.toMixin = initIntSet() - ctx.toInject = initIntSet() - ctx.c = c - ctx.owner = s + if nullary and + n[genericParamsPos].kind == nkEmpty and + n[bodyPos].kind != nkEmpty: + # template can be called with alias syntax, remove pushed noalias flag + excl(s.flags, sfNoalias) + + if n[patternPos].kind != nkEmpty: + n[patternPos] = semPattern(c, n[patternPos], s) + + var ctx = TemplCtx( + toBind: initIntSet(), + toMixin: initIntSet(), + toInject: initIntSet(), + c: c, + owner: s + ) + # handle default params: + for i in 1..<s.typ.n.len: + let param = s.typ.n[i].sym + if param.ast != nil: + # param default values need to be treated like template body: + if sfDirty in s.flags: + param.ast = semTemplBodyDirty(ctx, param.ast) + else: + param.ast = semTemplBody(ctx, param.ast) + if param.ast.referencesAnotherParam(s): + param.ast.flags.incl nfDefaultRefsParam if sfDirty in s.flags: - n.sons[bodyPos] = semTemplBodyDirty(ctx, n.sons[bodyPos]) + n[bodyPos] = semTemplBodyDirty(ctx, n[bodyPos]) else: - n.sons[bodyPos] = semTemplBody(ctx, n.sons[bodyPos]) + n[bodyPos] = semTemplBody(ctx, n[bodyPos]) # only parameters are resolved, no type checking is performed - semIdeForTemplateOrGeneric(c, n.sons[bodyPos], ctx.cursorInBody) + semIdeForTemplateOrGeneric(c, n[bodyPos], ctx.cursorInBody) closeScope(c) popOwner(c) - s.ast = n - result = n + if sfCustomPragma in s.flags: - if n.sons[bodyPos].kind != nkEmpty: - localError(c.config, n.sons[bodyPos].info, errImplOfXNotAllowed % s.name.s) - elif n.sons[bodyPos].kind == nkEmpty: + if n[bodyPos].kind != nkEmpty: + localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s) + elif n[bodyPos].kind == nkEmpty: localError(c.config, n.info, "implementation of '$1' expected" % s.name.s) - var proto = searchForProc(c, c.currentScope, s) + var (proto, comesFromShadowscope) = searchForProc(c, c.currentScope, s) if proto == nil: addInterfaceOverloadableSymAt(c, c.currentScope, s) - else: + elif not comesFromShadowscope: + if {sfTemplateRedefinition, sfGenSym} * s.flags == {}: + #wrongRedefinition(c, n.info, proto.name.s, proto.info) + message(c.config, n.info, warnImplicitTemplateRedefinition, s.name.s) symTabReplace(c.currentScope.symbols, proto, s) - if n.sons[patternPos].kind != nkEmpty: + if n[patternPos].kind != nkEmpty: c.patterns.add(s) proc semPatternBody(c: var TemplCtx, n: PNode): PNode = @@ -634,8 +790,8 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = # semtypes.addParamOrResult). Within the pattern we have to ensure # to use the param with the proper type though: incl(s.flags, sfUsed) - styleCheckUse(n.info, s) - let x = c.owner.typ.n.sons[s.position+1].sym + onUse(n.info, s) + let x = c.owner.typ.n[s.position+1].sym assert x.name == s.name result = newSymNode(x, n.info) @@ -661,11 +817,6 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = localError(c.c.config, n.info, "invalid expression") result = n - proc stupidStmtListExpr(n: PNode): bool = - for i in 0 .. n.len-2: - if n[i].kind notin {nkEmpty, nkCommentStmt}: return false - result = true - result = n case n.kind of nkIdent: @@ -679,14 +830,14 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = # '(pattern){|x}' does the same but the matches will be gathered in 'x' if n.len != 2: localError(c.c.config, n.info, "invalid expression") - elif n.sons[1].kind == nkIdent: - n.sons[0] = semPatternBody(c, n.sons[0]) - n.sons[1] = expectParam(c, n.sons[1]) - elif n.sons[1].kind == nkPrefix and n.sons[1].sons[0].kind == nkIdent: - let opr = n.sons[1].sons[0] + elif n[1].kind == nkIdent: + n[0] = semPatternBody(c, n[0]) + n[1] = expectParam(c, n[1]) + elif n[1].kind == nkPrefix and n[1][0].kind == nkIdent: + let opr = n[1][0] if opr.ident.s == "|": - n.sons[0] = semPatternBody(c, n.sons[0]) - n.sons[1].sons[1] = expectParam(c, n.sons[1].sons[1]) + n[0] = semPatternBody(c, n[0]) + n[1][1] = expectParam(c, n[1][1]) else: localError(c.c.config, n.info, "invalid expression") else: @@ -695,43 +846,41 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = if stupidStmtListExpr(n): result = semPatternBody(c, n.lastSon) else: - for i in countup(0, sonsLen(n) - 1): - result.sons[i] = semPatternBody(c, n.sons[i]) + for i in 0..<n.len: + result[i] = semPatternBody(c, n[i]) of nkCallKinds: - let s = qualifiedLookUp(c.c, n.sons[0], {}) + let s = qualifiedLookUp(c.c, n[0], {}) if s != nil: if s.owner == c.owner and s.kind == skParam: discard elif contains(c.toBind, s.id): discard elif templToExpand(s): return semPatternBody(c, semTemplateExpr(c.c, n, s, {efNoSemCheck})) - if n.kind == nkInfix and n.sons[0].kind == nkIdent: + if n.kind == nkInfix and (let id = considerQuotedIdent(c.c, n[0]); id != nil): # we interpret `*` and `|` only as pattern operators if they occur in # infix notation, so that '`*`(a, b)' can be used for verbatim matching: - let opr = n.sons[0] - if opr.ident.s == "*" or opr.ident.s == "**": + if id.s == "*" or id.s == "**": result = newNodeI(nkPattern, n.info, n.len) - result.sons[0] = opr - result.sons[1] = semPatternBody(c, n.sons[1]) - result.sons[2] = expectParam(c, n.sons[2]) + result[0] = newIdentNode(id, n.info) + result[1] = semPatternBody(c, n[1]) + result[2] = expectParam(c, n[2]) return - elif opr.ident.s == "|": + elif id.s == "|": result = newNodeI(nkPattern, n.info, n.len) - result.sons[0] = opr - result.sons[1] = semPatternBody(c, n.sons[1]) - result.sons[2] = semPatternBody(c, n.sons[2]) + result[0] = newIdentNode(id, n.info) + result[1] = semPatternBody(c, n[1]) + result[2] = semPatternBody(c, n[2]) return - if n.kind == nkPrefix and n.sons[0].kind == nkIdent: - let opr = n.sons[0] - if opr.ident.s == "~": + if n.kind == nkPrefix and (let id = considerQuotedIdent(c.c, n[0]); id != nil): + if id.s == "~": result = newNodeI(nkPattern, n.info, n.len) - result.sons[0] = opr - result.sons[1] = semPatternBody(c, n.sons[1]) + result[0] = newIdentNode(id, n.info) + result[1] = semPatternBody(c, n[1]) return - for i in countup(0, sonsLen(n) - 1): - result.sons[i] = semPatternBody(c, n.sons[i]) + for i in 0..<n.len: + result[i] = semPatternBody(c, n[i]) else: # dotExpr is ambiguous: note that we explicitly allow 'x.TemplateParam', # so we use the generic code for nkDotExpr too @@ -744,23 +893,25 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = else: return newIdentNode(s.name, n.info) of nkPar: - if n.len == 1: return semPatternBody(c, n.sons[0]) + if n.len == 1: return semPatternBody(c, n[0]) else: discard - for i in countup(0, sonsLen(n) - 1): - result.sons[i] = semPatternBody(c, n.sons[i]) + for i in 0..<n.len: + result[i] = semPatternBody(c, n[i]) -proc semPattern(c: PContext, n: PNode): PNode = +proc semPattern(c: PContext, n: PNode; s: PSym): PNode = openScope(c) - var ctx: TemplCtx - ctx.toBind = initIntSet() - ctx.toMixin = initIntSet() - ctx.toInject = initIntSet() - ctx.c = c - ctx.owner = getCurrOwner(c) + var ctx = TemplCtx( + toBind: initIntSet(), + toMixin: initIntSet(), + toInject: initIntSet(), + c: c, + owner: getCurrOwner(c) + ) result = flattenStmts(semPatternBody(ctx, n)) if result.kind in {nkStmtList, nkStmtListExpr}: if result.len == 1: - result = result.sons[0] + result = result[0] elif result.len == 0: localError(c.config, n.info, "a pattern cannot be empty") closeScope(c) + addPattern(c, LazySym(sym: s)) |