#
#
# 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, platform, condsyms, ast, astalgo, idents, semdata, msgs, renderer,
wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees,
types, lookups, lineinfos
const
FirstCallConv* = wNimcall
LastCallConv* = wNoconv
const
procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
wCompilerProc, wCore, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
wAsmNoStackFrame, wError, wDiscardable, wNoInit, wCodegenDecl,
wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe, wOverride,
wConstructor, wExportNims, wUsed, wLiftLocals, wStacktrace, wLinetrace}
converterPragmas* = procPragmas
methodPragmas* = procPragmas+{wBase}-{wImportCpp}
templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
wDelegator, wExportNims, wUsed, wPragma}
macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
wNodecl, wMagic, wNosideeffect, wCompilerProc, wCore, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator,
wExportNims, wUsed}
iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect,
wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
wTags, wLocks, wGcSafe, wExportNims, wUsed}
exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe}
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
wBoundchecks, wOverflowchecks, wNilchecks, wMovechecks, wAssertions,
wWarnings, wHints,
wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop,
wBreakpoint, wWatchPoint, wPassl, wPassc,
wDeadCodeElimUnused, # deprecated, always on
wDeprecated,
wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
wLinearScanEnd, wPatterns, wEffects, wNoForward, wReorder, wComputedGoto,
wInjectStmt, wDeprecated, wExperimental, wThis}
lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
wRaises, wLocks, wTags, wGcSafe}
typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
wPure, wHeader, wCompilerProc, wCore, wFinal, wSize, wExtern, wShallow,
wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
wBorrow, wGcSafe, wExportNims, wPartial, wUsed, wExplain, wPackage}
fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
wImportCpp, wImportObjC, wError, wGuard, wBitsize, wUsed}
varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
wMagic, wHeader, wDeprecated, wCompilerProc, wCore, wDynlib, wExtern,
wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims, wUsed}
constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims,
wIntDefine, wStrDefine, wUsed, wCompilerProc, wCore}
letPragmas* = varPragmas
procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
wThread, wRaises, wLocks, wTags, wGcSafe}
allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas
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)
proc recordPragma(c: PContext; n: PNode; key, val: string; val2 = "") =
var recorded = newNodeI(nkCommentStmt, n.info)
recorded.add newStrNode(key, n.info)
recorded.add newStrNode(val, n.info)
if val2.len > 0: recorded.add newStrNode(val2, n.info)
c.graph.recordStmt(c.graph, c.module, 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 pragmaAsm*(c: PContext, n: PNode): char =
result = '\0'
if n != nil:
for i in countup(0, sonsLen(n) - 1):
let it = n.sons[i]
if it.kind in nkPragmaCallKinds and it.len == 2 and it.sons[0].kind == nkIdent:
case whichKeyword(it.sons[0].ident)
of wSubsChar:
if it.sons[1].kind == nkCharLit: result = chr(int(it.sons[1].intVal))
else: invalidPragma(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 '$'?)")
if c.config.cmd == cmdPretty 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.cmd == cmdCompileToC:
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.sons[1] = c.semConstExpr(c, n.sons[1])
case n.sons[1].kind
of nkStrLit, nkRStrLit, nkTripleStrLit: result = n.sons[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.sons[1] = c.semConstExpr(c, n.sons[1])
case n.sons[1].kind
of nkIntLit..nkInt64Lit: result = int(n.sons[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.sons[1].kind == nkIdent: v = n.sons[1].ident.s
else: v = expectStrLit(c, n)
for m in countup(low(TMagic), high(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
result = TCallingConvention(ord(ccDefault) + 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.sons[1])
n.sons[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 = resOptions + op
else: resOptions = resOptions - op
proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
if isTurnedOn(c, n): incl(c.module.flags, flag)
else: excl(c.module.flags, flag)
proc processCallConv(c: PContext, n: PNode) =
if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent:
let sw = whichKeyword(n.sons[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.sons[1])
if result.kind == nkSym and result.sym.kind == skConst:
result = result.sym.ast # 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
sym.typ.callConv == ccDefault:
sym.typ.callConv = ccCDecl
proc processNote(c: PContext, n: PNode) =
if n.kind in nkPragmaCallKinds and len(n) == 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:
var x = findStr(HintsToStr, n[0][1].ident.s)
if x >= 0: nk = TNoteKind(x + ord(hintMin))
else: invalidPragma(c, n); return
of wWarning:
var x = findStr(WarningsToStr, n[0][1].ident.s)
if x >= 0: nk = TNoteKind(x + ord(warnMin))
else: invalidPragma(c, n); return
else:
invalidPragma(c, n)
return
let x = c.semConstBoolExpr(c, n[1])
n.sons[1] = x
if x.kind == nkIntLit and x.intVal != 0: incl(c.config.notes, nk)
else: excl(c.config.notes, nk)
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 wNilchecks: {optNilCheck}
of wFloatchecks: {optNaNCheck, optInfCheck}
of wNanChecks: {optNaNCheck}
of wInfChecks: {optInfCheck}
of wMovechecks: {optMoveCheck}
of wAssertions: {optAssert}
of wWarnings: {optWarns}
of wHints: {optHints}
of wLinedir: {optLineDir}
of wStacktrace: {optStackTrace}
of wLinetrace: {optLineTrace}
of wDebugger: {optEndb}
of wProfiler: {optProfiler, optMemTracker}
of wMemTracker: {optMemTracker}
of wByRef: {optByRef}
of wImplicitStatic: {optImplicitStatic}
of wPatterns: {optPatterns}
else: {}
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.sons[0].kind == nkBracketExpr: processNote(c, n)
elif n.sons[0].kind != nkIdent: result = false
else:
let sw = whichKeyword(n.sons[0].ident)
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.sons[1].kind != nkIdent:
invalidPragma(c, n)
else:
case n.sons[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.sons[start-1].kind in nkPragmaCallKinds:
localError(c.config, n.info, "'push' cannot have arguments")
var x = newOptionEntry(c.config)
var y = c.optionStack[^1]
x.options = c.config.options
x.defaultCC = y.defaultCC
x.dynlib = y.dynlib
x.notes = c.config.notes
c.optionStack.add(x)
for i in countup(start, sonsLen(n) - 1):
if not tryProcessOption(c, n.sons[i], c.config.options):
# simply store it somewhere:
if x.otherPragmas.isNil:
x.otherPragmas = newNodeI(nkPragma, n.info)
x.otherPragmas.add n.sons[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)
proc processPop(c: PContext, n: PNode) =
if c.optionStack.len <= 1:
localError(c.config, n.info, "{.pop.} without a corresponding {.push.}")
else:
c.config.options = c.optionStack[^1].options
c.config.notes = c.optionStack[^1].notes
c.optionStack.setLen(c.optionStack.len - 1)
proc processDefine(c: PContext, n: PNode) =
if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent):
defineSymbol(c.config.symbols, n[1].ident.s)
message(c.config, n.info, warnDeprecated, "define")
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)
message(c.config, n.info, warnDeprecated, "undef")
else:
invalidPragma(c, n)
type
TLinkFeature = enum
linkNormal, linkSys
proc relativeFile(c: PContext; n: PNode; ext=""): string =
var s = expectStrLit(c, n)
if ext.len > 0 and splitFile(s).ext == "":
s = addFileExt(s, ext)
result = parentDir(toFullPath(c.config, n.info)) / s
if not fileExists(result):
if isAbsolute(s): result = s
else:
result = findFile(c.config, s)
if result.len == 0: result = s
proc processCompile(c: PContext, n: PNode) =
proc docompile(c: PContext; it: PNode; src, dest: string) =
var cf = Cfile(cname: src, obj: dest, flags: {CfileFlag.External})
extccomp.addExternalFileToCompile(c.config, cf)
recordPragma(c, it, "compile", src, dest)
proc getStrLit(c: PContext, n: PNode; i: int): string =
n.sons[i] = c.semConstExpr(c, n[i])
case n[i].kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
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.sons[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, dest % extractFilename(f))
docompile(c, it, f, obj)
else:
let s = expectStrLit(c, n)
var found = parentDir(toFullPath(c.config, n.info)) / s
if not fileExists(found):
if isAbsolute(s): found = s
else:
found = findFile(c.config, s)
if found.len == 0: found = s
let obj = toObjFile(c.config, completeCFilePath(c.config, found, false))
docompile(c, it, found, obj)
proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
let found = relativeFile(c, n, CC[c.config.cCompiler].objExt)
case feature
of linkNormal:
extccomp.addExternalFileToLink(c.config, found)
recordPragma(c, n, "link", found)
of linkSys:
let dest = c.config.libpath / completeCFilePath(c.config, found, false)
extccomp.addExternalFileToLink(c.config, dest)
recordPragma(c, n, "link", dest)
else: internalError(c.config, n.info, "processCommonLink")
proc pragmaBreakpoint(c: PContext, n: PNode) =
discard getOptionalStr(c, n, "")
proc pragmaWatchpoint(c: PContext, n: PNode) =
if n.kind in nkPragmaCallKinds and n.len == 2:
n.sons[1] = c.semExpr(c, n.sons[1])
else:
invalidPragma(c, n)
proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode =
case n.sons[1].kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
result = newNode(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info)
var str = n.sons[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 != "": addSon(result, newStrNode(nkStrLit, sub))
if b < 0: break
var c = strutils.find(str, marker, b + 1)
if c < 0: sub = substr(str, b + 1)
else: sub = substr(str, b + 1, c - 1)
if sub != "":
var e = searchInScopes(con, getIdent(con.cache, sub))
if e != nil:
when false:
if e.kind == skStub: loadStub(e)
incl(e.flags, sfUsed)
addSon(result, newSymNode(e))
else:
addSon(result, newStrNode(nkStrLit, sub))
else:
# an empty '``' produces a single '`'
addSon(result, newStrNode(nkStrLit, $marker))
if c < 0: break
a = c + 1
else:
illFormedAstLocal(n, con.config)
result = newNode(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.sons[i] = c.semExpr(c, n1[i])
n.sons[1] = b
else:
n.sons[1] = c.semConstExpr(c, n1)
case n.sons[1].kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
n.sons[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.sons[1] = newIntNode(nkIntLit, unrollFactor)
else:
invalidPragma(c, n)
proc pragmaLine(c: PContext, n: PNode) =
if n.kind in nkPragmaCallKinds and n.len == 2:
n.sons[1] = c.semConstExpr(c, n.sons[1])
let a = n.sons[1]
if a.kind in {nkPar, nkTupleConstr}:
# unpack the tuple
var x = a.sons[0]
var y = a.sons[1]
if x.kind == nkExprColonExpr: x = x.sons[1]
if y.kind == nkExprColonExpr: y = y.sons[1]
if x.kind != nkStrLit:
localError(c.config, n.info, errStringLiteralExpected)
elif y.kind != nkIntLit:
localError(c.config, n.info, errIntLiteralExpected)
else:
# XXX this produces weird paths which are not properly resolved:
n.info.fileIndex = msgs.fileInfoIdx(c.config, 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) =
let it = n[i]
if it.kind notin nkPragmaCallKinds and it.len == 2: invalidPragma(c, n)
elif it[0].kind != nkIdent: invalidPragma(c, n)
elif it[1].kind != nkIdent: invalidPragma(c, n)
var userPragma = newSym(skTemplate, it[1].ident, nil, it.info, c.config.options)
userPragma.ast = newNode(nkPragma, n.info, n.sons[i+1..^1])
strTableAdd(c.userPragmas, userPragma)
proc pragmaRaisesOrTags(c: PContext, n: PNode) =
proc processExc(c: PContext, x: PNode) =
var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs)
if t.kind != tyObject:
localError(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.sons[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.sons[i] = c.semExpr(c, n.sons[i])
proc pragmaLocks(c: PContext, it: PNode): TLockLevel =
if it.kind notin nkPragmaCallKinds or it.len != 2:
invalidPragma(c, it)
else:
case it[1].kind
of nkStrLit, nkRStrLit, nkTripleStrLit:
if it[1].strVal == "unknown":
result = UnknownLockLevel
else:
localError(c.config, it[1].info, "invalid string literal for locks pragma (only allowed string is \"unknown\")")
else:
let x = expectIntLit(c, it)
if x < 0 or x > MaxLockLevel:
localError(c.config, it[1].info, "integer must be within 0.." & $MaxLockLevel)
else:
result = TLockLevel(x)
proc typeBorrow(c: PContext; sym: PSym, n: PNode) =
if n.kind in nkPragmaCallKinds and n.len == 2:
let it = n.sons[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)
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, dest, n[0].info, c.config.options)
incl(alias.flags, sfExported)
if sfCompilerProc in dest.flags: markCompilerProc(c, alias)
addInterfaceDecl(c, alias)
n.sons[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), nil, n.info,
c.config.options)
else:
result = qualifiedLookUp(c, n, {checkUndeclared})
proc semCustomPragma(c: PContext, n: PNode): PNode =
if n.kind == nkIdent:
result = newTree(nkCall, n)
elif n.kind == nkExprColonExpr:
# pragma: arg -> pragma(arg)
result = newTree(nkCall, n[0], n[1])
elif n.kind in nkPragmaCallKinds + {nkIdent}:
result = n
else:
invalidPragma(c, n)
return n
let r = c.semOverloadedCall(c, result, n, {skTemplate}, {})
if r.isNil or sfCustomPragma notin r[0].sym.flags:
invalidPragma(c, n)
else:
result = r
if n.kind == nkIdent:
result = result[0]
elif n.kind == nkExprColonExpr:
result.kind = n.kind # pragma(arg) -> pragma: arg
proc processExperimental(c: PContext; n: PNode; s: PSym) =
if not isTopLevel(c):
localError(c.config, n.info, "'experimental' pragma only valid as toplevel statement")
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:
c.features.incl parseEnum[Feature](n[1].strVal)
except ValueError:
localError(c.config, n[1].info, "unknown experimental feature")
else:
localError(c.config, n.info, errStringLiteralExpected)
proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
validPragmas: TSpecialWords): bool =
var it = n.sons[i]
var key = if it.kind in nkPragmaCallKinds and it.len > 1: it.sons[0] else: it
if key.kind == nkBracketExpr:
processNote(c, it)
return
elif key.kind notin nkIdentKinds:
n.sons[i] = semCustomPragma(c, it)
return
let ident = considerQuotedIdent(c, key)
var userPragma = strTableGet(c.userPragmas, ident)
if userPragma != nil:
# 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)
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:
case k
of wExportc:
makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info)
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 wImmediate:
if sym.kind in {skTemplate, skMacro}:
incl(sym.flags, sfImmediate)
incl(sym.flags, sfAllUntyped)
message(c.config, n.info, warnDeprecated, "use 'untyped' parameters instead; immediate")
else: invalidPragma(c, it)
of wDirty:
if sym.kind == skTemplate: incl(sym.flags, sfDirty)
else: invalidPragma(c, it)
of wImportCpp:
processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info)
of wImportObjC:
processImportObjC(c, sym, getOptionalStr(c, it, "$1"), it.info)
of wAlign:
if sym.typ == nil: invalidPragma(c, it)
var align = expectIntLit(c, it)
if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
localError(c.config, it.info, "power of two expected")
else:
sym.typ.align = align.int16
of wSize:
if sym.typ == nil: invalidPragma(c, it)
var size = expectIntLit(c, it)
if not isPowerOfTwo(size) or size <= 0 or size > 8:
localError(c.config, it.info, "power of two expected")
else:
sym.typ.size = size
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 wRegister:
noVal(c, it)
incl(sym.flags, sfRegister)
of wThreadVar:
noVal(c, it)
incl(sym.flags, {sfThread, sfGlobal})
of wDeadCodeElimUnused: discard # deprecated, dead code elim always on
of wNoForward: pragmaNoForward(c, it)
of wReorder: pragmaNoForward(c, it, sfReorder)
of wMagic: processMagic(c, it, sym)
of wCompileTime:
noVal(c, it)
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 == nil: sym.loc.r = rope(sym.name.s)
of wOverride:
sym.flags.incl sfOverriden
of wNosideeffect:
noVal(c, it)
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)
incl(sym.flags, sfNoReturn)
if sym.typ[0] != nil:
localError(c.config, sym.ast[paramsPos][0].info,
".noreturn with return type not allowed")
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 wProcVar:
noVal(c, it)
incl(sym.flags, sfProcvar)
of wExplain:
sym.flags.incl sfExplain
of wDeprecated:
if sym != nil and sym.kind in routineKinds:
if it.kind in nkPragmaCallKinds: discard getStrLitNode(c, it)
incl(sym.flags, sfDeprecated)
elif it.kind in nkPragmaCallKinds: deprecatedStmt(c, it)
elif sym != nil: incl(sym.flags, sfDeprecated)
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)
incl(sym.flags, sfProcvar)
if sym.typ != nil:
incl(sym.typ.flags, tfThread)
if sym.typ.callConv == ccClosure: sym.typ.callConv = ccDefault
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:
# This is subtle but correct: the error *statement* is only
# allowed for top level statements. Seems to be easier than
# distinguishing properly between
# ``proc p() {.error}`` and ``proc p() = {.error: "msg".}``
noVal(c, it)
incl(sym.flags, sfError)
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, errUser, expectStrLit(c, it))
of wDefine: processDefine(c, it)
of wUndef: processUndef(c, it)
of wCompile: processCompile(c, it)
of wLink: processCommonLink(c, it, linkNormal)
of wLinksys: processCommonLink(c, it, linkSys)
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 wBreakpoint: pragmaBreakpoint(c, it)
of wWatchPoint: pragmaWatchpoint(c, it)
of wPush:
processPush(c, n, i + 1)
result = true
of wPop: processPop(c, it)
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, wMovechecks, wCallconv, wDebugger, wProfiler,
wFloatchecks, wNanChecks, wInfChecks, wPatterns:
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)
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 wUnchecked:
noVal(c, it)
if sym.typ == nil: invalidPragma(c, it)
else: incl(sym.typ.flags, tfUncheckedArray)
of wUnion:
noVal(c, it)
if sym.typ == nil: invalidPragma(c, it)
else: incl(sym.typ.flags, tfUnion)
of wRequiresInit:
noVal(c, it)
if sym.typ == nil: invalidPragma(c, it)
else: incl(sym.typ.flags, tfNeedsInit)
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: pragmaRaisesOrTags(c, it)
of wLocks:
if sym == nil: pragmaLockStmt(c, it)
elif sym.typ == nil: invalidPragma(c, it)
else: sym.typ.lockLevel = pragmaLocks(c, it)
of wBitsize:
if sym == nil or sym.kind != skField:
invalidPragma(c, it)
else:
sym.bitsize = expectIntLit(c, it)
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 wInjectStmt:
if it.kind notin nkPragmaCallKinds or it.len != 2:
localError(c.config, it.info, "expression expected")
else:
it.sons[1] = c.semExpr(c, it.sons[1])
of wExperimental:
processExperimental(c, it, sym)
of wThis:
if it.kind in nkPragmaCallKinds and it.len == 2:
c.selfName = considerQuotedIdent(c, it[1])
message(c.config, n.info, warnDeprecated, "the '.this' pragma")
elif it.kind == nkIdent or it.len == 1:
c.selfName = getIdent(c.cache, "self")
message(c.config, n.info, warnDeprecated, "the '.this' pragma")
else:
localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments")
of wNoRewrite:
noVal(c, it)
of wBase:
noVal(c, it)
sym.flags.incl sfBase
of wIntDefine:
sym.magic = mIntDefine
of wStrDefine:
sym.magic = mStrDefine
of wUsed:
noVal(c, it)
if sym == nil: invalidPragma(c, it)
else: sym.flags.incl sfUsed
of wLiftLocals: discard
else: invalidPragma(c, it)
else:
n.sons[i] = semCustomPragma(c, it)
proc implicitPragmas*(c: PContext, sym: PSym, n: PNode,
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, n.info)
var i = 0
while i < o.len():
if singlePragma(c, sym, o, i, validPragmas):
internalError(c.config, n.info, "implicitPragmas")
inc i
popInfoContext(c.config)
if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
localError(c.config, n.info, ".dynlib requires .exportc")
var lib = c.optionStack[^1].dynlib
if {lfDynamicLib, lfHeader} * sym.loc.flags == {} and
sfImportc in sym.flags and lib != nil:
incl(sym.loc.flags, lfDynamicLib)
addToLib(lib, sym)
if sym.loc.r == nil: sym.loc.r = rope(sym.name.s)
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) =
if n == nil: return
var i = 0
while i < n.len:
if singlePragma(c, sym, n, i, validPragmas): break
inc i
proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
if n == nil: return
pragmaRec(c, sym, n, validPragmas)
implicitPragmas(c, sym, n, validPragmas)