diff options
Diffstat (limited to 'compiler/pragmas.nim')
-rw-r--r-- | compiler/pragmas.nim | 137 |
1 files changed, 92 insertions, 45 deletions
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 56e25c0b4..9a298cd90 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -10,10 +10,14 @@ # This module implements semantic checking for pragmas import - os, condsyms, ast, astalgo, idents, semdata, msgs, renderer, - wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees, + condsyms, ast, astalgo, idents, semdata, msgs, renderer, + wordrecg, ropes, options, extccomp, magicsys, trees, types, lookups, lineinfos, pathutils, linter, modulepaths +from sigmatch import trySuggestPragmas + +import std/[os, math, strutils] + when defined(nimPreviewSlimSystem): import std/assertions @@ -29,7 +33,7 @@ const ## common pragmas for declarations, to a good approximation procPragmas* = declPragmas + {FirstCallConv..LastCallConv, wMagic, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader, - wCompilerProc, wNonReloadable, wCore, wProcVar, wVarargs, wCompileTime, wMerge, + wCompilerProc, wNonReloadable, wCore, wProcVar, wVarargs, wCompileTime, wBorrow, wImportCompilerProc, wThread, wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl, wGensym, wInject, wRaises, wEffectsOf, wTags, wForbids, wLocks, wDelegator, wGcSafe, @@ -72,19 +76,19 @@ const wIncompleteStruct, wCompleteStruct, wByCopy, wByRef, wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, wCppNonPod, wBorrow, wGcSafe, wPartial, wExplain, wPackage, wCodegenDecl, - wSendable} + wSendable, wNoInit} fieldPragmas* = declPragmas + {wGuard, wBitsize, wCursor, - wRequiresInit, wNoalias, wAlign} - {wExportNims, wNodecl} # why exclude these? + wRequiresInit, wNoalias, wAlign, wNoInit} - {wExportNims, wNodecl} # why exclude these? varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar, wMagic, wHeader, wCompilerProc, wCore, wDynlib, - wNoInit, wCompileTime, wGlobal, + wNoInit, wCompileTime, wGlobal, wLiftLocals, wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor, wNoalias, wAlign} constPragmas* = declPragmas + {wHeader, wMagic, wGensym, wInject, wIntDefine, wStrDefine, wBoolDefine, wDefine, wCompilerProc, wCore} - paramPragmas* = {wNoalias, wInject, wGensym, wByRef, wByCopy, wCodegenDecl} + paramPragmas* = {wNoalias, wInject, wGensym, wByRef, wByCopy, wCodegenDecl, wExportc, wExportCpp} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect, wThread, wRaises, wEffectsOf, wLocks, wTags, wForbids, wGcSafe, @@ -117,6 +121,7 @@ const proc invalidPragma*(c: PContext; n: PNode) = localError(c.config, n.info, "invalid pragma: " & renderTree(n, {renderNoComments})) + proc illegalCustomPragma*(c: PContext, n: PNode, s: PSym) = var msg = "cannot attach a custom pragma to '" & s.name.s & "'" if s != nil: @@ -140,41 +145,27 @@ proc pragmaEnsures(c: PContext, n: PNode) = else: openScope(c) let o = getCurrOwner(c) - if o.kind in routineKinds and o.typ != nil and o.typ.sons[0] != nil: + if o.kind in routineKinds and o.typ != nil and o.typ.returnType != nil: var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen, o, n.info) - s.typ = o.typ.sons[0] + s.typ = o.typ.returnType incl(s.flags, sfUsed) addDecl(c, s) n[1] = c.semExpr(c, n[1]) closeScope(c) -proc pragmaAsm*(c: PContext, n: PNode): char = - result = '\0' - if n != nil: - for i in 0..<n.len: - let it = n[i] - if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent: - case whichKeyword(it[0].ident) - of wSubsChar: - if it[1].kind == nkCharLit: result = chr(int(it[1].intVal)) - else: invalidPragma(c, it) - else: invalidPragma(c, it) - else: - invalidPragma(c, it) - proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) = # special cases to improve performance: if extname == "$1": - s.loc.r = rope(s.name.s) + s.loc.snippet = rope(s.name.s) elif '$' notin extname: - s.loc.r = rope(extname) + s.loc.snippet = rope(extname) else: try: - s.loc.r = rope(extname % s.name.s) + s.loc.snippet = rope(extname % s.name.s) except ValueError: localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)") when hasFFI: - s.cname = $s.loc.r + s.cname = $s.loc.snippet proc makeExternImport(c: PContext; s: PSym, extname: string, info: TLineInfo) = @@ -250,7 +241,7 @@ proc processVirtual(c: PContext, n: PNode, s: PSym, flag: TSymFlag) = s.constraint.strVal = s.constraint.strVal % s.name.s s.flags.incl {flag, sfInfixCall, sfExportc, sfMangleCpp} - s.typ.callConv = ccNoConvention + s.typ.callConv = ccMember incl c.config.globalOptions, optMixedMode proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) = @@ -302,6 +293,24 @@ proc pragmaNoForward*(c: PContext, n: PNode; flag=sfNoForward) = "use {.experimental: \"codeReordering\".} instead; " & (if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}") & " is deprecated") +proc pragmaAsm*(c: PContext, n: PNode): char = + ## Checks asm pragmas and get's the asm subschar (default: '`'). + result = '\0' + if n != nil: + for i in 0..<n.len: + let it = n[i] + if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent: + case whichKeyword(it[0].ident) + of wSubsChar: + if it[1].kind == nkCharLit: result = chr(int(it[1].intVal)) + else: invalidPragma(c, it) + of wAsmSyntax: + let s = expectStrLit(c, it) + if s notin ["gcc", "vcc"]: invalidPragma(c, it) + else: invalidPragma(c, it) + else: + invalidPragma(c, it) + proc processCallConv(c: PContext, n: PNode) = if n.kind in nkPragmaCallKinds and n.len == 2 and n[1].kind == nkIdent: let sw = whichKeyword(n[1].ident) @@ -333,7 +342,7 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode = # {.dynlib: myGetProcAddr(...).} result = c.semExpr(c, n[1]) if result.kind == nkSym and result.sym.kind == skConst: - result = result.sym.astdef # look it up + result = c.semConstExpr(c, result) # fold const if result.typ == nil or result.typ.kind notin {tyPointer, tyString, tyProc}: localError(c.config, n.info, errStringLiteralExpected) result = newEmptyStrNode(c, n) @@ -361,7 +370,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) = proc processNote(c: PContext, n: PNode) = template handleNote(enumVals, notes) = let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown) - if x != errUnknown: + if x != errUnknown: nk = TNoteKind(x) let x = c.semConstBoolExpr(c, n[1]) n[1] = x @@ -471,6 +480,18 @@ proc processOption(c: PContext, n: PNode, resOptions: var TOptions) = # calling conventions (boring...): localError(c.config, n.info, "option expected") +proc checkPushedPragma(c: PContext, n: PNode) = + let keyDeep = n.kind in nkPragmaCallKinds and n.len > 1 + var key = if keyDeep: n[0] else: n + if key.kind in nkIdentKinds: + let ident = considerQuotedIdent(c, key) + var userPragma = strTableGet(c.userPragmas, ident) + if userPragma == nil: + let k = whichKeyword(ident) + # TODO: might as well make a list which is not accepted by `push`: emit, cast etc. + if k == wEmit: + localError(c.config, n.info, "an 'emit' pragma cannot be pushed") + proc processPush(c: PContext, n: PNode, start: int) = if n[start-1].kind in nkPragmaCallKinds: localError(c.config, n.info, "'push' cannot have arguments") @@ -478,6 +499,7 @@ proc processPush(c: PContext, n: PNode, start: int) = for i in start..<n.len: if not tryProcessOption(c, n[i], c.config.options): # simply store it somewhere: + checkPushedPragma(c, n[i]) if x.otherPragmas.isNil: x.otherPragmas = newNodeI(nkPragma, n.info) x.otherPragmas.add n[i] @@ -594,6 +616,7 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode = case n[1].kind of nkStrLit, nkRStrLit, nkTripleStrLit: result = newNodeI(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info) + if n.kind == nkAsmStmt: result.add n[0] # save asm pragmas for NIR var str = n[1].strVal if str == "": localError(con.config, n.info, "empty 'asm' statement") @@ -625,6 +648,7 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode = else: illFormedAstLocal(n, con.config) result = newNodeI(nkAsmStmt, n.info) + if n.kind == nkAsmStmt: result.add n[0] proc pragmaEmit(c: PContext, n: PNode) = if n.kind notin nkPragmaCallKinds or n.len != 2: @@ -683,9 +707,12 @@ proc pragmaLine(c: PContext, n: PNode) = proc processPragma(c: PContext, n: PNode, i: int) = ## Create and add a new custom pragma `{.pragma: name.}` node to the module's context. let it = n[i] - if it.kind notin nkPragmaCallKinds and it.safeLen == 2: invalidPragma(c, n) + if it.kind notin nkPragmaCallKinds and it.safeLen == 2: + invalidPragma(c, n) + return elif it.safeLen != 2 or it[0].kind != nkIdent or it[1].kind != nkIdent: invalidPragma(c, n) + return var userPragma = newSym(skTemplate, it[1].ident, c.idgen, c.module, it.info, c.config.options) styleCheckDef(c, userPragma) @@ -773,18 +800,21 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym = proc semCustomPragma(c: PContext, n: PNode, sym: PSym): PNode = var callNode: PNode - if n.kind in {nkIdent, nkSym}: + case n.kind + of nkIdentKinds: # pragma -> pragma() callNode = newTree(nkCall, n) - elif n.kind == nkExprColonExpr: + of nkExprColonExpr: # pragma: arg -> pragma(arg) callNode = newTree(nkCall, n[0], n[1]) - elif n.kind in nkPragmaCallKinds: + of nkPragmaCallKinds - {nkExprColonExpr}: callNode = n else: invalidPragma(c, n) return n + trySuggestPragmas(c, callNode[0]) + let r = c.semOverloadedCall(c, callNode, n, {skTemplate}, {efNoUndeclared}) if r.isNil or sfCustomPragma notin r[0].sym.flags: invalidPragma(c, n) @@ -853,6 +883,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, # number of pragmas increase/decrease with user pragma expansion inc c.instCounter + defer: dec c.instCounter if c.instCounter > 100: globalError(c.config, it.info, "recursive dependency: " & userPragma.name.s) @@ -862,7 +893,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, pragma(c, sym, userPragma.ast, validPragmas, isStatement) n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty - dec c.instCounter else: let k = whichKeyword(ident) if k in validPragmas: @@ -971,9 +1001,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, noVal(c, it) incl(sym.flags, sfGlobal) incl(sym.flags, sfPure) - of wMerge: - # only supported for backwards compat, doesn't do anything anymore - noVal(c, it) of wConstructor: incl(sym.flags, sfConstructor) if sfImportc notin sym.flags: @@ -988,7 +1015,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, incl(sym.loc.flags, lfHeader) incl(sym.loc.flags, lfNoDecl) # implies nodecl, because otherwise header would not make sense - if sym.loc.r == "": sym.loc.r = rope(sym.name.s) + if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s) of wNoSideEffect: noVal(c, it) if sym != nil: @@ -1002,7 +1029,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, # Disable the 'noreturn' annotation when in the "Quirky Exceptions" mode! if c.config.exc != excQuirky: incl(sym.flags, sfNoReturn) - if sym.typ[0] != nil: + if sym.typ.returnType != nil: localError(c.config, sym.ast[paramsPos][0].info, ".noreturn with return type not allowed") of wNoDestroy: @@ -1116,19 +1143,27 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wFatal: fatal(c.config, it.info, expectStrLit(c, it)) of wDefine: processDefine(c, it, sym) of wUndef: processUndef(c, it) - of wCompile: processCompile(c, it) + of wCompile: + let m = sym.getModule() + incl(m.flags, sfUsed) + processCompile(c, it) of wLink: processLink(c, it) of wPassl: + let m = sym.getModule() + incl(m.flags, sfUsed) let s = expectStrLit(c, it) extccomp.addLinkOption(c.config, s) recordPragma(c, it, "passl", s) of wPassc: + let m = sym.getModule() + incl(m.flags, sfUsed) let s = expectStrLit(c, it) extccomp.addCompileOption(c.config, s) recordPragma(c, it, "passc", s) of wLocalPassc: assert sym != nil and sym.kind == skModule let s = expectStrLit(c, it) + appendToModule(sym, n) extccomp.addLocalCompileOption(c.config, s, toFullPathConsiderDirty(c.config, sym.info.fileIndex)) recordPragma(c, it, "localpassl", s) of wPush: @@ -1274,7 +1309,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, noVal(c, it) if sym == nil: invalidPragma(c, it) else: sym.flags.incl sfUsed - of wLiftLocals: discard + of wLiftLocals: + sym.flags.incl(sfForceLift) of wRequires, wInvariant, wAssume, wAssert: pragmaProposition(c, it) of wEnsures: @@ -1308,12 +1344,22 @@ proc mergePragmas(n, pragmas: PNode) = else: for p in pragmas: n[pragmasPos].add p +proc mergeValidPragmas(n, pragmas: PNode, validPragmas: TSpecialWords) = + if n[pragmasPos].kind == nkEmpty: + n[pragmasPos] = newNodeI(nkPragma, n.info) + for p in pragmas: + let prag = whichPragma(p) + if prag in validPragmas: + let copy = copyTree(p) + overwriteLineInfo copy, n.info + n[pragmasPos].add copy + proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo, validPragmas: TSpecialWords) = if sym != nil and sym.kind != skModule: for it in c.optionStack: let o = it.otherPragmas - if not o.isNil and sfFromGeneric notin sym.flags: # see issue #12985 + if not o.isNil and sfFromGeneric notin sym.flags: # bug #23019 pushInfoContext(c.config, info) var i = 0 while i < o.len: @@ -1321,7 +1367,8 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo, internalError(c.config, info, "implicitPragmas") inc i popInfoContext(c.config) - if sym.kind in routineKinds and sym.ast != nil: mergePragmas(sym.ast, o) + if sym.kind in routineKinds and sym.ast != nil: + mergeValidPragmas(sym.ast, o, validPragmas) if lfExportLib in sym.loc.flags and sfExportc notin sym.flags: localError(c.config, info, ".dynlib requires .exportc") @@ -1330,7 +1377,7 @@ proc implicitPragmas*(c: PContext, sym: PSym, info: TLineInfo, sfImportc in sym.flags and lib != nil: incl(sym.loc.flags, lfDynamicLib) addToLib(lib, sym) - if sym.loc.r == "": sym.loc.r = rope(sym.name.s) + if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s) proc hasPragma*(n: PNode, pragma: TSpecialWord): bool = if n == nil: return false |