mirror of
https://github.com/nim-lang/Nim.git
synced 2025-12-28 17:04:41 +00:00
1326 lines
49 KiB
Nim
1326 lines
49 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
|
|
os, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
|
|
wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
|
|
types, lookups, lineinfos, pathutils, linter
|
|
|
|
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, wMerge,
|
|
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}
|
|
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}
|
|
fieldPragmas* = declPragmas + {wGuard, wBitsize, wCursor,
|
|
wRequiresInit, wNoalias, wAlign} - {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}
|
|
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 =
|
|
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) =
|
|
localError(c.config, n.info, "cannot attach a custom pragma to '" & s.name.s & "'")
|
|
|
|
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.sons[0] != nil:
|
|
var s = newSym(skResult, getIdent(c.cache, "result"), nextSymId(c.idgen), o, n.info)
|
|
s.typ = o.typ.sons[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
|
|
if c.config.cmd == cmdNimfix and '$' notin extname:
|
|
# note that '{.importc.}' is transformed into '{.importc: "$1".}'
|
|
s.loc.flags.incl(lfFullExternalName)
|
|
|
|
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): PNode {.noinline.} =
|
|
result = newNodeIT(nkStrLit, n.info, getSysType(c.graph, n.info, tyString))
|
|
result.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 =
|
|
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 processCodegenDecl(c: PContext, n: PNode, sym: PSym) =
|
|
sym.constraint = getStrLitNode(c, n)
|
|
|
|
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 =
|
|
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.isOverriden = 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 = result.sym.astdef # look it up
|
|
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.isOverriden:
|
|
c.optionStack[^1].dynlib = lib
|
|
else:
|
|
if n.kind in nkPragmaCallKinds:
|
|
var lib = getLib(c, libDynamic, expectDynlibNode(c, n))
|
|
if not lib.isOverriden:
|
|
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}
|
|
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) =
|
|
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):
|
|
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 obj = toObjFile(c.config, completeCfilePath(c.config, found, false))
|
|
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.semExpr(c, n1[i])
|
|
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, nextSymId(c.idgen), c.module, it.info, c.config.options)
|
|
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
|
|
for n in pragma:
|
|
if n.kind in nkPragmaCallKinds and n.len == 2:
|
|
let dest = qualifiedLookUp(c, n[1], {checkUndeclared})
|
|
if dest == nil or dest.kind in routineKinds:
|
|
localError(c.config, n.info, warnUser, "the .deprecated pragma is unreliable for routines")
|
|
let src = considerQuotedIdent(c, n[0])
|
|
let alias = newSym(skAlias, src, nextSymId(c.idgen), dest, n[0].info, c.config.options)
|
|
incl(alias.flags, sfExported)
|
|
if sfCompilerProc in dest.flags: markCompilerProc(c, alias)
|
|
addInterfaceDecl(c, alias)
|
|
n[1] = newSymNode(dest)
|
|
else:
|
|
localError(c.config, n.info, "key:value pair expected")
|
|
|
|
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), nextSymId(c.idgen), nil, n.info,
|
|
c.config.options)
|
|
else:
|
|
result = qualifiedLookUp(c, n, {checkUndeclared})
|
|
|
|
proc semCustomPragma(c: PContext, n: PNode): 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
|
|
|
|
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 =
|
|
var it = n[i]
|
|
var key = if it.kind in nkPragmaCallKinds and it.len > 1: 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)
|
|
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)
|
|
|
|
pragma(c, sym, userPragma.ast, validPragmas, isStatement)
|
|
n.sons[i..i] = userPragma.ast.sons # expand user pragma with its content
|
|
i.inc(userPragma.ast.len - 1) # inc by -1 is ok, user pragmas was empty
|
|
dec c.instCounter
|
|
else:
|
|
let k = whichKeyword(ident)
|
|
if k in validPragmas:
|
|
checkPragmaUse(c.config, key.info, k, ident.s)
|
|
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 wMerge:
|
|
# only supported for backwards compat, doesn't do anything anymore
|
|
noVal(c, it)
|
|
of wConstructor:
|
|
noVal(c, it)
|
|
incl(sym.flags, sfConstructor)
|
|
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 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)
|
|
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 or sym.typ == nil:
|
|
processOption(c, it, c.config.options)
|
|
else:
|
|
incl(sym.typ.flags, tfByRef)
|
|
of wByCopy:
|
|
noVal(c, it)
|
|
if sym.kind != skType or sym.typ == nil: invalidPragma(c, it)
|
|
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:
|
|
sym.flags.incl sfNeverRaises
|
|
of wSystemRaisesDefect:
|
|
sym.flags.incl sfSystemRaisesDefect
|
|
else: invalidPragma(c, it)
|
|
elif comesFromPush and whichKeyword(ident) != wInvalid:
|
|
discard "ignore the .push pragma; it doesn't apply"
|
|
else:
|
|
if sym == nil or (sym.kind in {skVar, skLet, skConst, skParam, skIterator,
|
|
skField, skProc, skFunc, skConverter, skMethod, skType}):
|
|
n[i] = semCustomPragma(c, it)
|
|
elif sym != nil:
|
|
illegalCustomPragma(c, it, sym)
|
|
else:
|
|
invalidPragma(c, it)
|
|
|
|
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 and sfFromGeneric notin sym.flags: # see issue #12985
|
|
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)
|