Files
Nim/compiler/pragmas.nim
ringabout 5dafcf4957 fixes #22913; fixes #12985 differently push-ing pragma exportc genera… (#22941)
…tes invalid C identifiers

fixes #22913
fixes #12985 differently


`{.push.} now does not apply to generic instantiations`
2023-11-19 17:52:42 +01:00

1365 lines
50 KiB
Nim

#
#
# 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..<n.len:
let it = n[i]
if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent:
case whichKeyword(it[0].ident)
of wSubsChar:
if it[1].kind == nkCharLit: result = chr(int(it[1].intVal))
else: invalidPragma(c, it)
else: invalidPragma(c, it)
else:
invalidPragma(c, it)
proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) =
# special cases to improve performance:
if extname == "$1":
s.loc.r = rope(s.name.s)
elif '$' notin extname:
s.loc.r = rope(extname)
else:
try:
s.loc.r = rope(extname % s.name.s)
except ValueError:
localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
when hasFFI:
s.cname = $s.loc.r
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(c: PContext; s: PSym, extname: string, info: TLineInfo) =
setExternName(c, s, extname, info)
incl(s.flags, sfExportc)
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(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 c.config.backend == backendC:
let m = s.getModule()
incl(m.flags, sfCompileToCpp)
incl c.config.globalOptions, optMixedMode
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)
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 notin nkPragmaCallKinds or n.len != 2:
localError(c.config, n.info, errStringLiteralExpected)
# error correction:
result = newEmptyStrNode(c, n)
else:
n[1] = c.semConstExpr(c, n[1])
case n[1].kind
of nkStrLit, nkRStrLit, nkTripleStrLit: result = n[1]
else:
localError(c.config, n.info, errStringLiteralExpected)
# error correction:
result = newEmptyStrNode(c, n)
proc expectStrLit(c: PContext, n: PNode): string =
result = getStrLitNode(c, n).strVal
proc expectIntLit(c: PContext, n: PNode): int =
result = 0
if n.kind notin nkPragmaCallKinds or n.len != 2:
localError(c.config, n.info, errIntLiteralExpected)
else:
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 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 = ccNoConvention
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 notin nkPragmaCallKinds or n.len != 2:
localError(c.config, n.info, errStringLiteralExpected)
return
var v: string
if n[1].kind == nkIdent: v = n[1].ident.s
else: v = expectStrLit(c, n)
for m in TMagic:
if substr($m, 1) == v:
s.magic = m
break
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
TCallingConvention(ord(ccNimCall) + ord(sw) - ord(wNimcall))
proc isTurnedOn(c: PContext, n: PNode): bool =
result = false
if n.kind in nkPragmaCallKinds and n.len == 2:
let x = c.semConstBoolExpr(c, n[1])
n[1] = x
if x.kind == nkIntLit: return x.intVal != 0
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 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
# 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 processCallConv(c: PContext, n: PNode) =
if n.kind in nkPragmaCallKinds and n.len == 2 and n[1].kind == nkIdent:
let sw = whichKeyword(n[1].ident)
case sw
of FirstCallConv..LastCallConv:
c.optionStack[^1].defaultCC = wordToCallConv(sw)
else: localError(c.config, n.info, "calling convention expected")
else:
localError(c.config, n.info, "calling convention expected")
proc getLib(c: PContext, kind: TLibKind, path: PNode): PLib =
for it in c.libs:
if it.kind == kind and trees.exprStructuralEquivalent(it.path, path):
return it
result = newLib(kind)
result.path = path
c.libs.add result
if path.kind in {nkStrLit..nkTripleStrLit}:
result.isOverridden = options.isDynlibOverride(c.config, path.strVal)
proc expectDynlibNode(c: PContext, n: PNode): PNode =
if n.kind notin nkPragmaCallKinds or n.len != 2:
localError(c.config, n.info, errStringLiteralExpected)
# error correction:
result = newEmptyStrNode(c, n)
else:
# For the OpenGL wrapper we support:
# {.dynlib: myGetProcAddr(...).}
result = c.semExpr(c, n[1])
if result.kind == nkSym and result.sym.kind == skConst:
result = c.semConstExpr(c, result) # fold const
if result.typ == nil or result.typ.kind notin {tyPointer, tyString, tyProc}:
localError(c.config, n.info, errStringLiteralExpected)
result = newEmptyStrNode(c, n)
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.isOverridden:
c.optionStack[^1].dynlib = lib
else:
if n.kind in nkPragmaCallKinds:
var lib = getLib(c, libDynamic, expectDynlibNode(c, n))
if not lib.isOverridden:
addToLib(lib, sym)
incl(sym.loc.flags, lfDynamicLib)
else:
incl(sym.loc.flags, lfExportLib)
# since we'll be loading the dynlib symbols dynamically, we must use
# 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
tfExplicitCallConv notin sym.typ.flags:
sym.typ.callConv = ccCDecl
proc processNote(c: PContext, n: PNode) =
template handleNote(enumVals, notes) =
let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown)
if x != errUnknown:
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(c, n)
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:
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 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:
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 processPush(c: PContext, n: PNode, start: int) =
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:
if x.otherPragmas.isNil:
x.otherPragmas = newNodeI(nkPragma, n.info)
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(c.config, n.info, "{.pop.} without a corresponding {.push.}")
else:
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(c, n)
proc processUndef(c: PContext, n: PNode) =
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
undefSymbol(c.config.symbols, n[1].ident.s)
else:
invalidPragma(c, n)
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 = 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..<n1.len:
b[i] = c.semExprWithType(c, n1[i], {efTypeAllowed})
n[1] = b
else:
n[1] = c.semConstExpr(c, n1)
case n[1].kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
n[1] = semAsmOrEmit(c, n, '`')
else:
localError(c.config, n.info, errStringLiteralExpected)
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(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..<n.len:
n[i] = c.semExpr(c, n[i])
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(c.config, n.info, "a type can only borrow `.` for now")
incl(sym.typ.flags, tfBorrowDot)
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(c, s, "$1", s.info)
incl(s.flags, sfCompilerProc)
incl(s.flags, sfUsed)
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(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 notin nkPragmaCallKinds or it.len != 2:
invalidPragma(c, it); return
let n = it[1]
if n.kind == nkSym:
result = n.sym
elif kind == skField:
# First check if the guard is a global variable:
result = qualifiedLookUp(c, n, {})
if result.isNil or result.kind notin {skLet, skVar} or
sfGlobal notin result.flags:
# We return a dummy symbol; later passes over the type will repair it.
# Generic instantiation needs to know about this too. But we're lazy
# and perform the lookup on demand instead.
result = newSym(skUnknown, considerQuotedIdent(c, n), c.idgen, nil, n.info,
c.config.options)
else:
result = qualifiedLookUp(c, n, {checkUndeclared})
proc semCustomPragma(c: PContext, n: PNode, sym: PSym): PNode =
var callNode: PNode
if n.kind in {nkIdent, nkSym}:
# pragma -> 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..<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 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(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: mergePragmas(sym.ast, o)
if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
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 == "": sym.loc.r = rope(sym.name.s)
proc hasPragma*(n: PNode, pragma: TSpecialWord): bool =
if n == nil: return false
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;
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)