diff options
Diffstat (limited to 'compiler/semtempl.nim')
-rw-r--r-- | compiler/semtempl.nim | 275 |
1 files changed, 159 insertions, 116 deletions
diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index a72143bdd..817cb6249 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -52,7 +52,7 @@ 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: @@ -136,24 +136,32 @@ type noGenSym: int inTemplateHeader: int -proc getIdentNode(c: var TemplCtx, n: PNode): PNode = +proc isTemplParam(c: TemplCtx, s: PSym): bool {.inline.} = + result = s.kind == skParam and + s.owner == c.owner and sfTemplateParam in s.flags + +proc getIdentReplaceParams(c: var TemplCtx, n: var PNode): tuple[node: PNode, hasParam: bool] = case n.kind - of nkPostfix: result = getIdentNode(c, n[1]) - of nkPragmaExpr: result = getIdentNode(c, n[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 sfTemplateParam in n.sym.flags + result = (n, false) proc semTemplBody(c: var TemplCtx, n: PNode): PNode @@ -168,19 +176,6 @@ 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) - onUse(n.info, s) - else: - for i in 0..<n.safeLen: - result[i] = onlyReplaceParams(c, n[i]) - proc newGenSym(kind: TSymKind, n: PNode, c: var TemplCtx): PSym = result = newSym(kind, considerQuotedIdent(c.c, n), c.c.idgen, c.owner, n.info) incl(result.flags, sfGenSym) @@ -192,24 +187,10 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = k == skField: # even if injected, don't produce a sym choice here: #n = semTemplBody(c, n) - var x = n - 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): - if k != skField: 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: if (n.kind == nkPragmaExpr and n.len >= 2 and n[1].kind == nkPragma): let pragmaNode = n[1] @@ -219,15 +200,15 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = var found = false if ni.kind == nkIdent: for a in templatePragmas: - if ni.ident == getIdent(c.c.cache, $a): + if ni.ident.id == ord(a): found = true break if not found: openScope(c) pragmaNode[i] = semTemplBody(c, pragmaNode[i]) closeScope(c) - let ident = getIdentNode(c, n) - if not isTemplParam(c, ident): + 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) @@ -236,77 +217,111 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = replaceIdentBySym(c.c, n, newSymNode(local, n.info)) if k == skParam and c.inTemplateHeader > 0: local.flags.incl sfTemplateParam - else: - replaceIdentBySym(c.c, n, ident) -proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = +proc semTemplSymbol(c: var TemplCtx, n: PNode, s: PSym; isField, isAmbiguous: bool): PNode = incl(s.flags, sfUsed) # 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, s) + 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-{skTemplate,skMacro}: - result = symChoice(c, n, s, scOpen, isField) - of skTemplate, skMacro: - result = symChoice(c, n, s, scOpen, isField) - if result.kind == nkSym: - # template/macro symbols might need to be semchecked again - # prepareOperand etc don't do this without setting the type to nil - result.typ = nil + of OverloadableSyms: + 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: if isField and sfGenSym in s.flags: result = n - else: result = newSymNodeTypeDesc(s, c.idgen, n.info) + 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: if isField and sfGenSym in s.flags: result = n - else: result = newSymNodeTypeDesc(s, c.idgen, n.info) + 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: if isField and sfGenSym in s.flags: result = n - else: result = newSymNode(s, n.info) + 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.graph, n.info, s, c.graph.usageSym, false) + 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, n.info, s) + 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) onUse(n.info, s) else: for i in 0..<n.safeLen: - result[i] = semRoutineInTemplName(c, n[i]) + 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[pragmasPos]) == spGenSym: - let ident = getIdentNode(c, n[namePos]) - if not isTemplParam(c, ident): - 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) + 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[namePos] = ident - else: - n[namePos] = semRoutineInTemplName(c, n[namePos]) + n[namePos] = semRoutineInTemplName(c, n[namePos], binding == spInject) # open scope for parameters openScope(c) for i in patternPos..paramsPos-1: @@ -363,6 +378,7 @@ 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 and sfTemplateParam in s.flags: @@ -380,9 +396,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = result = newSymNode(s, n.info) onUse(n.info, s) else: - if s.kind in {skType, skVar, skLet, skConst}: + if s.kind in {skVar, skLet, skConst}: discard qualifiedLookUp(c.c, n, {checkAmbiguity, checkModule}) - result = semTemplSymbol(c.c, n, s, c.noGenSym > 0) + result = semTemplSymbol(c, n, s, c.noGenSym > 0, c.c.isAmbiguous) of nkBind: result = semTemplBody(c, n[0]) of nkBindStmt: @@ -527,14 +543,21 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = if x.kind == nkExprColonExpr: 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]) + 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, nkSinkAsgn: checkSonsLen(n, 2, c.c.config) @@ -544,17 +567,21 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = 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) + 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) @@ -565,8 +592,10 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = 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: @@ -578,7 +607,9 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = elif contains(c.toMixin, s.name.id): return symChoice(c.c, n, s, scForceOpen, c.noGenSym > 0) else: - return symChoice(c.c, n, s, scOpen, c.noGenSym > 0) + 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]) @@ -662,6 +693,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = pushOwner(c, s) openScope(c) n[namePos] = newSymNode(s) + s.ast = n # for implicitPragmas to use pragmaCallable(c, s, n, templatePragmas) implicitPragmas(c, s, n.info, templatePragmas) @@ -674,6 +706,9 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = # 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: + 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): @@ -687,7 +722,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = # XXX why do we need tyTyped as a return type again? s.typ.n = newNodeI(nkFormalParams, n.info) rawAddSon(s.typ, newTypeS(tyTyped, c)) - s.typ.n.add newNodeIT(nkType, n.info, s.typ[0]) + 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] @@ -702,12 +737,24 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = if n[patternPos].kind != nkEmpty: n[patternPos] = semPattern(c, n[patternPos], s) - var ctx: TemplCtx - ctx.toBind = initIntSet() - ctx.toMixin = initIntSet() - ctx.toInject = initIntSet() - ctx.c = c - ctx.owner = 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[bodyPos] = semTemplBodyDirty(ctx, n[bodyPos]) else: @@ -717,11 +764,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = closeScope(c) popOwner(c) - # set the symbol AST after pragmas, at least. This stops pragma that have - # been pushed (implicit) to be explicitly added to the template definition - # and misapplied to the body. see #18113 - s.ast = n - if sfCustomPragma in s.flags: if n[bodyPos].kind != nkEmpty: localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s) @@ -858,12 +900,13 @@ proc semPatternBody(c: var TemplCtx, 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: |