# # # The Nim Compiler # (c) Copyright 2015 Andreas Rumpf # # See the file "copying.txt", included in this # distribution, for details about the copyright. # # This module implements semantic checking for pragmas import condsyms, ast, astalgo, idents, semdata, msgs, renderer, wordrecg, ropes, options, extccomp, magicsys, trees, types, lookups, lineinfos, pathutils, linter, modulepaths import std/[os, math, strutils] when defined(nimPreviewSlimSystem): import std/assertions from ic / ic import addCompilerProc const FirstCallConv* = wNimcall LastCallConv* = wNoconv const 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* = {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, 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, 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, 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} letPragmas* = varPragmas 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) 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 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[0] != nil: var s = newSym(skResult, getIdent(c.cache, "result"), c.idgen, o, n.info) s.typ = o.typ[0] 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.. 0 and splitFile(s).ext == "": s = addFileExt(s, ext) result = AbsoluteFile parentDir(toFullPath(c.config, n.info)) / s if not fileExists(result): if isAbsolute(s): result = AbsoluteFile s else: 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[i] = c.semConstExpr(c, n[i]) case n[i].kind of nkStrLit, nkRStrLit, nkTripleStrLit: when defined(gcArc) or defined(gcOrc) or defined(gcAtomicArc): result = n[i].strVal else: shallowCopy(result, n[i].strVal) else: localError(c.config, n.info, errStringLiteralExpected) result = "" 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(toFullPath(c.config, n.info)) / s for f in os.walkFiles(found): let obj = completeCfilePath(c.config, AbsoluteFile(dest % extractFilename(f))) docompile(c, it, AbsoluteFile f, obj, "") else: 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 = AbsoluteFile s else: 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[1].kind of nkStrLit, nkRStrLit, nkTripleStrLit: result = newNodeI(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info) var str = n[1].strVal if str == "": 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 != "": 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 amb = false var e = searchInScopes(con, getIdent(con.cache, sub), amb) # XXX what to do here if 'amb' is true? if e != nil: incl(e.flags, sfUsed) result.add newSymNode(e) else: result.add newStrNode(nkStrLit, sub) else: # an empty '``' produces a single '`' result.add newStrNode(nkStrLit, $marker) if c < 0: break a = c + 1 else: illFormedAstLocal(n, con.config) result = newNodeI(nkAsmStmt, n.info) proc pragmaEmit(c: PContext, n: PNode) = 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.. 1: invalidPragma(c, n) proc pragmaUnroll(c: PContext, n: PNode) = if c.p.nestedLoopCounter <= 0: invalidPragma(c, n) elif n.kind in nkPragmaCallKinds and n.len == 2: var unrollFactor = expectIntLit(c, n) if unrollFactor <% 32: n[1] = newIntNode(nkIntLit, unrollFactor) else: invalidPragma(c, n) proc pragmaLine(c: PContext, n: PNode) = 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(c.config, n.info, errStringLiteralExpected) elif y.kind != nkIntLit: localError(c.config, n.info, errIntLiteralExpected) else: n.info.fileIndex = fileInfoIdx(c.config, AbsoluteFile(x.strVal)) n.info.line = uint16(y.intVal) else: localError(c.config, n.info, "tuple expected") else: # sensible default: n.info = getInfoContext(c.config, -1) 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) elif it.safeLen != 2 or it[0].kind != nkIdent or it[1].kind != nkIdent: invalidPragma(c, n) 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) = 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 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(c, n) proc pragmaLockStmt(c: PContext; it: PNode) = if it.kind notin nkPragmaCallKinds or it.len != 2: invalidPragma(c, it) else: let n = it[1] if n.kind != nkBracket: localError(c.config, n.info, errGenerated, "locks pragma takes a list of expressions") else: for i in 0.. pragma() callNode = newTree(nkCall, n) elif n.kind == nkExprColonExpr: # pragma: arg -> pragma(arg) callNode = newTree(nkCall, n[0], n[1]) elif n.kind in nkPragmaCallKinds: callNode = n else: invalidPragma(c, n) return n 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 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 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, 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) 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.config, name) recordPragma(c, it, "cppdefine", name) makeExternImport(c, sym, name, it.info) of wImportCompilerProc: let name = getOptionalStr(c, it, "$1") 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(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(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(c, sym, getOptionalStr(c, it, "$1"), it.info) of wSize: if sym.typ == nil: invalidPragma(c, it) var size = expectIntLit(c, it) 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(c, it) incl(sym.loc.flags, lfNoDecl) of wPure, wAsmNoStackFrame: noVal(c, it) if sym != nil: if k == wPure and sym.kind in routineKinds: invalidPragma(c, it) else: incl(sym.flags, sfPure) of wVolatile: noVal(c, it) incl(sym.flags, sfVolatile) of wCursor: noVal(c, it) incl(sym.flags, sfCursor) of wRegister: 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(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, flag = sfReorder) of wMagic: processMagic(c, it, sym) of wCompileTime: 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(c, it) incl(sym.flags, sfGlobal) incl(sym.flags, sfPure) of wConstructor: 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) incl(sym.flags, sfImportc) 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) 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(c, it) # Disable the 'noreturn' annotation when in the "Quirky Exceptions" mode! if c.config.exc != excQuirky: incl(sym.flags, sfNoReturn) if sym.typ[0] != 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, wCore: noVal(c, it) # compilerproc may not get a string! cppDefine(c.graph.config, sym.name.s) recordPragma(c, it, "cppdefine", sym.name.s) if sfFromGeneric notin sym.flags: markCompilerProc(c, sym) of wNonReloadable: sym.flags.incl sfNonReloadable of wProcVar: # old procvar annotation, no longer needed noVal(c, it) of wExplain: sym.flags.incl sfExplain of wDeprecated: 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(c, it) if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfVarargs) of wBorrow: if sym.kind == skType: typeBorrow(c, sym, it) else: noVal(c, it) incl(sym.flags, sfBorrow) of wFinal: noVal(c, it) if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfFinal) of wInheritable: 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(c, it) if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfAcyclic) of wShallow: noVal(c, it) if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfShallow) of wThread: noVal(c, it) incl(sym.flags, sfThread) 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: if sym.kind != skType: incl(sym.flags, sfThread) if sym.typ != nil: incl(sym.typ.flags, tfGcSafe) else: invalidPragma(c, it) else: discard "no checking if used as a code block" of wPacked: noVal(c, it) if sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfPacked) 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 or sym.kind == skType) and not isStatement: # This is subtle but correct: the error *statement* is only # 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".}`` if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it) incl(sym.flags, sfError) excl(sym.flags, sfForward) else: 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: processLink(c, it) of wPassl: let s = expectStrLit(c, it) extccomp.addLinkOption(c.config, s) recordPragma(c, it, "passl", s) of wPassc: 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) 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(c, it) if sym != nil: incl(sym.flags, sfDiscardable) of wNoInit: 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, 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(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(c, it) of wEffects: # is later processed in effect analysis: noVal(c, it) of wIncompleteStruct: 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(c, it) if sym.typ == nil or sym.typ.kind notin {tyArray, tyUncheckedArray}: invalidPragma(c, it) else: sym.typ.kind = tyUncheckedArray of wUnion: 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(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(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(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) if sym.kind != skType or sym.typ == nil: invalidPragma(c, it) else: incl(sym.typ.flags, tfPartial) of wInject, wGensym: # We check for errors, but do nothing with these pragmas otherwise # as they are handled directly in 'evalTemplate'. noVal(c, it) if sym == nil: invalidPragma(c, it) of wLine: pragmaLine(c, it) of wRaises, wTags, wForbids: pragmaRaisesOrTags(c, it) of wLocks: if sym == nil: pragmaLockStmt(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: 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(c, it) else: sym.guard = pragmaGuard(c, it, sym.kind) of wGoto: if sym == nil or sym.kind notin {skVar, skLet}: invalidPragma(c, it) else: sym.flags.incl sfGoto of wExportNims: if sym == nil: invalidPragma(c, it) else: magicsys.registerNimScriptSymbol(c.graph, sym) of wExperimental: 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(c, it) of wBase: noVal(c, it) sym.flags.incl sfBase of wIntDefine: processDefineConst(c, n, sym, mIntDefine) of wStrDefine: processDefineConst(c, n, sym, mStrDefine) of wBoolDefine: 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 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.. 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; 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 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 pragmaCallable*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords, isStatement: bool = false) = if n == nil: return if n[pragmasPos].kind != nkEmpty: pragmaRec(c, sym, n[pragmasPos], validPragmas, isStatement)