diff options
Diffstat (limited to 'compiler/pragmas.nim')
-rw-r--r-- | compiler/pragmas.nim | 301 |
1 files changed, 200 insertions, 101 deletions
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index b60331b29..9a298cd90 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -10,9 +10,13 @@ # This module implements semantic checking for pragmas import - os, condsyms, ast, astalgo, idents, semdata, msgs, renderer, - wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees, - types, lookups, lineinfos, pathutils, linter + 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,12 +33,12 @@ 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, wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy, - wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect} + wRequires, wEnsures, wEnforceNoRaises, wSystemRaisesDefect, wVirtual, wQuirky, wMember} converterPragmas* = procPragmas methodPragmas* = procPragmas+{wBase}-{wImportCpp} templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty, @@ -71,18 +75,20 @@ const wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow, wIncompleteStruct, wCompleteStruct, wByCopy, wByRef, wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, - wCppNonPod, wBorrow, wGcSafe, wPartial, wExplain, wPackage} + wCppNonPod, wBorrow, wGcSafe, wPartial, wExplain, wPackage, wCodegenDecl, + 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, wCompilerProc, wCore} - paramPragmas* = {wNoalias, wInject, wGensym} + wIntDefine, wStrDefine, wBoolDefine, wDefine, + wCompilerProc, wCore} + paramPragmas* = {wNoalias, wInject, wGensym, wByRef, wByCopy, wCodegenDecl, wExportc, wExportCpp} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect, wThread, wRaises, wEffectsOf, wLocks, wTags, wForbids, wGcSafe, @@ -92,6 +98,7 @@ const enumFieldPragmas* = {wDeprecated} proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode = + result = nil let p = procAst[pragmasPos] if p.kind == nkEmpty: return nil for it in p: @@ -114,8 +121,17 @@ 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) = - localError(c.config, n.info, "cannot attach a custom pragma to '" & s.name.s & "'") + var msg = "cannot attach a custom pragma to '" & s.name.s & "'" + if s != nil: + msg.add("; custom pragmas are not supported for ") + case s.kind + of skForVar: msg.add("`for` loop variables") + of skEnumField: msg.add("enum fields") + of skModule: msg.add("modules") + else: msg.add("symbol kind " & $s.kind) + localError(c.config, n.info, msg) proc pragmaProposition(c: PContext, n: PNode) = if n.kind notin nkPragmaCallKinds or n.len != 2: @@ -129,44 +145,28 @@ 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: - var s = newSym(skResult, getIdent(c.cache, "result"), nextSymId(c.idgen), o, n.info) - s.typ = o.typ.sons[0] + 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.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 - if c.config.cmd == cmdNimfix and '$' notin extname: - # note that '{.importc.}' is transformed into '{.importc: "$1".}' - s.loc.flags.incl(lfFullExternalName) + s.cname = $s.loc.snippet + proc makeExternImport(c: PContext; s: PSym, extname: string, info: TLineInfo) = setExternName(c, s, extname, info) @@ -201,9 +201,9 @@ proc processImportObjC(c: PContext; s: PSym, extname: string, info: TLineInfo) = let m = s.getModule() incl(m.flags, sfCompileToObjc) -proc newEmptyStrNode(c: PContext; n: PNode): PNode {.noinline.} = +proc newEmptyStrNode(c: PContext; n: PNode, strVal: string = ""): PNode {.noinline.} = result = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString)) - result.strVal = "" + result.strVal = strVal proc getStrLitNode(c: PContext, n: PNode): PNode = if n.kind notin nkPragmaCallKinds or n.len != 2: @@ -223,6 +223,7 @@ proc expectStrLit(c: PContext, n: PNode): string = result = getStrLitNode(c, n).strVal proc expectIntLit(c: PContext, n: PNode): int = + result = 0 if n.kind notin nkPragmaCallKinds or n.len != 2: localError(c.config, n.info, errIntLiteralExpected) else: @@ -235,8 +236,17 @@ proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string = if n.kind in nkPragmaCallKinds: result = expectStrLit(c, n) else: result = defaultStr +proc processVirtual(c: PContext, n: PNode, s: PSym, flag: TSymFlag) = + s.constraint = newEmptyStrNode(c, n, getOptionalStr(c, n, "$1")) + s.constraint.strVal = s.constraint.strVal % s.name.s + s.flags.incl {flag, sfInfixCall, sfExportc, sfMangleCpp} + + s.typ.callConv = ccMember + incl c.config.globalOptions, optMixedMode + proc processCodegenDecl(c: PContext, n: PNode, sym: PSym) = sym.constraint = getStrLitNode(c, n) + sym.flags.incl sfCodegenDecl proc processMagic(c: PContext, n: PNode, s: PSym) = #if sfSystemModule notin c.module.flags: @@ -259,6 +269,7 @@ proc wordToCallConv(sw: TSpecialWord): TCallingConvention = TCallingConvention(ord(ccNimCall) + ord(sw) - ord(wNimcall)) proc isTurnedOn(c: PContext, n: PNode): bool = + result = false if n.kind in nkPragmaCallKinds and n.len == 2: let x = c.semConstBoolExpr(c, n[1]) n[1] = x @@ -269,7 +280,7 @@ proc onOff(c: PContext, n: PNode, op: TOptions, resOptions: var TOptions) = if isTurnedOn(c, n): resOptions.incl op else: resOptions.excl op -proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) = +proc pragmaNoForward*(c: PContext, n: PNode; flag=sfNoForward) = if isTurnedOn(c, n): incl(c.module.flags, flag) c.features.incl codeReordering @@ -282,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) @@ -301,7 +330,7 @@ proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib = result.path = path c.libs.add result if path.kind in {nkStrLit..nkTripleStrLit}: - result.isOverriden = options.isDynlibOverride(c.config, path.strVal) + result.isOverridden = options.isDynlibOverride(c.config, path.strVal) proc expectDynlibNode(c: PContext, n: PNode): PNode = if n.kind notin nkPragmaCallKinds or n.len != 2: @@ -313,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) @@ -321,12 +350,12 @@ proc expectDynlibNode(c: PContext, n: PNode): PNode = proc processDynLib(c: PContext, n: PNode, sym: PSym) = if (sym == nil) or (sym.kind == skModule): let lib = getLib(c, libDynamic, expectDynlibNode(c, n)) - if not lib.isOverriden: + if not lib.isOverridden: c.optionStack[^1].dynlib = lib else: if n.kind in nkPragmaCallKinds: var lib = getLib(c, libDynamic, expectDynlibNode(c, n)) - if not lib.isOverriden: + if not lib.isOverridden: addToLib(lib, sym) incl(sym.loc.flags, lfDynamicLib) else: @@ -341,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 @@ -363,7 +392,7 @@ proc processNote(c: PContext, n: PNode) = else: invalidPragma(c, n) else: invalidPragma(c, n) -proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} = +proc pragmaToOptions*(w: TSpecialWord): TOptions {.inline.} = case w of wChecks: ChecksOptions of wObjChecks: {optObjCheck} @@ -389,6 +418,7 @@ proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} = of wImplicitStatic: {optImplicitStatic} of wPatterns, wTrMacros: {optTrMacros} of wSinkInference: {optSinkInference} + of wQuirky: {optQuirky} else: {} proc processExperimental(c: PContext; n: PNode) = @@ -450,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") @@ -457,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] @@ -476,8 +519,16 @@ proc processPop(c: PContext, n: PNode) = when defined(debugOptions): echo c.config $ n.info, " POP config is now ", c.config.options -proc processDefine(c: PContext, n: PNode) = - if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent): +proc processDefineConst(c: PContext, n: PNode, sym: PSym, kind: TMagic) = + sym.magic = kind + if n.kind in nkPragmaCallKinds and n.len == 2: + # could also use TLib + n[1] = getStrLitNode(c, n) + +proc processDefine(c: PContext, n: PNode, sym: PSym) = + if sym != nil and sym.kind == skConst: + processDefineConst(c, n, sym, mGenericDefine) + elif (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent): defineSymbol(c.config.symbols, n[1].ident.s) else: invalidPragma(c, n) @@ -500,18 +551,25 @@ proc relativeFile(c: PContext; n: PNode; ext=""): AbsoluteFile = if result.isEmpty: result = AbsoluteFile s proc processCompile(c: PContext, n: PNode) = + ## This pragma can take two forms. The first is a simple file input: + ## {.compile: "file.c".} + ## The second is a tuple where the second arg is the output name strutils formatter: + ## {.compile: ("file.c", "$1.o").} proc docompile(c: PContext; it: PNode; src, dest: AbsoluteFile; customArgs: string) = var cf = Cfile(nimname: splitFile(src).name, cname: src, obj: dest, flags: {CfileFlag.External}, customArgs: customArgs) - extccomp.addExternalFileToCompile(c.config, cf) - recordPragma(c, it, "compile", src.string, dest.string, customArgs) + if not fileExists(src): + localError(c.config, n.info, "cannot find: " & src.string) + else: + extccomp.addExternalFileToCompile(c.config, cf) + recordPragma(c, it, "compile", src.string, dest.string, customArgs) proc getStrLit(c: PContext, n: PNode; i: int): string = n[i] = c.semConstExpr(c, n[i]) case n[i].kind of nkStrLit, nkRStrLit, nkTripleStrLit: - when defined(gcArc) or defined(gcOrc): + when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc): result = n[i].strVal else: shallowCopy(result, n[i].strVal) @@ -545,7 +603,8 @@ proc processCompile(c: PContext, n: PNode) = else: found = findFile(c.config, s) if found.isEmpty: found = AbsoluteFile s - let obj = toObjFile(c.config, completeCfilePath(c.config, found, false)) + let mangled = completeCfilePath(c.config, mangleModuleName(c.config, found).AbsoluteFile) + let obj = toObjFile(c.config, mangled) docompile(c, it, found, obj, customArgs) proc processLink(c: PContext, n: PNode) = @@ -557,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") @@ -588,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: @@ -597,7 +658,7 @@ proc pragmaEmit(c: PContext, n: PNode) = if n1.kind == nkBracket: var b = newNodeI(nkBracket, n1.info, n1.len) for i in 0..<n1.len: - b[i] = c.semExpr(c, n1[i]) + b[i] = c.semExprWithType(c, n1[i], {efTypeAllowed}) n[1] = b else: n[1] = c.semConstExpr(c, n1) @@ -646,11 +707,15 @@ 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, nextSymId(c.idgen), c.module, it.info, c.config.options) + var userPragma = newSym(skTemplate, it[1].ident, c.idgen, c.module, it.info, c.config.options) + styleCheckDef(c, userPragma) userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1]) strTableAdd(c.userPragmas, userPragma) @@ -710,19 +775,8 @@ proc deprecatedStmt(c: PContext; outerPragma: PNode) = return if pragma.kind != nkBracket: localError(c.config, pragma.info, "list of key:value pairs expected"); return - for n in pragma: - if n.kind in nkPragmaCallKinds and n.len == 2: - let dest = qualifiedLookUp(c, n[1], {checkUndeclared}) - if dest == nil or dest.kind in routineKinds: - localError(c.config, n.info, warnUser, "the .deprecated pragma is unreliable for routines") - let src = considerQuotedIdent(c, n[0]) - let alias = newSym(skAlias, src, nextSymId(c.idgen), dest, n[0].info, c.config.options) - incl(alias.flags, sfExported) - if sfCompilerProc in dest.flags: markCompilerProc(c, alias) - addInterfaceDecl(c, alias) - n[1] = newSymNode(dest) - else: - localError(c.config, n.info, "key:value pair expected") + message(c.config, pragma.info, warnDeprecated, + "deprecated statement is now a no-op, use regular deprecated pragma") proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym = if it.kind notin nkPragmaCallKinds or it.len != 2: @@ -738,31 +792,39 @@ proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym = # We return a dummy symbol; later passes over the type will repair it. # Generic instantiation needs to know about this too. But we're lazy # and perform the lookup on demand instead. - result = newSym(skUnknown, considerQuotedIdent(c, n), nextSymId(c.idgen), nil, n.info, + result = newSym(skUnknown, considerQuotedIdent(c, n), c.idgen, nil, n.info, c.config.options) else: result = qualifiedLookUp(c, n, {checkUndeclared}) -proc semCustomPragma(c: PContext, n: PNode): PNode = +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) return n + # we have a valid custom pragma + if sym != nil and sym.kind in {skEnumField, skForVar, skModule}: + illegalCustomPragma(c, n, sym) + return n + result = r # Transform the nkCall node back to its original form if possible if n.kind == nkIdent and r.len == 1: @@ -795,8 +857,10 @@ proc processEffectsOf(c: PContext, n: PNode; owner: PSym) = proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, validPragmas: TSpecialWords, comesFromPush, isStatement: bool): bool = + result = false var it = n[i] - var key = if it.kind in nkPragmaCallKinds and it.len > 1: it[0] else: it + let keyDeep = it.kind in nkPragmaCallKinds and it.len > 1 + var key = if keyDeep: it[0] else: it if key.kind == nkBracketExpr: processNote(c, it) return @@ -810,7 +874,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, else: discard return elif key.kind notin nkIdentKinds: - n[i] = semCustomPragma(c, it) + n[i] = semCustomPragma(c, it, sym) return let ident = considerQuotedIdent(c, key) var userPragma = strTableGet(c.userPragmas, ident) @@ -819,17 +883,20 @@ 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) + if keyDeep: + localError(c.config, it.info, "user pragma cannot have arguments") + 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: - checkPragmaUse(c.config, key.info, k, ident.s) + checkPragmaUse(c, key.info, k, ident.s, (if sym != nil: sym else: c.module)) case k of wExportc, wExportCpp: makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info) @@ -917,7 +984,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wThreadVar: noVal(c, it) incl(sym.flags, {sfThread, sfGlobal}) - of wDeadCodeElimUnused: discard # deprecated, dead code elim always on + of wDeadCodeElimUnused: + warningDeprecated(c.config, n.info, "'{.deadcodeelim: on.}' is deprecated, now a noop") # deprecated, dead code elim always on of wNoForward: pragmaNoForward(c, it) of wReorder: pragmaNoForward(c, it, flag = sfReorder) of wMagic: processMagic(c, it, sym) @@ -933,12 +1001,13 @@ 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: - noVal(c, it) incl(sym.flags, sfConstructor) + if sfImportc notin sym.flags: + sym.constraint = newEmptyStrNode(c, it, getOptionalStr(c, it, "")) + sym.constraint.strVal = sym.constraint.strVal + sym.flags.incl {sfExportc, sfMangleCpp} + sym.typ.callConv = ccNoConvention of wHeader: var lib = getLib(c, libHeader, getStrLitNode(c, it)) addToLib(lib, sym) @@ -946,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: @@ -960,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: @@ -1032,6 +1101,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, if sym.typ != nil: incl(sym.typ.flags, tfThread) if sym.typ.callConv == ccClosure: sym.typ.callConv = ccNimCall + of wSendable: + noVal(c, it) + if sym != nil and sym.typ != nil: + incl(sym.typ.flags, tfSendable) + else: + invalidPragma(c, it) of wGcSafe: noVal(c, it) if sym != nil: @@ -1066,21 +1141,29 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, recordPragma(c, it, "error", s) localError(c.config, it.info, errUser, s) of wFatal: fatal(c.config, it.info, expectStrLit(c, it)) - of wDefine: processDefine(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: @@ -1156,13 +1239,17 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, invalidPragma(c, it) of wByRef: noVal(c, it) - if sym == nil or sym.typ == nil: + if sym != nil and sym.kind == skParam: + sym.options.incl optByRef + elif sym == nil or sym.typ == nil: processOption(c, it, c.config.options) else: incl(sym.typ.flags, tfByRef) of wByCopy: noVal(c, it) - if sym.kind != skType or sym.typ == nil: invalidPragma(c, it) + if sym.kind == skParam: + incl(sym.flags, sfByCopy) + elif sym.kind != skType or sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfByCopy) of wPartial: noVal(c, it) @@ -1213,35 +1300,36 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, noVal(c, it) sym.flags.incl sfBase of wIntDefine: - sym.magic = mIntDefine + processDefineConst(c, n, sym, mIntDefine) of wStrDefine: - sym.magic = mStrDefine + processDefineConst(c, n, sym, mStrDefine) of wBoolDefine: - sym.magic = mBoolDefine + processDefineConst(c, n, sym, mBoolDefine) of wUsed: 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: pragmaEnsures(c, it) - of wEnforceNoRaises: + of wEnforceNoRaises, wQuirky: sym.flags.incl sfNeverRaises of wSystemRaisesDefect: sym.flags.incl sfSystemRaisesDefect + of wVirtual: + processVirtual(c, it, sym, sfVirtual) + of wMember: + processVirtual(c, it, sym, sfMember) + else: invalidPragma(c, it) elif comesFromPush and whichKeyword(ident) != wInvalid: discard "ignore the .push pragma; it doesn't apply" else: - if sym == nil or (sym.kind in {skVar, skLet, skConst, skParam, skIterator, - skField, skProc, skFunc, skConverter, skMethod, skType}): - n[i] = semCustomPragma(c, it) - elif sym != nil: - illegalCustomPragma(c, it, sym) - else: - invalidPragma(c, it) + # semCustomPragma gives appropriate error for invalid pragmas + n[i] = semCustomPragma(c, it, sym) proc overwriteLineInfo(n: PNode; info: TLineInfo) = n.info = info @@ -1256,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: @@ -1269,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") @@ -1278,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 |