diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/semstmts.nim | 286 | ||||
-rw-r--r-- | compiler/semtempl.nim | 5 |
2 files changed, 175 insertions, 116 deletions
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index f302dd4c3..33e304ab2 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -341,8 +341,12 @@ proc checkNilable(c: PContext; v: PSym) = #include liftdestructors -proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) = - let value = identDefs[^1] +proc addToVarSection(c: PContext; result: var PNode; n: PNode) = + if result.kind != nkStmtList: + result = makeStmtList(result) + result.add n + +proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) = if result.kind == nkStmtList: let o = copyNode(orig) o.add identDefs @@ -445,55 +449,115 @@ proc setVarType(c: PContext; v: PSym, typ: PType) = "; new type is: " & typeToString(typ, preferDesc)) v.typ = typ -proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode = - var b = a[0] - if b.kind == nkPragmaExpr: - if b[1].len != 1: - # we could in future support pragmas w args e.g.: `var foo {.bar:"goo".} = expr` - return nil - let nodePragma = b[1][0] - # see: `singlePragma` - - var amb = false - var sym: PSym = nil - case nodePragma.kind - of nkIdent, nkAccQuoted: - let ident = considerQuotedIdent(c, nodePragma) - var userPragma = strTableGet(c.userPragmas, ident) - if userPragma != nil: return nil - let w = nodePragma.whichPragma - if n.kind == nkVarSection and w in varPragmas or - n.kind == nkLetSection and w in letPragmas or - n.kind == nkConstSection and w in constPragmas: - return nil - sym = searchInScopes(c, ident, amb) - # XXX what if amb is true? - # CHECKME: should that test also apply to `nkSym` case? - if sym == nil or sfCustomPragma in sym.flags: return nil - of nkSym: - sym = nodePragma.sym - else: - return nil - # skip if not in scope; skip `template myAttr() {.pragma.}` - - let lhs = b[0] - let clash = strTableGet(c.currentScope.symbols, lhs.ident) - if clash != nil: - # refs https://github.com/nim-lang/Nim/issues/8275 - wrongRedefinition(c, lhs.info, lhs.ident.s, clash.info) - - result = newTree(nkCall) - result.add nodePragma - result.add lhs - if a[1].kind != nkEmpty: - result.add a[1] - else: - result.add newNodeIT(nkNilLit, a.info, c.graph.sysTypes[tyNil]) - result.add a[2] - result.info = a.info - let ret = newNodeI(nkStmtList, a.info) - ret.add result - result = semExprNoType(c, ret) +proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool = + # make sure it's not a normal pragma, and calls an identifier + # considerQuotedIdent below will fail on non-identifiers + result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds + if result: + # make sure it's not a user pragma + let ident = considerQuotedIdent(c, key) + result = strTableGet(c.userPragmas, ident) == nil + if result: + # make sure it's not a custom pragma + var amb = false + let sym = searchInScopes(c, ident, amb) + result = sym == nil or sfCustomPragma notin sym.flags + +proc copyExcept(n: PNode, i: int): PNode = + result = copyNode(n) + for j in 0..<n.len: + if j != i: result.add(n[j]) + +proc semVarMacroPragma(c: PContext, a: PNode, n: PNode): PNode = + # Mirrored with semProcAnnotation + result = nil + # a, b {.prag.}: int = 3 not allowed + const lhsPos = 0 + if a.len == 3 and a[lhsPos].kind == nkPragmaExpr: + var b = a[lhsPos] + const + namePos = 0 + pragmaPos = 1 + let pragmas = b[pragmaPos] + for i in 0 ..< pragmas.len: + let it = pragmas[i] + let key = if it.kind in nkPragmaCallKinds and it.len >= 1: it[0] else: it + + when false: + let lhs = b[0] + let clash = strTableGet(c.currentScope.symbols, lhs.ident) + if clash != nil: + # refs https://github.com/nim-lang/Nim/issues/8275 + wrongRedefinition(c, lhs.info, lhs.ident.s, clash.info) + + if isPossibleMacroPragma(c, it, key): + # we transform ``var p {.m, rest.}`` into ``m(do: var p {.rest.})`` and + # let the semantic checker deal with it: + var x = newNodeI(nkCall, key.info) + x.add(key) + + if it.kind in nkPragmaCallKinds and it.len > 1: + # pass pragma arguments to the macro too: + for i in 1..<it.len: + x.add(it[i]) + + # Drop the pragma from the list, this prevents getting caught in endless + # recursion when the nkCall is semanticized + let oldExpr = a[lhsPos] + let newPragmas = copyExcept(pragmas, i) + if newPragmas.kind != nkEmpty and newPragmas.len == 0: + a[lhsPos] = oldExpr[namePos] + else: + a[lhsPos] = copyNode(oldExpr) + a[lhsPos].add(oldExpr[namePos]) + a[lhsPos].add(newPragmas) + + var unarySection = newNodeI(n.kind, a.info) + unarySection.add(a) + x.add(unarySection) + + # recursion assures that this works for multiple macro annotations too: + var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared}) + if r == nil: + # Restore the old list of pragmas since we couldn't process this + a[lhsPos] = oldExpr + # No matching macro was found but there's always the possibility this may + # be a .pragma. template instead + continue + + doAssert r[0].kind == nkSym + let m = r[0].sym + case m.kind + of skMacro: result = semMacroExpr(c, r, r, m, {}) + of skTemplate: result = semTemplateExpr(c, r, m, {}) + else: + a[lhsPos] = oldExpr + continue + + doAssert result != nil + + # since a macro pragma can set pragmas, we process these here again. + # This is required for SqueakNim-like export pragmas. + if false and result.kind in {nkVarSection, nkLetSection, nkConstSection}: + var validPragmas: TSpecialWords + case result.kind + of nkVarSection: + validPragmas = varPragmas + of nkLetSection: + validPragmas = letPragmas + of nkConstSection: + validPragmas = constPragmas + else: + # unreachable + discard + for defs in result: + for i in 0 ..< defs.len - 2: + let ex = defs[i] + if ex.kind == nkPragmaExpr and + ex[namePos].kind == nkSym and + ex[pragmaPos].kind != nkEmpty: + pragma(c, defs[lhsPos][namePos].sym, defs[lhsPos][pragmaPos], validPragmas) + return result proc errorSymChoiceUseQualifier(c: PContext; n: PNode) = assert n.kind in nkSymChoices @@ -508,10 +572,6 @@ proc errorSymChoiceUseQualifier(c: PContext; n: PNode) = localError(c.config, n.info, errGenerated, err) proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = - if n.len == 1: - result = semLowerLetVarCustomPragma(c, n[0], n) - if result != nil: return result - var b: PNode result = copyNode(n) for i in 0..<n.len: @@ -521,6 +581,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if a.kind notin {nkIdentDefs, nkVarTuple}: illFormedAst(a, c.config) checkMinSonsLen(a, 3, c.config) + b = semVarMacroPragma(c, a, n) + if b != nil: + addToVarSection(c, result, b) + continue + var typ: PType = nil if a[^2].kind != nkEmpty: typ = semTypeNode(c, a[^2], nil) @@ -663,6 +728,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = proc semConst(c: PContext, n: PNode): PNode = result = copyNode(n) inc c.inStaticContext + var b: PNode for i in 0..<n.len: var a = n[i] if c.config.cmd == cmdIdeTools: suggestStmt(c, a) @@ -670,6 +736,11 @@ proc semConst(c: PContext, n: PNode): PNode = if a.kind notin {nkConstDef, nkVarTuple}: illFormedAst(a, c.config) checkMinSonsLen(a, 3, c.config) + b = semVarMacroPragma(c, a, n) + if b != nil: + addToVarSection(c, result, b) + continue + var typ: PType = nil if a[^2].kind != nkEmpty: typ = semTypeNode(c, a[^2], nil) @@ -704,7 +775,6 @@ proc semConst(c: PContext, n: PNode): PNode = typFlags.incl taConcept typeAllowedCheck(c, a.info, typ, skConst, typFlags) - var b: PNode if a.kind == nkVarTuple: if typ.kind != tyTuple: localError(c.config, a.info, errXExpected, "tuple") @@ -735,7 +805,7 @@ proc semConst(c: PContext, n: PNode): PNode = v.ast = if def[j].kind != nkExprColonExpr: def[j] else: def[j][1] b[j] = newSymNode(v) - result.add b + addToVarSection(c, result, n, b) dec c.inStaticContext include semfields @@ -1519,77 +1589,61 @@ proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) = n.add newSymNode(c.p.resultSym) addParamOrResult(c, c.p.resultSym, owner) -proc copyExcept(n: PNode, i: int): PNode = - result = copyNode(n) - for j in 0..<n.len: - if j != i: result.add(n[j]) - proc semProcAnnotation(c: PContext, prc: PNode; validPragmas: TSpecialWords): PNode = + # Mirrored with semVarMacroPragma var n = prc[pragmasPos] if n == nil or n.kind == nkEmpty: return for i in 0..<n.len: let it = n[i] let key = if it.kind in nkPragmaCallKinds and it.len >= 1: it[0] else: it - if whichPragma(it) != wInvalid: - # Not a custom pragma - continue - else: - let ident = considerQuotedIdent(c, key) - if strTableGet(c.userPragmas, ident) != nil: - continue # User defined pragma - else: - var amb = false - let sym = searchInScopes(c, ident, amb) - if sym != nil and sfCustomPragma in sym.flags: - continue # User custom pragma - - # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and - # let the semantic checker deal with it: - var x = newNodeI(nkCall, key.info) - x.add(key) - - if it.kind in nkPragmaCallKinds and it.len > 1: - # pass pragma arguments to the macro too: - for i in 1..<it.len: - x.add(it[i]) - - # Drop the pragma from the list, this prevents getting caught in endless - # recursion when the nkCall is semanticized - prc[pragmasPos] = copyExcept(n, i) - if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0: - prc[pragmasPos] = c.graph.emptyNode - - x.add(prc) - - # recursion assures that this works for multiple macro annotations too: - var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared}) - if r == nil: - # Restore the old list of pragmas since we couldn't process this - prc[pragmasPos] = n - # No matching macro was found but there's always the possibility this may - # be a .pragma. template instead - continue + if isPossibleMacroPragma(c, it, key): + # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and + # let the semantic checker deal with it: + var x = newNodeI(nkCall, key.info) + x.add(key) + + if it.kind in nkPragmaCallKinds and it.len > 1: + # pass pragma arguments to the macro too: + for i in 1..<it.len: + x.add(it[i]) + + # Drop the pragma from the list, this prevents getting caught in endless + # recursion when the nkCall is semanticized + prc[pragmasPos] = copyExcept(n, i) + if prc[pragmasPos].kind != nkEmpty and prc[pragmasPos].len == 0: + prc[pragmasPos] = c.graph.emptyNode + + x.add(prc) + + # recursion assures that this works for multiple macro annotations too: + var r = semOverloadedCall(c, x, x, {skMacro, skTemplate}, {efNoUndeclared}) + if r == nil: + # Restore the old list of pragmas since we couldn't process this + prc[pragmasPos] = n + # No matching macro was found but there's always the possibility this may + # be a .pragma. template instead + continue - doAssert r[0].kind == nkSym - let m = r[0].sym - case m.kind - of skMacro: result = semMacroExpr(c, r, r, m, {}) - of skTemplate: result = semTemplateExpr(c, r, m, {}) - else: - prc[pragmasPos] = n - continue + doAssert r[0].kind == nkSym + let m = r[0].sym + case m.kind + of skMacro: result = semMacroExpr(c, r, r, m, {}) + of skTemplate: result = semTemplateExpr(c, r, m, {}) + else: + prc[pragmasPos] = n + continue - doAssert result != nil + doAssert result != nil - # since a proc annotation can set pragmas, we process these here again. - # This is required for SqueakNim-like export pragmas. - if result.kind in procDefs and result[namePos].kind == nkSym and - result[pragmasPos].kind != nkEmpty: - pragma(c, result[namePos].sym, result[pragmasPos], validPragmas) + # since a proc annotation can set pragmas, we process these here again. + # This is required for SqueakNim-like export pragmas. + if false and result.kind in procDefs and result[namePos].kind == nkSym and + result[pragmasPos].kind != nkEmpty: + pragma(c, result[namePos].sym, result[pragmasPos], validPragmas) - return + return result proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} = ## used for resolving 'auto' in lambdas based on their callsite diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index b921fda2c..e7c74a767 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -603,7 +603,12 @@ proc semTemplBodyDirty(c: var TemplCtx, n: PNode): PNode = 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): |