diff options
Diffstat (limited to 'compiler/pragmas.nim')
-rw-r--r-- | compiler/pragmas.nim | 1578 |
1 files changed, 984 insertions, 594 deletions
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index bc3771700..9a298cd90 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -10,219 +10,316 @@ # This module implements semantic checking for pragmas import - os, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer, - wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees, - rodread, types, lookups + 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 + +from ic / ic import addCompilerProc const FirstCallConv* = wNimcall LastCallConv* = wNoconv const - procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, - wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, - wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge, - wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC, - wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl, - wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, - wOverride, wConstructor, wExportNims, wUsed} + declPragmas = {wImportc, wImportObjC, wImportCpp, wImportJs, wExportc, wExportCpp, + wExportNims, wExtern, wDeprecated, wNodecl, wError, wUsed} + ## 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, + 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, wVirtual, wQuirky, wMember} converterPragmas* = procPragmas methodPragmas* = procPragmas+{wBase}-{wImportCpp} - templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty, - wDelegator, wExportNims, wUsed} - macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc, - wNodecl, wMagic, wNosideeffect, wCompilerproc, wDeprecated, wExtern, - wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator, - wExportNims, wUsed} - iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect, - wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern, - wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises, - wTags, wLocks, wGcSafe, wExportNims, wUsed} - exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe} - stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks, - wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, - wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError, + templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty, + wDelegator, wExportNims, wUsed, wPragma, wRedefine, wCallsite} + macroPragmas* = declPragmas + {FirstCallConv..LastCallConv, + wMagic, wNoSideEffect, wCompilerProc, wNonReloadable, wCore, + wDiscardable, wGensym, wInject, wDelegator} + iteratorPragmas* = declPragmas + {FirstCallConv..LastCallConv, wNoSideEffect, wSideEffect, + wMagic, wBorrow, + wDiscardable, wGensym, wInject, wRaises, wEffectsOf, + wTags, wForbids, wLocks, wGcSafe, wRequires, wEnsures} + exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNoSideEffect} + stmtPragmas* = { + wHint, wWarning, wError, wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop, - wBreakpoint, wWatchPoint, wPassl, wPassc, wDeadCodeElim, wDeprecated, - wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, - wLinearScanEnd, wPatterns, wEffects, wNoForward, wReorder, wComputedGoto, - wInjectStmt, wDeprecated, wExperimental, wThis} - lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl, - wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader, - wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame, - wRaises, wLocks, wTags, wGcSafe} - typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl, - wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow, - wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef, + wPassl, wPassc, wLocalPassc, + wDeadCodeElimUnused, # deprecated, always on + wDeprecated, + wPragma, wEmit, wUnroll, + wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto, + wExperimental, wDoctype, wThis, wUsed, wInvariant, wAssume, wAssert} + stmtPragmasTopLevel* = {wChecks, wObjChecks, wFieldChecks, wRangeChecks, + wBoundChecks, wOverflowChecks, wNilChecks, wStaticBoundchecks, + wStyleChecks, wAssertions, + wWarnings, wHints, + wLineDir, wStackTrace, wLineTrace, wOptimization, + wFloatChecks, wInfChecks, wNanChecks} + lambdaPragmas* = {FirstCallConv..LastCallConv, + wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader, + wThread, wAsmNoStackFrame, + wRaises, wLocks, wTags, wForbids, wRequires, wEnsures, wEffectsOf, + wGcSafe, wCodegenDecl, wNoInit, wCompileTime} + typePragmas* = declPragmas + {wMagic, wAcyclic, + wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wShallow, + wIncompleteStruct, wCompleteStruct, wByCopy, wByRef, wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, - wBorrow, wGcSafe, wExportNims, wPartial, wUsed, wExplain} - fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern, - wImportCpp, wImportObjC, wError, wGuard, wBitsize, wUsed} - varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl, - wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern, - wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal, - wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims, wUsed} - constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl, - wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims, - wIntDefine, wStrDefine, wUsed} + wCppNonPod, wBorrow, wGcSafe, wPartial, wExplain, wPackage, wCodegenDecl, + wSendable, wNoInit} + fieldPragmas* = declPragmas + {wGuard, wBitsize, wCursor, + wRequiresInit, wNoalias, wAlign, wNoInit} - {wExportNims, wNodecl} # why exclude these? + varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar, + wMagic, wHeader, wCompilerProc, wCore, wDynlib, + 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, wExportc, wExportCpp} letPragmas* = varPragmas - procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect, - wThread, wRaises, wLocks, wTags, wGcSafe} + procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect, + wThread, wRaises, wEffectsOf, wLocks, wTags, wForbids, wGcSafe, + wRequires, wEnsures} + forVarPragmas* = {wInject, wGensym} allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas + 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: + if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent and + it[0].ident.id == ord(name): + return it[1] + +proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords; + isStatement: bool = false) + +proc recordPragma(c: PContext; n: PNode; args: varargs[string]) = + var recorded = newNodeI(nkReplayAction, n.info) + for i in 0..args.high: + recorded.add newStrNode(args[i], n.info) + addPragmaComputation(c, recorded) -proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) -# implementation - -proc invalidPragma*(n: PNode) = - localError(n.info, errInvalidPragmaX, renderTree(n, {renderNoComments})) - -proc pragmaAsm*(c: PContext, n: PNode): char = - result = '\0' - if n != nil: - for i in countup(0, sonsLen(n) - 1): - let it = n.sons[i] - if it.kind == nkExprColonExpr and it.sons[0].kind == nkIdent: - case whichKeyword(it.sons[0].ident) - of wSubsChar: - if it.sons[1].kind == nkCharLit: result = chr(int(it.sons[1].intVal)) - else: invalidPragma(it) - else: invalidPragma(it) - else: - invalidPragma(it) +const + errStringLiteralExpected = "string literal expected" + errIntLiteralExpected = "integer literal expected" + +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: + 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: + localError(c.config, n.info, "proposition expected") + else: + n[1] = c.semExpr(c, n[1]) -proc setExternName(s: PSym, extname: string, info: TLineInfo) = +proc pragmaEnsures(c: PContext, n: PNode) = + if n.kind notin nkPragmaCallKinds or n.len != 2: + localError(c.config, n.info, "proposition expected") + else: + openScope(c) + let o = getCurrOwner(c) + 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 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(info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)") - if gCmd == cmdPretty and '$' notin extname: - # note that '{.importc.}' is transformed into '{.importc: "$1".}' - s.loc.flags.incl(lfFullExternalName) + localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)") + when hasFFI: + s.cname = $s.loc.snippet + -proc makeExternImport(s: PSym, extname: string, info: TLineInfo) = - setExternName(s, extname, info) +proc makeExternImport(c: PContext; s: PSym, extname: string, info: TLineInfo) = + setExternName(c, s, extname, info) incl(s.flags, sfImportc) excl(s.flags, sfForward) -proc makeExternExport(s: PSym, extname: string, info: TLineInfo) = - setExternName(s, extname, info) +proc makeExternExport(c: PContext; s: PSym, extname: string, info: TLineInfo) = + setExternName(c, s, extname, info) incl(s.flags, sfExportc) -proc processImportCompilerProc(s: PSym, extname: string, info: TLineInfo) = - setExternName(s, extname, info) +proc processImportCompilerProc(c: PContext; s: PSym, extname: string, info: TLineInfo) = + setExternName(c, s, extname, info) incl(s.flags, sfImportc) excl(s.flags, sfForward) incl(s.loc.flags, lfImportCompilerProc) -proc processImportCpp(s: PSym, extname: string, info: TLineInfo) = - setExternName(s, extname, info) +proc processImportCpp(c: PContext; s: PSym, extname: string, info: TLineInfo) = + setExternName(c, s, extname, info) incl(s.flags, sfImportc) incl(s.flags, sfInfixCall) excl(s.flags, sfForward) - if gCmd == cmdCompileToC: + if c.config.backend == backendC: let m = s.getModule() incl(m.flags, sfCompileToCpp) - extccomp.gMixedMode = true + incl c.config.globalOptions, optMixedMode -proc processImportObjC(s: PSym, extname: string, info: TLineInfo) = - setExternName(s, extname, info) +proc processImportObjC(c: PContext; s: PSym, extname: string, info: TLineInfo) = + setExternName(c, s, extname, info) incl(s.flags, sfImportc) incl(s.flags, sfNamedParamCall) excl(s.flags, sfForward) let m = s.getModule() - incl(m.flags, sfCompileToObjC) + incl(m.flags, sfCompileToObjc) -proc newEmptyStrNode(n: PNode): PNode {.noinline.} = - result = newNodeIT(nkStrLit, n.info, getSysType(tyString)) - result.strVal = "" +proc newEmptyStrNode(c: PContext; n: PNode, strVal: string = ""): PNode {.noinline.} = + result = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString)) + result.strVal = strVal proc getStrLitNode(c: PContext, n: PNode): PNode = - if n.kind != nkExprColonExpr: - localError(n.info, errStringLiteralExpected) + if n.kind notin nkPragmaCallKinds or n.len != 2: + localError(c.config, n.info, errStringLiteralExpected) # error correction: - result = newEmptyStrNode(n) + result = newEmptyStrNode(c, n) else: - n.sons[1] = c.semConstExpr(c, n.sons[1]) - case n.sons[1].kind - of nkStrLit, nkRStrLit, nkTripleStrLit: result = n.sons[1] + n[1] = c.semConstExpr(c, n[1]) + case n[1].kind + of nkStrLit, nkRStrLit, nkTripleStrLit: result = n[1] else: - localError(n.info, errStringLiteralExpected) + localError(c.config, n.info, errStringLiteralExpected) # error correction: - result = newEmptyStrNode(n) + result = newEmptyStrNode(c, n) proc expectStrLit(c: PContext, n: PNode): string = result = getStrLitNode(c, n).strVal proc expectIntLit(c: PContext, n: PNode): int = - if n.kind != nkExprColonExpr: - localError(n.info, errIntLiteralExpected) + result = 0 + if n.kind notin nkPragmaCallKinds or n.len != 2: + localError(c.config, n.info, errIntLiteralExpected) else: - n.sons[1] = c.semConstExpr(c, n.sons[1]) - case n.sons[1].kind - of nkIntLit..nkInt64Lit: result = int(n.sons[1].intVal) - else: localError(n.info, errIntLiteralExpected) + n[1] = c.semConstExpr(c, n[1]) + case n[1].kind + of nkIntLit..nkInt64Lit: result = int(n[1].intVal) + else: localError(c.config, n.info, errIntLiteralExpected) proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string = - if n.kind == nkExprColonExpr: result = expectStrLit(c, n) + 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: # liMessage(n.info, errMagicOnlyInSystem) - if n.kind != nkExprColonExpr: - localError(n.info, errStringLiteralExpected) + if n.kind notin nkPragmaCallKinds or n.len != 2: + localError(c.config, n.info, errStringLiteralExpected) return var v: string - if n.sons[1].kind == nkIdent: v = n.sons[1].ident.s + if n[1].kind == nkIdent: v = n[1].ident.s else: v = expectStrLit(c, n) - for m in countup(low(TMagic), high(TMagic)): + for m in TMagic: if substr($m, 1) == v: s.magic = m break - if s.magic == mNone: message(n.info, warnUnknownMagic, v) + if s.magic == mNone: message(c.config, n.info, warnUnknownMagic, v) proc wordToCallConv(sw: TSpecialWord): TCallingConvention = # this assumes that the order of special words and calling conventions is # the same - result = TCallingConvention(ord(ccDefault) + ord(sw) - ord(wNimcall)) + TCallingConvention(ord(ccNimCall) + ord(sw) - ord(wNimcall)) proc isTurnedOn(c: PContext, n: PNode): bool = - if n.kind == nkExprColonExpr: - let x = c.semConstBoolExpr(c, n.sons[1]) - n.sons[1] = x + result = false + if n.kind in nkPragmaCallKinds and n.len == 2: + let x = c.semConstBoolExpr(c, n[1]) + n[1] = x if x.kind == nkIntLit: return x.intVal != 0 - localError(n.info, errOnOrOffExpected) + localError(c.config, n.info, "'on' or 'off' expected") + +proc onOff(c: PContext, n: PNode, op: TOptions, resOptions: var TOptions) = + if isTurnedOn(c, n): resOptions.incl op + else: resOptions.excl op -proc onOff(c: PContext, n: PNode, op: TOptions) = - if isTurnedOn(c, n): gOptions = gOptions + op - else: gOptions = gOptions - op +proc pragmaNoForward*(c: PContext, n: PNode; flag=sfNoForward) = + if isTurnedOn(c, n): + incl(c.module.flags, flag) + c.features.incl codeReordering + else: + excl(c.module.flags, flag) + # c.features.excl codeReordering -proc pragmaDeadCodeElim(c: PContext, n: PNode) = - if isTurnedOn(c, n): incl(c.module.flags, sfDeadCodeElim) - else: excl(c.module.flags, sfDeadCodeElim) + # deprecated as of 0.18.1 + message(c.config, n.info, warnDeprecated, + "use {.experimental: \"codeReordering\".} instead; " & + (if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}") & " is deprecated") -proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) = - if isTurnedOn(c, n): incl(c.module.flags, flag) - else: excl(c.module.flags, flag) +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 == nkExprColonExpr) and (n.sons[1].kind == nkIdent): - var sw = whichKeyword(n.sons[1].ident) + if n.kind in nkPragmaCallKinds and n.len == 2 and n[1].kind == nkIdent: + let sw = whichKeyword(n[1].ident) case sw of FirstCallConv..LastCallConv: c.optionStack[^1].defaultCC = wordToCallConv(sw) - else: localError(n.info, errCallConvExpected) + else: localError(c.config, n.info, "calling convention expected") else: - localError(n.info, errCallConvExpected) + localError(c.config, n.info, "calling convention expected") proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib = for it in c.libs: @@ -233,32 +330,32 @@ 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(path.strVal) + result.isOverridden = options.isDynlibOverride(c.config, path.strVal) proc expectDynlibNode(c: PContext, n: PNode): PNode = - if n.kind != nkExprColonExpr: - localError(n.info, errStringLiteralExpected) + if n.kind notin nkPragmaCallKinds or n.len != 2: + localError(c.config, n.info, errStringLiteralExpected) # error correction: - result = newEmptyStrNode(n) + result = newEmptyStrNode(c, n) else: # For the OpenGL wrapper we support: # {.dynlib: myGetProcAddr(...).} - result = c.semExpr(c, n.sons[1]) + result = c.semExpr(c, n[1]) if result.kind == nkSym and result.sym.kind == skConst: - result = result.sym.ast # look it up + result = c.semConstExpr(c, result) # fold const if result.typ == nil or result.typ.kind notin {tyPointer, tyString, tyProc}: - localError(n.info, errStringLiteralExpected) - result = newEmptyStrNode(n) + localError(c.config, n.info, errStringLiteralExpected) + result = newEmptyStrNode(c, n) 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 == nkExprColonExpr: + 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: @@ -267,370 +364,423 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) = # a calling convention that doesn't introduce custom name mangling # cdecl is the default - the user can override this explicitly if sym.kind in routineKinds and sym.typ != nil and - sym.typ.callConv == ccDefault: + tfExplicitCallConv notin sym.typ.flags: sym.typ.callConv = ccCDecl proc processNote(c: PContext, n: PNode) = - if (n.kind == nkExprColonExpr) and (sonsLen(n) == 2) and - (n.sons[0].kind == nkBracketExpr) and - (n.sons[0].sons.len == 2) and - (n.sons[0].sons[1].kind == nkIdent) and - (n.sons[0].sons[0].kind == nkIdent): - #and (n.sons[1].kind == nkIdent): - var nk: TNoteKind - case whichKeyword(n.sons[0].sons[0].ident) - of wHint: - var x = findStr(msgs.HintsToStr, n.sons[0].sons[1].ident.s) - if x >= 0: nk = TNoteKind(x + ord(hintMin)) - else: invalidPragma(n); return - of wWarning: - var x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s) - if x >= 0: nk = TNoteKind(x + ord(warnMin)) - else: invalidPragma(n); return + template handleNote(enumVals, notes) = + let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown) + if x != errUnknown: + nk = TNoteKind(x) + let x = c.semConstBoolExpr(c, n[1]) + n[1] = x + if x.kind == nkIntLit and x.intVal != 0: incl(notes, nk) + else: excl(notes, nk) else: - invalidPragma(n) - return + invalidPragma(c, n) - let x = c.semConstBoolExpr(c, n.sons[1]) - n.sons[1] = x - if x.kind == nkIntLit and x.intVal != 0: incl(gNotes, nk) - else: excl(gNotes, nk) + if n.kind in nkPragmaCallKinds and n.len == 2 and + n[0].kind == nkBracketExpr and + n[0].len == 2 and + n[0][1].kind == nkIdent and n[0][0].kind == nkIdent: + var nk: TNoteKind + case whichKeyword(n[0][0].ident) + of wHint: handleNote(hintMin .. hintMax, c.config.notes) + of wWarning: handleNote(warnMin .. warnMax, c.config.notes) + of wWarningAsError: handleNote(warnMin .. warnMax, c.config.warningAsErrors) + of wHintAsError: handleNote(hintMin .. hintMax, c.config.warningAsErrors) + else: invalidPragma(c, n) + else: invalidPragma(c, n) + +proc pragmaToOptions*(w: TSpecialWord): TOptions {.inline.} = + case w + of wChecks: ChecksOptions + of wObjChecks: {optObjCheck} + of wFieldChecks: {optFieldCheck} + of wRangeChecks: {optRangeCheck} + of wBoundChecks: {optBoundsCheck} + of wOverflowChecks: {optOverflowCheck} + of wFloatChecks: {optNaNCheck, optInfCheck} + of wNanChecks: {optNaNCheck} + of wInfChecks: {optInfCheck} + of wStaticBoundchecks: {optStaticBoundsCheck} + of wStyleChecks: {optStyleCheck} + of wAssertions: {optAssert} + of wWarnings: {optWarns} + of wHints: {optHints} + of wLineDir: {optLineDir} + of wStackTrace: {optStackTrace} + of wLineTrace: {optLineTrace} + of wDebugger: {optNone} + of wProfiler: {optProfiler, optMemTracker} + of wMemTracker: {optMemTracker} + of wByRef: {optByRef} + of wImplicitStatic: {optImplicitStatic} + of wPatterns, wTrMacros: {optTrMacros} + of wSinkInference: {optSinkInference} + of wQuirky: {optQuirky} + else: {} + +proc processExperimental(c: PContext; n: PNode) = + if n.kind notin nkPragmaCallKinds or n.len != 2: + c.features.incl oldExperimentalFeatures else: - invalidPragma(n) + n[1] = c.semConstExpr(c, n[1]) + case n[1].kind + of nkStrLit, nkRStrLit, nkTripleStrLit: + try: + let feature = parseEnum[Feature](n[1].strVal) + c.features.incl feature + if feature == codeReordering: + if not isTopLevel(c): + localError(c.config, n.info, + "Code reordering experimental pragma only valid at toplevel") + c.module.flags.incl sfReorder + except ValueError: + localError(c.config, n[1].info, "unknown experimental feature") + else: + localError(c.config, n.info, errStringLiteralExpected) -proc processOption(c: PContext, n: PNode): bool = - if n.kind != nkExprColonExpr: result = true - elif n.sons[0].kind == nkBracketExpr: processNote(c, n) - elif n.sons[0].kind != nkIdent: result = true +proc tryProcessOption(c: PContext, n: PNode, resOptions: var TOptions): bool = + result = true + if n.kind notin nkPragmaCallKinds or n.len != 2: result = false + elif n[0].kind == nkBracketExpr: processNote(c, n) + elif n[0].kind != nkIdent: result = false else: - var sw = whichKeyword(n.sons[0].ident) - case sw - of wChecks: onOff(c, n, ChecksOptions) - of wObjChecks: onOff(c, n, {optObjCheck}) - of wFieldChecks: onOff(c, n, {optFieldCheck}) - of wRangechecks: onOff(c, n, {optRangeCheck}) - of wBoundchecks: onOff(c, n, {optBoundsCheck}) - of wOverflowchecks: onOff(c, n, {optOverflowCheck}) - of wNilchecks: onOff(c, n, {optNilCheck}) - of wFloatchecks: onOff(c, n, {optNaNCheck, optInfCheck}) - of wNanChecks: onOff(c, n, {optNaNCheck}) - of wInfChecks: onOff(c, n, {optInfCheck}) - of wAssertions: onOff(c, n, {optAssert}) - of wWarnings: onOff(c, n, {optWarns}) - of wHints: onOff(c, n, {optHints}) - of wCallconv: processCallConv(c, n) - of wLinedir: onOff(c, n, {optLineDir}) - of wStacktrace: onOff(c, n, {optStackTrace}) - of wLinetrace: onOff(c, n, {optLineTrace}) - of wDebugger: onOff(c, n, {optEndb}) - of wProfiler: onOff(c, n, {optProfiler, optMemTracker}) - of wMemTracker: onOff(c, n, {optMemTracker}) - of wByRef: onOff(c, n, {optByRef}) - of wDynlib: processDynLib(c, n, nil) - of wOptimization: - if n.sons[1].kind != nkIdent: - invalidPragma(n) - else: - case n.sons[1].ident.s.normalize - of "speed": - incl(gOptions, optOptimizeSpeed) - excl(gOptions, optOptimizeSize) - of "size": - excl(gOptions, optOptimizeSpeed) - incl(gOptions, optOptimizeSize) - of "none": - excl(gOptions, optOptimizeSpeed) - excl(gOptions, optOptimizeSize) - else: localError(n.info, errNoneSpeedOrSizeExpected) - of wImplicitStatic: onOff(c, n, {optImplicitStatic}) - of wPatterns: onOff(c, n, {optPatterns}) - else: result = true + let sw = whichKeyword(n[0].ident) + if sw == wExperimental: + processExperimental(c, n) + return true + let opts = pragmaToOptions(sw) + if opts != {}: + onOff(c, n, opts, resOptions) + else: + case sw + of wCallconv: processCallConv(c, n) + of wDynlib: processDynLib(c, n, nil) + of wOptimization: + if n[1].kind != nkIdent: + invalidPragma(c, n) + else: + case n[1].ident.s.normalize + of "speed": + incl(resOptions, optOptimizeSpeed) + excl(resOptions, optOptimizeSize) + of "size": + excl(resOptions, optOptimizeSpeed) + incl(resOptions, optOptimizeSize) + of "none": + excl(resOptions, optOptimizeSpeed) + excl(resOptions, optOptimizeSize) + else: localError(c.config, n.info, "'none', 'speed' or 'size' expected") + else: result = false + +proc processOption(c: PContext, n: PNode, resOptions: var TOptions) = + if not tryProcessOption(c, n, resOptions): + # 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.sons[start-1].kind == nkExprColonExpr: - localError(n.info, errGenerated, "':' after 'push' not supported") - var x = newOptionEntry() - var y = c.optionStack[^1] - x.options = gOptions - x.defaultCC = y.defaultCC - x.dynlib = y.dynlib - x.notes = gNotes - c.optionStack.add(x) - for i in countup(start, sonsLen(n) - 1): - if processOption(c, n.sons[i]): + if n[start-1].kind in nkPragmaCallKinds: + localError(c.config, n.info, "'push' cannot have arguments") + var x = pushOptionEntry(c) + 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.sons[i] - #localError(n.info, errOptionExpected) + x.otherPragmas.add n[i] + #localError(c.config, n.info, errOptionExpected) + + # If stacktrace is disabled globally we should not enable it + if optStackTrace notin c.optionStack[0].options: + c.config.options.excl(optStackTrace) + when defined(debugOptions): + echo c.config $ n.info, " PUSH config is now ", c.config.options proc processPop(c: PContext, n: PNode) = if c.optionStack.len <= 1: - localError(n.info, errAtPopWithoutPush) + localError(c.config, n.info, "{.pop.} without a corresponding {.push.}") else: - gOptions = c.optionStack[^1].options - gNotes = c.optionStack[^1].notes - c.optionStack.setLen(c.optionStack.len - 1) - -proc processDefine(c: PContext, n: PNode) = - if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent): - defineSymbol(n.sons[1].ident.s) - message(n.info, warnDeprecated, "define") + popOptionEntry(c) + when defined(debugOptions): + echo c.config $ n.info, " POP config is now ", c.config.options + +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(n) + invalidPragma(c, n) proc processUndef(c: PContext, n: PNode) = - if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent): - undefSymbol(n.sons[1].ident.s) - message(n.info, warnDeprecated, "undef") + if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent): + undefSymbol(c.config.symbols, n[1].ident.s) else: - invalidPragma(n) - -type - TLinkFeature = enum - linkNormal, linkSys + invalidPragma(c, n) -proc relativeFile(c: PContext; n: PNode; ext=""): string = +proc relativeFile(c: PContext; n: PNode; ext=""): AbsoluteFile = var s = expectStrLit(c, n) if ext.len > 0 and splitFile(s).ext == "": s = addFileExt(s, ext) - result = parentDir(n.info.toFullPath) / s + result = AbsoluteFile parentDir(toFullPath(c.config, n.info)) / s if not fileExists(result): - if isAbsolute(s): result = s + if isAbsolute(s): result = AbsoluteFile s else: - result = findFile(s) - if result.len == 0: result = s + result = findFile(c.config, s) + 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) + 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.sons[i] = c.semConstExpr(c, n.sons[i]) - case n.sons[i].kind + n[i] = c.semConstExpr(c, n[i]) + case n[i].kind of nkStrLit, nkRStrLit, nkTripleStrLit: - shallowCopy(result, n.sons[i].strVal) + when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc): + result = n[i].strVal + else: + shallowCopy(result, n[i].strVal) else: - localError(n.info, errStringLiteralExpected) + localError(c.config, n.info, errStringLiteralExpected) result = "" - let it = if n.kind == nkExprColonExpr: n.sons[1] else: n - if it.kind == nkPar and it.len == 2: + let it = if n.kind in nkPragmaCallKinds and n.len == 2: n[1] else: n + if it.kind in {nkPar, nkTupleConstr} and it.len == 2: let s = getStrLit(c, it, 0) let dest = getStrLit(c, it, 1) - var found = parentDir(n.info.toFullPath) / s + var found = parentDir(toFullPath(c.config, n.info)) / s for f in os.walkFiles(found): - let nameOnly = extractFilename(f) - var cf = Cfile(cname: f, - obj: completeCFilePath(dest % nameOnly), - flags: {CfileFlag.External}) - extccomp.addExternalFileToCompile(cf) + let obj = completeCfilePath(c.config, AbsoluteFile(dest % extractFilename(f))) + docompile(c, it, AbsoluteFile f, obj, "") else: - let s = expectStrLit(c, n) - var found = parentDir(n.info.toFullPath) / s + var s = "" + var customArgs = "" + if n.kind in nkCallKinds: + s = getStrLit(c, n, 1) + if n.len <= 3: + customArgs = getStrLit(c, n, 2) + else: + localError(c.config, n.info, "'.compile' pragma takes up 2 arguments") + else: + s = expectStrLit(c, n) + + var found = AbsoluteFile(parentDir(toFullPath(c.config, n.info)) / s) if not fileExists(found): - if isAbsolute(s): found = s + if isAbsolute(s): found = AbsoluteFile s else: - found = findFile(s) - if found.len == 0: found = s - extccomp.addExternalFileToCompile(found) - -proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) = - let found = relativeFile(c, n, CC[cCompiler].objExt) - case feature - of linkNormal: extccomp.addExternalFileToLink(found) - of linkSys: - extccomp.addExternalFileToLink(libpath / completeCFilePath(found, false)) - else: internalError(n.info, "processCommonLink") - -proc pragmaBreakpoint(c: PContext, n: PNode) = - discard getOptionalStr(c, n, "") - -proc pragmaWatchpoint(c: PContext, n: PNode) = - if n.kind == nkExprColonExpr: - n.sons[1] = c.semExpr(c, n.sons[1]) - else: - invalidPragma(n) + found = findFile(c.config, s) + if found.isEmpty: found = AbsoluteFile s + 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) = + let found = relativeFile(c, n, CC[c.config.cCompiler].objExt) + extccomp.addExternalFileToLink(c.config, found) + recordPragma(c, n, "link", found.string) proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode = - case n.sons[1].kind + case n[1].kind of nkStrLit, nkRStrLit, nkTripleStrLit: - result = newNode(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info) - var str = n.sons[1].strVal + 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(n.info, errEmptyAsm) + localError(con.config, n.info, "empty 'asm' statement") return # now parse the string literal and substitute symbols: var a = 0 while true: var b = strutils.find(str, marker, a) var sub = if b < 0: substr(str, a) else: substr(str, a, b - 1) - if sub != "": addSon(result, newStrNode(nkStrLit, sub)) + if sub != "": result.add newStrNode(nkStrLit, sub) if b < 0: break var c = strutils.find(str, marker, b + 1) if c < 0: sub = substr(str, b + 1) else: sub = substr(str, b + 1, c - 1) if sub != "": - var e = searchInScopes(con, getIdent(sub)) + var amb = false + var e = searchInScopes(con, getIdent(con.cache, sub), amb) + # XXX what to do here if 'amb' is true? if e != nil: - if e.kind == skStub: loadStub(e) incl(e.flags, sfUsed) - addSon(result, newSymNode(e)) + result.add newSymNode(e) else: - addSon(result, newStrNode(nkStrLit, sub)) + result.add newStrNode(nkStrLit, sub) else: # an empty '``' produces a single '`' - addSon(result, newStrNode(nkStrLit, $marker)) + result.add newStrNode(nkStrLit, $marker) if c < 0: break a = c + 1 else: - illFormedAstLocal(n) - result = newNode(nkAsmStmt, n.info) + 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 != nkExprColonExpr: - localError(n.info, errStringLiteralExpected) + if n.kind notin nkPragmaCallKinds or n.len != 2: + localError(c.config, n.info, errStringLiteralExpected) else: let n1 = n[1] if n1.kind == nkBracket: var b = newNodeI(nkBracket, n1.info, n1.len) for i in 0..<n1.len: - b.sons[i] = c.semExpr(c, n1[i]) - n.sons[1] = b + b[i] = c.semExprWithType(c, n1[i], {efTypeAllowed}) + n[1] = b else: - n.sons[1] = c.semConstExpr(c, n1) - case n.sons[1].kind + n[1] = c.semConstExpr(c, n1) + case n[1].kind of nkStrLit, nkRStrLit, nkTripleStrLit: - n.sons[1] = semAsmOrEmit(c, n, '`') + n[1] = semAsmOrEmit(c, n, '`') else: - localError(n.info, errStringLiteralExpected) + localError(c.config, n.info, errStringLiteralExpected) -proc noVal(n: PNode) = - if n.kind == nkExprColonExpr: invalidPragma(n) +proc noVal(c: PContext; n: PNode) = + if n.kind in nkPragmaCallKinds and n.len > 1: invalidPragma(c, n) proc pragmaUnroll(c: PContext, n: PNode) = if c.p.nestedLoopCounter <= 0: - invalidPragma(n) - elif n.kind == nkExprColonExpr: + invalidPragma(c, n) + elif n.kind in nkPragmaCallKinds and n.len == 2: var unrollFactor = expectIntLit(c, n) if unrollFactor <% 32: - n.sons[1] = newIntNode(nkIntLit, unrollFactor) + n[1] = newIntNode(nkIntLit, unrollFactor) else: - invalidPragma(n) + invalidPragma(c, n) proc pragmaLine(c: PContext, n: PNode) = - if n.kind == nkExprColonExpr: - n.sons[1] = c.semConstExpr(c, n.sons[1]) - let a = n.sons[1] - if a.kind == nkPar: - var x = a.sons[0] - var y = a.sons[1] - if x.kind == nkExprColonExpr: x = x.sons[1] - if y.kind == nkExprColonExpr: y = y.sons[1] + if n.kind in nkPragmaCallKinds and n.len == 2: + n[1] = c.semConstExpr(c, n[1]) + let a = n[1] + if a.kind in {nkPar, nkTupleConstr}: + # unpack the tuple + var x = a[0] + var y = a[1] + if x.kind == nkExprColonExpr: x = x[1] + if y.kind == nkExprColonExpr: y = y[1] if x.kind != nkStrLit: - localError(n.info, errStringLiteralExpected) + localError(c.config, n.info, errStringLiteralExpected) elif y.kind != nkIntLit: - localError(n.info, errIntLiteralExpected) + localError(c.config, n.info, errIntLiteralExpected) else: - # XXX this produces weird paths which are not properly resolved: - n.info.fileIndex = msgs.fileInfoIdx(x.strVal) - n.info.line = int16(y.intVal) + n.info.fileIndex = fileInfoIdx(c.config, AbsoluteFile(x.strVal)) + n.info.line = uint16(y.intVal) else: - localError(n.info, errXExpected, "tuple") + localError(c.config, n.info, "tuple expected") else: # sensible default: - n.info = getInfoContext(-1) + n.info = getInfoContext(c.config, -1) proc processPragma(c: PContext, n: PNode, i: int) = - var it = n.sons[i] - if it.kind != nkExprColonExpr: invalidPragma(n) - elif it.sons[0].kind != nkIdent: invalidPragma(n) - elif it.sons[1].kind != nkIdent: invalidPragma(n) - - var userPragma = newSym(skTemplate, it.sons[1].ident, nil, it.info) - var body = newNodeI(nkPragma, n.info) - for j in i+1 .. sonsLen(n)-1: addSon(body, n.sons[j]) - userPragma.ast = body + ## 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) + 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) + userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1]) strTableAdd(c.userPragmas, userPragma) proc pragmaRaisesOrTags(c: PContext, n: PNode) = proc processExc(c: PContext, x: PNode) = - var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs) - if t.kind != tyObject: - localError(x.info, errGenerated, "invalid type for raises/tags list") - x.typ = t + if c.hasUnresolvedArgs(c, x): + x.typ = makeTypeFromExpr(c, x) + else: + var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs) + if t.kind notin {tyObject, tyOr}: + localError(c.config, x.info, errGenerated, "invalid type for raises/tags list") + x.typ = t - if n.kind == nkExprColonExpr: - let it = n.sons[1] + if n.kind in nkPragmaCallKinds and n.len == 2: + let it = n[1] if it.kind notin {nkCurly, nkBracket}: processExc(c, it) else: for e in items(it): processExc(c, e) else: - invalidPragma(n) + invalidPragma(c, n) proc pragmaLockStmt(c: PContext; it: PNode) = - if it.kind != nkExprColonExpr: - invalidPragma(it) + if it.kind notin nkPragmaCallKinds or it.len != 2: + invalidPragma(c, it) else: let n = it[1] if n.kind != nkBracket: - localError(n.info, errGenerated, "locks pragma takes a list of expressions") - else: - for i in 0 .. <n.len: - n.sons[i] = c.semExpr(c, n.sons[i]) - -proc pragmaLocks(c: PContext, it: PNode): TLockLevel = - if it.kind != nkExprColonExpr: - invalidPragma(it) - else: - case it[1].kind - of nkStrLit, nkRStrLit, nkTripleStrLit: - if it[1].strVal == "unknown": - result = UnknownLockLevel - else: - localError(it[1].info, "invalid string literal for locks pragma (only allowed string is \"unknown\")") + localError(c.config, n.info, errGenerated, "locks pragma takes a list of expressions") else: - let x = expectIntLit(c, it) - if x < 0 or x > MaxLockLevel: - localError(it[1].info, "integer must be within 0.." & $MaxLockLevel) - else: - result = TLockLevel(x) + for i in 0..<n.len: + n[i] = c.semExpr(c, n[i]) -proc typeBorrow(sym: PSym, n: PNode) = - if n.kind == nkExprColonExpr: - let it = n.sons[1] +proc typeBorrow(c: PContext; sym: PSym, n: PNode) = + if n.kind in nkPragmaCallKinds and n.len == 2: + let it = n[1] if it.kind != nkAccQuoted: - localError(n.info, "a type can only borrow `.` for now") + localError(c.config, n.info, "a type can only borrow `.` for now") incl(sym.typ.flags, tfBorrowDot) -proc markCompilerProc(s: PSym) = - # minor hack ahead: FlowVar is the only generic .compilerProc type which +proc markCompilerProc(c: PContext; s: PSym) = + # minor hack ahead: FlowVar is the only generic .compilerproc type which # should not have an external name set: if s.kind != skType or s.name.s != "FlowVar": - makeExternExport(s, "$1", s.info) + makeExternExport(c, s, "$1", s.info) incl(s.flags, sfCompilerProc) incl(s.flags, sfUsed) - registerCompilerProc(s) - -proc deprecatedStmt(c: PContext; pragma: PNode) = - let pragma = pragma[1] + registerCompilerProc(c.graph, s) + if c.config.symbolFiles != disabledSf: + addCompilerProc(c.encoder, c.packedRepr, s) + +proc deprecatedStmt(c: PContext; outerPragma: PNode) = + let pragma = outerPragma[1] + if pragma.kind in {nkStrLit..nkTripleStrLit}: + incl(c.module.flags, sfDeprecated) + c.module.constraint = getStrLitNode(c, outerPragma) + return if pragma.kind != nkBracket: - localError(pragma.info, "list of key:value pairs expected"); return - for n in pragma: - if n.kind in {nkExprColonExpr, nkExprEqExpr}: - let dest = qualifiedLookUp(c, n[1], {checkUndeclared}) - assert dest != nil - let src = considerQuotedIdent(n[0]) - let alias = newSym(skAlias, src, dest, n[0].info) - incl(alias.flags, sfExported) - if sfCompilerProc in dest.flags: markCompilerProc(alias) - addInterfaceDecl(c, alias) - n.sons[1] = newSymNode(dest) - else: - localError(n.info, "key:value pair expected") + localError(c.config, pragma.info, "list of key:value pairs expected"); return + 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 != nkExprColonExpr: - invalidPragma(it); return + if it.kind notin nkPragmaCallKinds or it.len != 2: + invalidPragma(c, it); return let n = it[1] if n.kind == nkSym: result = n.sym @@ -642,106 +792,222 @@ 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(n), nil, n.info) + result = newSym(skUnknown, considerQuotedIdent(c, n), c.idgen, nil, n.info, + c.config.options) else: result = qualifiedLookUp(c, n, {checkUndeclared}) -proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, - validPragmas: TSpecialWords): bool = - var it = n.sons[i] - var key = if it.kind == nkExprColonExpr: it.sons[0] else: it +proc semCustomPragma(c: PContext, n: PNode, sym: PSym): PNode = + var callNode: PNode + + case n.kind + of nkIdentKinds: + # pragma -> pragma() + callNode = newTree(nkCall, n) + of nkExprColonExpr: + # pragma: arg -> pragma(arg) + callNode = newTree(nkCall, n[0], n[1]) + 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: + # pragma() -> pragma + result = result[0] + elif n.kind == nkExprColonExpr and r.len == 2: + # pragma(arg) -> pragma: arg + result.transitionSonsKind(n.kind) + +proc processEffectsOf(c: PContext, n: PNode; owner: PSym) = + proc processParam(c: PContext; n: PNode) = + let r = c.semExpr(c, n) + if r.kind == nkSym and r.sym.kind == skParam: + if r.sym.owner == owner: + incl r.sym.flags, sfEffectsDelayed + else: + localError(c.config, n.info, errGenerated, "parameter cannot be declared as .effectsOf") + else: + localError(c.config, n.info, errGenerated, "parameter name expected") + + if n.kind notin nkPragmaCallKinds or n.len != 2: + localError(c.config, n.info, errGenerated, "parameter name expected") + else: + let it = n[1] + if it.kind in {nkCurly, nkBracket}: + for x in items(it): processParam(c, x) + else: + processParam(c, it) + +proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, + validPragmas: TSpecialWords, + comesFromPush, isStatement: bool): bool = + result = false + var it = n[i] + 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 - let ident = considerQuotedIdent(key) + elif key.kind == nkCast: + if comesFromPush: + localError(c.config, n.info, "a 'cast' pragma cannot be pushed") + elif not isStatement: + localError(c.config, n.info, "'cast' pragma only allowed in a statement context") + case whichPragma(key[1]) + of wRaises, wTags, wForbids: pragmaRaisesOrTags(c, key[1]) + else: discard + return + elif key.kind notin nkIdentKinds: + n[i] = semCustomPragma(c, it, sym) + return + let ident = considerQuotedIdent(c, key) var userPragma = strTableGet(c.userPragmas, ident) if userPragma != nil: + styleCheckUse(c, key.info, userPragma) + + # number of pragmas increase/decrease with user pragma expansion inc c.instCounter + defer: dec c.instCounter if c.instCounter > 100: - globalError(it.info, errRecursiveDependencyX, userPragma.name.s) - pragma(c, sym, userPragma.ast, validPragmas) - # ensure the pragma is also remember for generic instantiations in other - # modules: - n.sons[i] = userPragma.ast - dec c.instCounter + 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 else: - var k = whichKeyword(ident) + let k = whichKeyword(ident) if k in validPragmas: + checkPragmaUse(c, key.info, k, ident.s, (if sym != nil: sym else: c.module)) case k - of wExportc: - makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info) + of wExportc, wExportCpp: + makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info) + if k == wExportCpp: + if c.config.backend != backendCpp: + localError(c.config, it.info, "exportcpp requires `cpp` backend, got: " & $c.config.backend) + else: + incl(sym.flags, sfMangleCpp) incl(sym.flags, sfUsed) # avoid wrong hints of wImportc: let name = getOptionalStr(c, it, "$1") - cppDefine(c.graph.config, name) - makeExternImport(sym, name, it.info) + cppDefine(c.config, name) + recordPragma(c, it, "cppdefine", name) + makeExternImport(c, sym, name, it.info) of wImportCompilerProc: let name = getOptionalStr(c, it, "$1") - cppDefine(c.graph.config, name) - processImportCompilerProc(sym, name, it.info) - of wExtern: setExternName(sym, expectStrLit(c, it), it.info) - of wImmediate: - if sym.kind in {skTemplate, skMacro}: - incl(sym.flags, sfImmediate) - incl(sym.flags, sfAllUntyped) - message(n.info, warnDeprecated, "use 'untyped' parameters instead; immediate") - else: invalidPragma(it) + cppDefine(c.config, name) + recordPragma(c, it, "cppdefine", name) + processImportCompilerProc(c, sym, name, it.info) + of wExtern: setExternName(c, sym, expectStrLit(c, it), it.info) of wDirty: if sym.kind == skTemplate: incl(sym.flags, sfDirty) - else: invalidPragma(it) + else: invalidPragma(c, it) + of wRedefine: + if sym.kind == skTemplate: incl(sym.flags, sfTemplateRedefinition) + else: invalidPragma(c, it) + of wCallsite: + if sym.kind == skTemplate: incl(sym.flags, sfCallsite) + else: invalidPragma(c, it) of wImportCpp: - processImportCpp(sym, getOptionalStr(c, it, "$1"), it.info) + processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info) + of wCppNonPod: + incl(sym.flags, sfCppNonPod) + of wImportJs: + if c.config.backend != backendJs: + localError(c.config, it.info, "`importjs` pragma requires the JavaScript target") + let name = getOptionalStr(c, it, "$1") + incl(sym.flags, sfImportc) + incl(sym.flags, sfInfixCall) + if sym.kind in skProcKinds and {'(', '#', '@'} notin name: + localError(c.config, n.info, "`importjs` for routines requires a pattern") + setExternName(c, sym, name, it.info) of wImportObjC: - processImportObjC(sym, getOptionalStr(c, it, "$1"), it.info) - of wAlign: - if sym.typ == nil: invalidPragma(it) - var align = expectIntLit(c, it) - if (not isPowerOfTwo(align) and align != 0) or align >% high(int16): - localError(it.info, errPowerOfTwoExpected) - else: - sym.typ.align = align.int16 + processImportObjC(c, sym, getOptionalStr(c, it, "$1"), it.info) of wSize: - if sym.typ == nil: invalidPragma(it) + if sym.typ == nil: invalidPragma(c, it) var size = expectIntLit(c, it) - if not isPowerOfTwo(size) or size <= 0 or size > 8: - localError(it.info, errPowerOfTwoExpected) - else: + case size + of 1, 2, 4: sym.typ.size = size + sym.typ.align = int16 size + of 8: + sym.typ.size = 8 + sym.typ.align = floatInt64Align(c.config) + else: + localError(c.config, it.info, "size may only be 1, 2, 4 or 8") + of wAlign: + let alignment = expectIntLit(c, it) + if isPowerOfTwo(alignment) and alignment > 0: + sym.alignment = max(sym.alignment, alignment) + else: + localError(c.config, it.info, "power of two expected") of wNodecl: - noVal(it) + noVal(c, it) incl(sym.loc.flags, lfNoDecl) of wPure, wAsmNoStackFrame: - noVal(it) + noVal(c, it) if sym != nil: - if k == wPure and sym.kind in routineKinds: invalidPragma(it) + if k == wPure and sym.kind in routineKinds: invalidPragma(c, it) else: incl(sym.flags, sfPure) of wVolatile: - noVal(it) + noVal(c, it) incl(sym.flags, sfVolatile) + of wCursor: + noVal(c, it) + incl(sym.flags, sfCursor) of wRegister: - noVal(it) + noVal(c, it) incl(sym.flags, sfRegister) + of wNoalias: + noVal(c, it) + incl(sym.flags, sfNoalias) + of wEffectsOf: + processEffectsOf(c, it, sym) of wThreadVar: - noVal(it) - incl(sym.flags, sfThread) - of wDeadCodeElim: pragmaDeadCodeElim(c, it) + noVal(c, it) + incl(sym.flags, {sfThread, sfGlobal}) + 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, sfReorder) + of wReorder: pragmaNoForward(c, it, flag = sfReorder) of wMagic: processMagic(c, it, sym) of wCompileTime: - noVal(it) - incl(sym.flags, sfCompileTime) - incl(sym.loc.flags, lfNoDecl) + noVal(c, it) + if comesFromPush: + if sym.kind in {skProc, skFunc}: + incl(sym.flags, sfCompileTime) + else: + incl(sym.flags, sfCompileTime) + #incl(sym.loc.flags, lfNoDecl) of wGlobal: - noVal(it) + noVal(c, it) incl(sym.flags, sfGlobal) incl(sym.flags, sfPure) - of wMerge: - # only supported for backwards compat, doesn't do anything anymore - noVal(it) of wConstructor: - noVal(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) @@ -749,273 +1015,397 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int, incl(sym.loc.flags, lfHeader) incl(sym.loc.flags, lfNoDecl) # implies nodecl, because otherwise header would not make sense - if sym.loc.r == nil: sym.loc.r = rope(sym.name.s) - of wDestructor: - sym.flags.incl sfOverriden - if sym.name.s.normalize != "destroy": - localError(n.info, errGenerated, "destructor has to be named 'destroy'") - of wOverride: - sym.flags.incl sfOverriden - of wNosideeffect: - noVal(it) - incl(sym.flags, sfNoSideEffect) - if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect) - of wSideeffect: - noVal(it) + if sym.loc.snippet == "": sym.loc.snippet = rope(sym.name.s) + of wNoSideEffect: + noVal(c, it) + if sym != nil: + incl(sym.flags, sfNoSideEffect) + if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect) + of wSideEffect: + noVal(c, it) incl(sym.flags, sfSideEffect) of wNoreturn: - noVal(it) - incl(sym.flags, sfNoReturn) + noVal(c, it) + # Disable the 'noreturn' annotation when in the "Quirky Exceptions" mode! + if c.config.exc != excQuirky: + incl(sym.flags, sfNoReturn) + if sym.typ.returnType != nil: + localError(c.config, sym.ast[paramsPos][0].info, + ".noreturn with return type not allowed") + of wNoDestroy: + noVal(c, it) + incl(sym.flags, sfGeneratedOp) + of wNosinks: + noVal(c, it) + incl(sym.flags, sfWasForwarded) of wDynlib: processDynLib(c, it, sym) - of wCompilerproc: - noVal(it) # compilerproc may not get a string! + of wCompilerProc, wCore: + noVal(c, it) # compilerproc may not get a string! cppDefine(c.graph.config, sym.name.s) - if sfFromGeneric notin sym.flags: markCompilerProc(sym) + recordPragma(c, it, "cppdefine", sym.name.s) + if sfFromGeneric notin sym.flags: markCompilerProc(c, sym) + of wNonReloadable: + sym.flags.incl sfNonReloadable of wProcVar: - noVal(it) - incl(sym.flags, sfProcvar) + # old procvar annotation, no longer needed + noVal(c, it) of wExplain: sym.flags.incl sfExplain of wDeprecated: - if it.kind == nkExprColonExpr: deprecatedStmt(c, it) - elif sym != nil: incl(sym.flags, sfDeprecated) + if sym != nil and sym.kind in routineKinds + {skType, skVar, skLet, skConst}: + if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it) + incl(sym.flags, sfDeprecated) + elif sym != nil and sym.kind != skModule: + # We don't support the extra annotation field + if it.kind in nkPragmaCallKinds: + localError(c.config, it.info, "annotation to deprecated not supported here") + incl(sym.flags, sfDeprecated) + # At this point we're quite sure this is a statement and applies to the + # whole module + elif it.kind in nkPragmaCallKinds: deprecatedStmt(c, it) else: incl(c.module.flags, sfDeprecated) of wVarargs: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfVarargs) of wBorrow: if sym.kind == skType: - typeBorrow(sym, it) + typeBorrow(c, sym, it) else: - noVal(it) + noVal(c, it) incl(sym.flags, sfBorrow) of wFinal: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfFinal) of wInheritable: - noVal(it) - if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it) + noVal(c, it) + if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(c, it) else: incl(sym.typ.flags, tfInheritable) + of wPackage: + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) + else: incl(sym.flags, sfForward) of wAcyclic: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfAcyclic) of wShallow: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfShallow) of wThread: - noVal(it) + noVal(c, it) incl(sym.flags, sfThread) - incl(sym.flags, sfProcvar) if sym.typ != nil: incl(sym.typ.flags, tfThread) - if sym.typ.callConv == ccClosure: sym.typ.callConv = ccDefault + 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(it) + noVal(c, it) if sym != nil: if sym.kind != skType: incl(sym.flags, sfThread) if sym.typ != nil: incl(sym.typ.flags, tfGcSafe) - else: invalidPragma(it) + else: invalidPragma(c, it) else: discard "no checking if used as a code block" of wPacked: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfPacked) - of wHint: message(it.info, hintUser, expectStrLit(c, it)) - of wWarning: message(it.info, warnUser, expectStrLit(c, it)) + of wHint: + let s = expectStrLit(c, it) + recordPragma(c, it, "hint", s) + message(c.config, it.info, hintUser, s) + of wWarning: + let s = expectStrLit(c, it) + recordPragma(c, it, "warning", s) + message(c.config, it.info, warnUser, s) of wError: - if sym != nil and sym.isRoutine: + if sym != nil and (sym.isRoutine or sym.kind == skType) and not isStatement: # This is subtle but correct: the error *statement* is only - # allowed for top level statements. Seems to be easier than - # distinguishing properly between + # allowed when 'wUsed' is not in validPragmas. Here this is the easiest way to + # distinguish properly between # ``proc p() {.error}`` and ``proc p() = {.error: "msg".}`` - noVal(it) + if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it) incl(sym.flags, sfError) + excl(sym.flags, sfForward) else: - localError(it.info, errUser, expectStrLit(c, it)) - of wFatal: fatal(it.info, errUser, expectStrLit(c, it)) - of wDefine: processDefine(c, it) + let s = expectStrLit(c, it) + 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, sym) of wUndef: processUndef(c, it) - of wCompile: processCompile(c, it) - of wLink: processCommonLink(c, it, linkNormal) - of wLinksys: processCommonLink(c, it, linkSys) - of wPassl: extccomp.addLinkOption(expectStrLit(c, it)) - of wPassc: extccomp.addCompileOption(expectStrLit(c, it)) - of wBreakpoint: pragmaBreakpoint(c, it) - of wWatchPoint: pragmaWatchpoint(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: processPush(c, n, i + 1) result = true - of wPop: processPop(c, it) - of wPragma: - processPragma(c, n, i) + of wPop: + processPop(c, it) result = true + of wPragma: + if not sym.isNil and sym.kind == skTemplate: + sym.flags.incl sfCustomPragma + else: + processPragma(c, n, i) + result = true of wDiscardable: - noVal(it) + noVal(c, it) if sym != nil: incl(sym.flags, sfDiscardable) of wNoInit: - noVal(it) + noVal(c, it) if sym != nil: incl(sym.flags, sfNoInit) of wCodegenDecl: processCodegenDecl(c, it, sym) - of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks, - wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints, - wLinedir, wStacktrace, wLinetrace, wOptimization, - wCallconv, - wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks, - wPatterns: - if processOption(c, it): - # calling conventions (boring...): - localError(it.info, errOptionExpected) + of wChecks, wObjChecks, wFieldChecks, wRangeChecks, wBoundChecks, + wOverflowChecks, wNilChecks, wAssertions, wWarnings, wHints, + wLineDir, wOptimization, wStaticBoundchecks, wStyleChecks, + wCallconv, wDebugger, wProfiler, + wFloatChecks, wNanChecks, wInfChecks, wPatterns, wTrMacros: + processOption(c, it, c.config.options) + of wStackTrace, wLineTrace: + if sym.kind in {skProc, skMethod, skConverter}: + processOption(c, it, sym.options) + else: + processOption(c, it, c.config.options) of FirstCallConv..LastCallConv: assert(sym != nil) - if sym.typ == nil: invalidPragma(it) - else: sym.typ.callConv = wordToCallConv(k) + if sym.typ == nil: invalidPragma(c, it) + else: + sym.typ.callConv = wordToCallConv(k) + sym.typ.flags.incl tfExplicitCallConv of wEmit: pragmaEmit(c, it) of wUnroll: pragmaUnroll(c, it) - of wLinearScanEnd, wComputedGoto: noVal(it) + of wLinearScanEnd, wComputedGoto: noVal(c, it) of wEffects: # is later processed in effect analysis: - noVal(it) + noVal(c, it) of wIncompleteStruct: - noVal(it) - if sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfIncompleteStruct) + of wCompleteStruct: + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) + else: incl(sym.typ.flags, tfCompleteStruct) of wUnchecked: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfUncheckedArray) + noVal(c, it) + if sym.typ == nil or sym.typ.kind notin {tyArray, tyUncheckedArray}: + invalidPragma(c, it) + else: + sym.typ.kind = tyUncheckedArray of wUnion: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfUnion) + if c.config.backend == backendJs: + localError(c.config, it.info, "`{.union.}` is not implemented for js backend.") + else: + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) + else: incl(sym.typ.flags, tfUnion) of wRequiresInit: - noVal(it) - if sym.typ == nil: invalidPragma(it) - else: incl(sym.typ.flags, tfNeedsInit) + noVal(c, it) + if sym.kind == skField: + sym.flags.incl sfRequiresInit + elif sym.typ != nil: + incl(sym.typ.flags, tfNeedsFullInit) + else: + invalidPragma(c, it) of wByRef: - noVal(it) - if sym == nil or sym.typ == nil: - if processOption(c, it): localError(it.info, errOptionExpected) + noVal(c, it) + 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(it) - if sym.kind != skType or sym.typ == nil: invalidPragma(it) + noVal(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(it) - if sym.kind != skType or sym.typ == nil: invalidPragma(it) + noVal(c, it) + if sym.kind != skType or sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfPartial) - # .partial types can only work with dead code elimination - # to prevent the codegen from doing anything before we compiled - # the whole program: - incl gGlobalOptions, optDeadCodeElim of wInject, wGensym: # We check for errors, but do nothing with these pragmas otherwise # as they are handled directly in 'evalTemplate'. - noVal(it) - if sym == nil: invalidPragma(it) + noVal(c, it) + if sym == nil: invalidPragma(c, it) of wLine: pragmaLine(c, it) - of wRaises, wTags: pragmaRaisesOrTags(c, it) + of wRaises, wTags, wForbids: pragmaRaisesOrTags(c, it) of wLocks: if sym == nil: pragmaLockStmt(c, it) - elif sym.typ == nil: invalidPragma(it) - else: sym.typ.lockLevel = pragmaLocks(c, it) + elif sym.typ == nil: invalidPragma(c, it) + else: warningDeprecated(c.config, n.info, "'Lock levels' are deprecated, now a noop") of wBitsize: - if sym == nil or sym.kind != skField or it.kind != nkExprColonExpr: - invalidPragma(it) + if sym == nil or sym.kind != skField: + invalidPragma(c, it) else: sym.bitsize = expectIntLit(c, it) + if sym.bitsize <= 0: + localError(c.config, it.info, "bitsize needs to be positive") of wGuard: if sym == nil or sym.kind notin {skVar, skLet, skField}: - invalidPragma(it) + invalidPragma(c, it) else: sym.guard = pragmaGuard(c, it, sym.kind) of wGoto: if sym == nil or sym.kind notin {skVar, skLet}: - invalidPragma(it) + invalidPragma(c, it) else: sym.flags.incl sfGoto of wExportNims: - if sym == nil: invalidPragma(it) - else: magicsys.registerNimScriptSymbol(sym) - of wInjectStmt: - if it.kind != nkExprColonExpr: - localError(it.info, errExprExpected) - else: - it.sons[1] = c.semExpr(c, it.sons[1]) + if sym == nil: invalidPragma(c, it) + else: magicsys.registerNimScriptSymbol(c.graph, sym) of wExperimental: - noVal(it) - if isTopLevel(c): - c.module.flags.incl sfExperimental - else: - localError(it.info, "'experimental' pragma only valid as toplevel statement") - of wThis: - if it.kind == nkExprColonExpr: - c.selfName = considerQuotedIdent(it[1]) - else: - c.selfName = getIdent("self") + if not isTopLevel(c): + localError(c.config, n.info, "'experimental' pragma only valid as toplevel statement or in a 'push' environment") + processExperimental(c, it) + of wDoctype: + if not isTopLevel(c): + localError(c.config, n.info, "\"doctype\" pragma only valid as top-level statement") of wNoRewrite: - noVal(it) + noVal(c, it) of wBase: - noVal(it) + 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: + processDefineConst(c, n, sym, mBoolDefine) of wUsed: - noVal(it) - if sym == nil: invalidPragma(it) + noVal(c, it) + if sym == nil: invalidPragma(c, it) else: sym.flags.incl sfUsed - else: invalidPragma(it) - else: invalidPragma(it) - -proc implicitPragmas*(c: PContext, sym: PSym, n: PNode, + of wLiftLocals: + sym.flags.incl(sfForceLift) + of wRequires, wInvariant, wAssume, wAssert: + pragmaProposition(c, it) + of wEnsures: + pragmaEnsures(c, it) + 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: + # semCustomPragma gives appropriate error for invalid pragmas + n[i] = semCustomPragma(c, it, sym) + +proc overwriteLineInfo(n: PNode; info: TLineInfo) = + n.info = info + for i in 0..<n.safeLen: + overwriteLineInfo(n[i], info) + +proc mergePragmas(n, pragmas: PNode) = + var pragmas = copyTree(pragmas) + overwriteLineInfo pragmas, n.info + if n[pragmasPos].kind == nkEmpty: + n[pragmasPos] = pragmas + 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: - pushInfoContext(n.info) - for i in countup(0, sonsLen(o) - 1): - if singlePragma(c, sym, o, i, validPragmas): - internalError(n.info, "implicitPragmas") - popInfoContext() + if not o.isNil and sfFromGeneric notin sym.flags: # bug #23019 + pushInfoContext(c.config, info) + var i = 0 + while i < o.len: + if singlePragma(c, sym, o, i, validPragmas, true, false): + internalError(c.config, info, "implicitPragmas") + inc i + popInfoContext(c.config) + 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(n.info, errDynlibRequiresExportc) + localError(c.config, info, ".dynlib requires .exportc") var lib = c.optionStack[^1].dynlib if {lfDynamicLib, lfHeader} * sym.loc.flags == {} and sfImportc in sym.flags and lib != nil: incl(sym.loc.flags, lfDynamicLib) addToLib(lib, sym) - if sym.loc.r == nil: 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 or n.sons == nil: - return false + if n == nil: return false - for p in n.sons: - var key = if p.kind == nkExprColonExpr: p[0] else: p + for p in n: + var key = if p.kind in nkPragmaCallKinds and p.len > 1: p[0] else: p if key.kind == nkIdent and whichKeyword(key.ident) == pragma: return true return false -proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = +proc pragmaRec(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords; + isStatement: bool) = + if n == nil: return + var i = 0 + while i < n.len: + if singlePragma(c, sym, n, i, validPragmas, false, isStatement): break + inc i + +proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords; + isStatement: bool) = if n == nil: return - for i in countup(0, sonsLen(n) - 1): - if n.sons[i].kind == nkPragma: pragmaRec(c, sym, n.sons[i], validPragmas) - elif singlePragma(c, sym, n, i, validPragmas): break + pragmaRec(c, sym, n, validPragmas, isStatement) + # XXX: in the case of a callable def, this should use its info + implicitPragmas(c, sym, n.info, validPragmas) -proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) = +proc pragmaCallable*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords, + isStatement: bool = false) = if n == nil: return - pragmaRec(c, sym, n, validPragmas) - implicitPragmas(c, sym, n, validPragmas) + if n[pragmasPos].kind != nkEmpty: + pragmaRec(c, sym, n[pragmasPos], validPragmas, isStatement) |