12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022 |
- #
- #
- # 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,
- rodread, types, lookups
- const
- FirstCallConv* = wNimcall
- LastCallConv* = wNoconv
- const
- procPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
- wMagic, wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
- wCompilerproc, wProcVar, wDeprecated, wVarargs, wCompileTime, wMerge,
- wBorrow, wExtern, wImportCompilerProc, wThread, wImportCpp, wImportObjC,
- wAsmNoStackFrame, wError, wDiscardable, wNoInit, wDestructor, wCodegenDecl,
- wGensym, wInject, wRaises, wTags, wLocks, wDelegator, wGcSafe,
- wOverride, wConstructor, wExportNims, wUsed}
- converterPragmas* = procPragmas
- methodPragmas* = procPragmas+{wBase}-{wImportCpp}
- templatePragmas* = {wImmediate, wDeprecated, wError, wGensym, wInject, wDirty,
- wDelegator, wExportNims, wUsed}
- macroPragmas* = {FirstCallConv..LastCallConv, wImmediate, wImportc, wExportc,
- wNodecl, wMagic, wNosideeffect, wCompilerproc, wDeprecated, wExtern,
- wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wDelegator,
- wExportNims, wUsed}
- iteratorPragmas* = {FirstCallConv..LastCallConv, wNosideeffect, wSideeffect,
- wImportc, wExportc, wNodecl, wMagic, wDeprecated, wBorrow, wExtern,
- wImportCpp, wImportObjC, wError, wDiscardable, wGensym, wInject, wRaises,
- wTags, wLocks, wGcSafe, wExportNims, wUsed}
- exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe}
- stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangechecks,
- wBoundchecks, wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
- wLinedir, wStacktrace, wLinetrace, wOptimization, wHint, wWarning, wError,
- wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop,
- wBreakpoint, wWatchPoint, wPassl, wPassc, wDeadCodeElim, wDeprecated,
- wFloatchecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll,
- wLinearScanEnd, wPatterns, wEffects, wNoForward, wReorder, wComputedGoto,
- wInjectStmt, wDeprecated, wExperimental, wThis}
- lambdaPragmas* = {FirstCallConv..LastCallConv, wImportc, wExportc, wNodecl,
- wNosideeffect, wSideeffect, wNoreturn, wDynlib, wHeader,
- wDeprecated, wExtern, wThread, wImportCpp, wImportObjC, wAsmNoStackFrame,
- wRaises, wLocks, wTags, wGcSafe}
- typePragmas* = {wImportc, wExportc, wDeprecated, wMagic, wAcyclic, wNodecl,
- wPure, wHeader, wCompilerproc, wFinal, wSize, wExtern, wShallow,
- wImportCpp, wImportObjC, wError, wIncompleteStruct, wByCopy, wByRef,
- wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked,
- wBorrow, wGcSafe, wExportNims, wPartial, wUsed, wExplain}
- fieldPragmas* = {wImportc, wExportc, wDeprecated, wExtern,
- wImportCpp, wImportObjC, wError, wGuard, wBitsize, wUsed}
- varPragmas* = {wImportc, wExportc, wVolatile, wRegister, wThreadVar, wNodecl,
- wMagic, wHeader, wDeprecated, wCompilerproc, wDynlib, wExtern,
- wImportCpp, wImportObjC, wError, wNoInit, wCompileTime, wGlobal,
- wGensym, wInject, wCodegenDecl, wGuard, wGoto, wExportNims, wUsed}
- constPragmas* = {wImportc, wExportc, wHeader, wDeprecated, wMagic, wNodecl,
- wExtern, wImportCpp, wImportObjC, wError, wGensym, wInject, wExportNims,
- wIntDefine, wStrDefine, wUsed}
- letPragmas* = varPragmas
- procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNosideeffect,
- wThread, wRaises, wLocks, wTags, wGcSafe}
- allRoutinePragmas* = methodPragmas + iteratorPragmas + lambdaPragmas
- proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords)
- # implementation
- proc invalidPragma*(n: PNode) =
- localError(n.info, errInvalidPragmaX, renderTree(n, {renderNoComments}))
- proc pragmaAsm*(c: PContext, n: PNode): char =
- result = '\0'
- if n != nil:
- for i in countup(0, sonsLen(n) - 1):
- let it = n.sons[i]
- if it.kind == nkExprColonExpr and it.sons[0].kind == nkIdent:
- case whichKeyword(it.sons[0].ident)
- of wSubsChar:
- if it.sons[1].kind == nkCharLit: result = chr(int(it.sons[1].intVal))
- else: invalidPragma(it)
- else: invalidPragma(it)
- else:
- invalidPragma(it)
- proc setExternName(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(info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)")
- if gCmd == cmdPretty and '$' notin extname:
- # note that '{.importc.}' is transformed into '{.importc: "$1".}'
- s.loc.flags.incl(lfFullExternalName)
- proc makeExternImport(s: PSym, extname: string, info: TLineInfo) =
- setExternName(s, extname, info)
- incl(s.flags, sfImportc)
- excl(s.flags, sfForward)
- proc makeExternExport(s: PSym, extname: string, info: TLineInfo) =
- setExternName(s, extname, info)
- incl(s.flags, sfExportc)
- proc processImportCompilerProc(s: PSym, extname: string, info: TLineInfo) =
- setExternName(s, extname, info)
- incl(s.flags, sfImportc)
- excl(s.flags, sfForward)
- incl(s.loc.flags, lfImportCompilerProc)
- proc processImportCpp(s: PSym, extname: string, info: TLineInfo) =
- setExternName(s, extname, info)
- incl(s.flags, sfImportc)
- incl(s.flags, sfInfixCall)
- excl(s.flags, sfForward)
- if gCmd == cmdCompileToC:
- let m = s.getModule()
- incl(m.flags, sfCompileToCpp)
- extccomp.gMixedMode = true
- proc processImportObjC(s: PSym, extname: string, info: TLineInfo) =
- setExternName(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(n: PNode): PNode {.noinline.} =
- result = newNodeIT(nkStrLit, n.info, getSysType(tyString))
- result.strVal = ""
- proc getStrLitNode(c: PContext, n: PNode): PNode =
- if n.kind != nkExprColonExpr:
- localError(n.info, errStringLiteralExpected)
- # error correction:
- result = newEmptyStrNode(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(n.info, errStringLiteralExpected)
- # error correction:
- result = newEmptyStrNode(n)
- proc expectStrLit(c: PContext, n: PNode): string =
- result = getStrLitNode(c, n).strVal
- proc expectIntLit(c: PContext, n: PNode): int =
- if n.kind != nkExprColonExpr:
- localError(n.info, errIntLiteralExpected)
- else:
- n.sons[1] = c.semConstExpr(c, n.sons[1])
- case n.sons[1].kind
- of nkIntLit..nkInt64Lit: result = int(n.sons[1].intVal)
- else: localError(n.info, errIntLiteralExpected)
- proc getOptionalStr(c: PContext, n: PNode, defaultStr: string): string =
- if n.kind == nkExprColonExpr: 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 != nkExprColonExpr:
- localError(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(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 == nkExprColonExpr:
- let x = c.semConstBoolExpr(c, n.sons[1])
- n.sons[1] = x
- if x.kind == nkIntLit: return x.intVal != 0
- localError(n.info, errOnOrOffExpected)
- proc onOff(c: PContext, n: PNode, op: TOptions) =
- if isTurnedOn(c, n): gOptions = gOptions + op
- else: gOptions = gOptions - op
- proc pragmaDeadCodeElim(c: PContext, n: PNode) =
- if isTurnedOn(c, n): incl(c.module.flags, sfDeadCodeElim)
- else: excl(c.module.flags, sfDeadCodeElim)
- 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 == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
- var sw = whichKeyword(n.sons[1].ident)
- case sw
- of FirstCallConv..LastCallConv:
- c.optionStack[^1].defaultCC = wordToCallConv(sw)
- else: localError(n.info, errCallConvExpected)
- else:
- localError(n.info, errCallConvExpected)
- 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(path.strVal)
- proc expectDynlibNode(c: PContext, n: PNode): PNode =
- if n.kind != nkExprColonExpr:
- localError(n.info, errStringLiteralExpected)
- # error correction:
- result = newEmptyStrNode(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(n.info, errStringLiteralExpected)
- result = newEmptyStrNode(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 == nkExprColonExpr:
- 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 == nkExprColonExpr) and (sonsLen(n) == 2) and
- (n.sons[0].kind == nkBracketExpr) and
- (n.sons[0].sons.len == 2) and
- (n.sons[0].sons[1].kind == nkIdent) and
- (n.sons[0].sons[0].kind == nkIdent):
- #and (n.sons[1].kind == nkIdent):
- var nk: TNoteKind
- case whichKeyword(n.sons[0].sons[0].ident)
- of wHint:
- var x = findStr(msgs.HintsToStr, n.sons[0].sons[1].ident.s)
- if x >= 0: nk = TNoteKind(x + ord(hintMin))
- else: invalidPragma(n); return
- of wWarning:
- var x = findStr(msgs.WarningsToStr, n.sons[0].sons[1].ident.s)
- if x >= 0: nk = TNoteKind(x + ord(warnMin))
- else: invalidPragma(n); return
- else:
- invalidPragma(n)
- return
- let x = c.semConstBoolExpr(c, n.sons[1])
- n.sons[1] = x
- if x.kind == nkIntLit and x.intVal != 0: incl(gNotes, nk)
- else: excl(gNotes, nk)
- else:
- invalidPragma(n)
- proc processOption(c: PContext, n: PNode): bool =
- if n.kind != nkExprColonExpr: result = true
- elif n.sons[0].kind == nkBracketExpr: processNote(c, n)
- elif n.sons[0].kind != nkIdent: result = true
- else:
- var sw = whichKeyword(n.sons[0].ident)
- case sw
- of wChecks: onOff(c, n, ChecksOptions)
- of wObjChecks: onOff(c, n, {optObjCheck})
- of wFieldChecks: onOff(c, n, {optFieldCheck})
- of wRangechecks: onOff(c, n, {optRangeCheck})
- of wBoundchecks: onOff(c, n, {optBoundsCheck})
- of wOverflowchecks: onOff(c, n, {optOverflowCheck})
- of wNilchecks: onOff(c, n, {optNilCheck})
- of wFloatchecks: onOff(c, n, {optNaNCheck, optInfCheck})
- of wNanChecks: onOff(c, n, {optNaNCheck})
- of wInfChecks: onOff(c, n, {optInfCheck})
- of wAssertions: onOff(c, n, {optAssert})
- of wWarnings: onOff(c, n, {optWarns})
- of wHints: onOff(c, n, {optHints})
- of wCallconv: processCallConv(c, n)
- of wLinedir: onOff(c, n, {optLineDir})
- of wStacktrace: onOff(c, n, {optStackTrace})
- of wLinetrace: onOff(c, n, {optLineTrace})
- of wDebugger: onOff(c, n, {optEndb})
- of wProfiler: onOff(c, n, {optProfiler, optMemTracker})
- of wMemTracker: onOff(c, n, {optMemTracker})
- of wByRef: onOff(c, n, {optByRef})
- of wDynlib: processDynLib(c, n, nil)
- of wOptimization:
- if n.sons[1].kind != nkIdent:
- invalidPragma(n)
- else:
- case n.sons[1].ident.s.normalize
- of "speed":
- incl(gOptions, optOptimizeSpeed)
- excl(gOptions, optOptimizeSize)
- of "size":
- excl(gOptions, optOptimizeSpeed)
- incl(gOptions, optOptimizeSize)
- of "none":
- excl(gOptions, optOptimizeSpeed)
- excl(gOptions, optOptimizeSize)
- else: localError(n.info, errNoneSpeedOrSizeExpected)
- of wImplicitStatic: onOff(c, n, {optImplicitStatic})
- of wPatterns: onOff(c, n, {optPatterns})
- else: result = true
- proc processPush(c: PContext, n: PNode, start: int) =
- if n.sons[start-1].kind == nkExprColonExpr:
- localError(n.info, errGenerated, "':' after 'push' not supported")
- var x = newOptionEntry()
- var y = c.optionStack[^1]
- x.options = gOptions
- x.defaultCC = y.defaultCC
- x.dynlib = y.dynlib
- x.notes = gNotes
- c.optionStack.add(x)
- for i in countup(start, sonsLen(n) - 1):
- if processOption(c, n.sons[i]):
- # simply store it somewhere:
- if x.otherPragmas.isNil:
- x.otherPragmas = newNodeI(nkPragma, n.info)
- x.otherPragmas.add n.sons[i]
- #localError(n.info, errOptionExpected)
- proc processPop(c: PContext, n: PNode) =
- if c.optionStack.len <= 1:
- localError(n.info, errAtPopWithoutPush)
- else:
- gOptions = c.optionStack[^1].options
- gNotes = c.optionStack[^1].notes
- c.optionStack.setLen(c.optionStack.len - 1)
- proc processDefine(c: PContext, n: PNode) =
- if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
- defineSymbol(n.sons[1].ident.s)
- message(n.info, warnDeprecated, "define")
- else:
- invalidPragma(n)
- proc processUndef(c: PContext, n: PNode) =
- if (n.kind == nkExprColonExpr) and (n.sons[1].kind == nkIdent):
- undefSymbol(n.sons[1].ident.s)
- message(n.info, warnDeprecated, "undef")
- else:
- invalidPragma(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(n.info.toFullPath) / s
- if not fileExists(result):
- if isAbsolute(s): result = s
- else:
- result = findFile(s)
- if result.len == 0: result = s
- proc processCompile(c: PContext, n: PNode) =
- proc getStrLit(c: PContext, n: PNode; i: int): string =
- n.sons[i] = c.semConstExpr(c, n.sons[i])
- case n.sons[i].kind
- of nkStrLit, nkRStrLit, nkTripleStrLit:
- shallowCopy(result, n.sons[i].strVal)
- else:
- localError(n.info, errStringLiteralExpected)
- result = ""
- let it = if n.kind == nkExprColonExpr: n.sons[1] else: n
- if it.kind == nkPar and it.len == 2:
- let s = getStrLit(c, it, 0)
- let dest = getStrLit(c, it, 1)
- var found = parentDir(n.info.toFullPath) / s
- for f in os.walkFiles(found):
- let nameOnly = extractFilename(f)
- var cf = Cfile(cname: f,
- obj: completeCFilePath(dest % nameOnly),
- flags: {CfileFlag.External})
- extccomp.addExternalFileToCompile(cf)
- else:
- let s = expectStrLit(c, n)
- var found = parentDir(n.info.toFullPath) / s
- if not fileExists(found):
- if isAbsolute(s): found = s
- else:
- found = findFile(s)
- if found.len == 0: found = s
- extccomp.addExternalFileToCompile(found)
- proc processCommonLink(c: PContext, n: PNode, feature: TLinkFeature) =
- let found = relativeFile(c, n, CC[cCompiler].objExt)
- case feature
- of linkNormal: extccomp.addExternalFileToLink(found)
- of linkSys:
- extccomp.addExternalFileToLink(libpath / completeCFilePath(found, false))
- else: internalError(n.info, "processCommonLink")
- proc pragmaBreakpoint(c: PContext, n: PNode) =
- discard getOptionalStr(c, n, "")
- proc pragmaWatchpoint(c: PContext, n: PNode) =
- if n.kind == nkExprColonExpr:
- n.sons[1] = c.semExpr(c, n.sons[1])
- else:
- invalidPragma(n)
- 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(n.info, errEmptyAsm)
- 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(sub))
- if e != nil:
- 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)
- result = newNode(nkAsmStmt, n.info)
- proc pragmaEmit(c: PContext, n: PNode) =
- if n.kind != nkExprColonExpr:
- localError(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(n.info, errStringLiteralExpected)
- proc noVal(n: PNode) =
- if n.kind == nkExprColonExpr: invalidPragma(n)
- proc pragmaUnroll(c: PContext, n: PNode) =
- if c.p.nestedLoopCounter <= 0:
- invalidPragma(n)
- elif n.kind == nkExprColonExpr:
- var unrollFactor = expectIntLit(c, n)
- if unrollFactor <% 32:
- n.sons[1] = newIntNode(nkIntLit, unrollFactor)
- else:
- invalidPragma(n)
- proc pragmaLine(c: PContext, n: PNode) =
- if n.kind == nkExprColonExpr:
- n.sons[1] = c.semConstExpr(c, n.sons[1])
- let a = n.sons[1]
- if a.kind == nkPar:
- var x = a.sons[0]
- var y = a.sons[1]
- if x.kind == nkExprColonExpr: x = x.sons[1]
- if y.kind == nkExprColonExpr: y = y.sons[1]
- if x.kind != nkStrLit:
- localError(n.info, errStringLiteralExpected)
- elif y.kind != nkIntLit:
- localError(n.info, errIntLiteralExpected)
- else:
- # XXX this produces weird paths which are not properly resolved:
- n.info.fileIndex = msgs.fileInfoIdx(x.strVal)
- n.info.line = int16(y.intVal)
- else:
- localError(n.info, errXExpected, "tuple")
- else:
- # sensible default:
- n.info = getInfoContext(-1)
- proc processPragma(c: PContext, n: PNode, i: int) =
- var it = n.sons[i]
- if it.kind != nkExprColonExpr: invalidPragma(n)
- elif it.sons[0].kind != nkIdent: invalidPragma(n)
- elif it.sons[1].kind != nkIdent: invalidPragma(n)
- var userPragma = newSym(skTemplate, it.sons[1].ident, nil, it.info)
- var body = newNodeI(nkPragma, n.info)
- for j in i+1 .. sonsLen(n)-1: addSon(body, n.sons[j])
- userPragma.ast = body
- strTableAdd(c.userPragmas, userPragma)
- proc pragmaRaisesOrTags(c: PContext, n: PNode) =
- proc processExc(c: PContext, x: PNode) =
- var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs)
- if t.kind != tyObject:
- localError(x.info, errGenerated, "invalid type for raises/tags list")
- x.typ = t
- if n.kind == nkExprColonExpr:
- 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(n)
- proc pragmaLockStmt(c: PContext; it: PNode) =
- if it.kind != nkExprColonExpr:
- invalidPragma(it)
- else:
- let n = it[1]
- if n.kind != nkBracket:
- localError(n.info, errGenerated, "locks pragma takes a list of expressions")
- else:
- for i in 0 .. <n.len:
- n.sons[i] = c.semExpr(c, n.sons[i])
- proc pragmaLocks(c: PContext, it: PNode): TLockLevel =
- if it.kind != nkExprColonExpr:
- invalidPragma(it)
- else:
- case it[1].kind
- of nkStrLit, nkRStrLit, nkTripleStrLit:
- if it[1].strVal == "unknown":
- result = UnknownLockLevel
- else:
- localError(it[1].info, "invalid string literal for locks pragma (only allowed string is \"unknown\")")
- else:
- let x = expectIntLit(c, it)
- if x < 0 or x > MaxLockLevel:
- localError(it[1].info, "integer must be within 0.." & $MaxLockLevel)
- else:
- result = TLockLevel(x)
- proc typeBorrow(sym: PSym, n: PNode) =
- if n.kind == nkExprColonExpr:
- let it = n.sons[1]
- if it.kind != nkAccQuoted:
- localError(n.info, "a type can only borrow `.` for now")
- incl(sym.typ.flags, tfBorrowDot)
- proc markCompilerProc(s: PSym) =
- # minor hack ahead: FlowVar is the only generic .compilerProc type which
- # should not have an external name set:
- if s.kind != skType or s.name.s != "FlowVar":
- makeExternExport(s, "$1", s.info)
- incl(s.flags, sfCompilerProc)
- incl(s.flags, sfUsed)
- registerCompilerProc(s)
- proc deprecatedStmt(c: PContext; pragma: PNode) =
- let pragma = pragma[1]
- if pragma.kind != nkBracket:
- localError(pragma.info, "list of key:value pairs expected"); return
- for n in pragma:
- if n.kind in {nkExprColonExpr, nkExprEqExpr}:
- let dest = qualifiedLookUp(c, n[1], {checkUndeclared})
- assert dest != nil
- let src = considerQuotedIdent(n[0])
- let alias = newSym(skAlias, src, dest, n[0].info)
- incl(alias.flags, sfExported)
- if sfCompilerProc in dest.flags: markCompilerProc(alias)
- addInterfaceDecl(c, alias)
- n.sons[1] = newSymNode(dest)
- else:
- localError(n.info, "key:value pair expected")
- proc pragmaGuard(c: PContext; it: PNode; kind: TSymKind): PSym =
- if it.kind != nkExprColonExpr:
- invalidPragma(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(n), nil, n.info)
- else:
- result = qualifiedLookUp(c, n, {checkUndeclared})
- proc singlePragma(c: PContext, sym: PSym, n: PNode, i: int,
- validPragmas: TSpecialWords): bool =
- var it = n.sons[i]
- var key = if it.kind == nkExprColonExpr: it.sons[0] else: it
- if key.kind == nkBracketExpr:
- processNote(c, it)
- return
- let ident = considerQuotedIdent(key)
- var userPragma = strTableGet(c.userPragmas, ident)
- if userPragma != nil:
- inc c.instCounter
- if c.instCounter > 100:
- globalError(it.info, errRecursiveDependencyX, userPragma.name.s)
- pragma(c, sym, userPragma.ast, validPragmas)
- # ensure the pragma is also remember for generic instantiations in other
- # modules:
- n.sons[i] = userPragma.ast
- dec c.instCounter
- else:
- var k = whichKeyword(ident)
- if k in validPragmas:
- case k
- of wExportc:
- makeExternExport(sym, getOptionalStr(c, it, "$1"), it.info)
- incl(sym.flags, sfUsed) # avoid wrong hints
- of wImportc:
- let name = getOptionalStr(c, it, "$1")
- cppDefine(c.graph.config, name)
- makeExternImport(sym, name, it.info)
- of wImportCompilerProc:
- let name = getOptionalStr(c, it, "$1")
- cppDefine(c.graph.config, name)
- processImportCompilerProc(sym, name, it.info)
- of wExtern: setExternName(sym, expectStrLit(c, it), it.info)
- of wImmediate:
- if sym.kind in {skTemplate, skMacro}:
- incl(sym.flags, sfImmediate)
- incl(sym.flags, sfAllUntyped)
- message(n.info, warnDeprecated, "use 'untyped' parameters instead; immediate")
- else: invalidPragma(it)
- of wDirty:
- if sym.kind == skTemplate: incl(sym.flags, sfDirty)
- else: invalidPragma(it)
- of wImportCpp:
- processImportCpp(sym, getOptionalStr(c, it, "$1"), it.info)
- of wImportObjC:
- processImportObjC(sym, getOptionalStr(c, it, "$1"), it.info)
- of wAlign:
- if sym.typ == nil: invalidPragma(it)
- var align = expectIntLit(c, it)
- if (not isPowerOfTwo(align) and align != 0) or align >% high(int16):
- localError(it.info, errPowerOfTwoExpected)
- else:
- sym.typ.align = align.int16
- of wSize:
- if sym.typ == nil: invalidPragma(it)
- var size = expectIntLit(c, it)
- if not isPowerOfTwo(size) or size <= 0 or size > 8:
- localError(it.info, errPowerOfTwoExpected)
- else:
- sym.typ.size = size
- of wNodecl:
- noVal(it)
- incl(sym.loc.flags, lfNoDecl)
- of wPure, wAsmNoStackFrame:
- noVal(it)
- if sym != nil:
- if k == wPure and sym.kind in routineKinds: invalidPragma(it)
- else: incl(sym.flags, sfPure)
- of wVolatile:
- noVal(it)
- incl(sym.flags, sfVolatile)
- of wRegister:
- noVal(it)
- incl(sym.flags, sfRegister)
- of wThreadVar:
- noVal(it)
- incl(sym.flags, sfThread)
- of wDeadCodeElim: pragmaDeadCodeElim(c, it)
- of wNoForward: pragmaNoForward(c, it)
- of wReorder: pragmaNoForward(c, it, sfReorder)
- of wMagic: processMagic(c, it, sym)
- of wCompileTime:
- noVal(it)
- incl(sym.flags, sfCompileTime)
- incl(sym.loc.flags, lfNoDecl)
- of wGlobal:
- noVal(it)
- incl(sym.flags, sfGlobal)
- incl(sym.flags, sfPure)
- of wMerge:
- # only supported for backwards compat, doesn't do anything anymore
- noVal(it)
- of wConstructor:
- noVal(it)
- incl(sym.flags, sfConstructor)
- 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 wDestructor:
- sym.flags.incl sfOverriden
- if sym.name.s.normalize != "destroy":
- localError(n.info, errGenerated, "destructor has to be named 'destroy'")
- of wOverride:
- sym.flags.incl sfOverriden
- of wNosideeffect:
- noVal(it)
- incl(sym.flags, sfNoSideEffect)
- if sym.typ != nil: incl(sym.typ.flags, tfNoSideEffect)
- of wSideeffect:
- noVal(it)
- incl(sym.flags, sfSideEffect)
- of wNoreturn:
- noVal(it)
- incl(sym.flags, sfNoReturn)
- of wDynlib:
- processDynLib(c, it, sym)
- of wCompilerproc:
- noVal(it) # compilerproc may not get a string!
- cppDefine(c.graph.config, sym.name.s)
- if sfFromGeneric notin sym.flags: markCompilerProc(sym)
- of wProcVar:
- noVal(it)
- incl(sym.flags, sfProcvar)
- of wExplain:
- sym.flags.incl sfExplain
- of wDeprecated:
- if it.kind == nkExprColonExpr: deprecatedStmt(c, it)
- elif sym != nil: incl(sym.flags, sfDeprecated)
- else: incl(c.module.flags, sfDeprecated)
- of wVarargs:
- noVal(it)
- if sym.typ == nil: invalidPragma(it)
- else: incl(sym.typ.flags, tfVarargs)
- of wBorrow:
- if sym.kind == skType:
- typeBorrow(sym, it)
- else:
- noVal(it)
- incl(sym.flags, sfBorrow)
- of wFinal:
- noVal(it)
- if sym.typ == nil: invalidPragma(it)
- else: incl(sym.typ.flags, tfFinal)
- of wInheritable:
- noVal(it)
- if sym.typ == nil or tfFinal in sym.typ.flags: invalidPragma(it)
- else: incl(sym.typ.flags, tfInheritable)
- of wAcyclic:
- noVal(it)
- if sym.typ == nil: invalidPragma(it)
- else: incl(sym.typ.flags, tfAcyclic)
- of wShallow:
- noVal(it)
- if sym.typ == nil: invalidPragma(it)
- else: incl(sym.typ.flags, tfShallow)
- of wThread:
- noVal(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(it)
- if sym != nil:
- if sym.kind != skType: incl(sym.flags, sfThread)
- if sym.typ != nil: incl(sym.typ.flags, tfGcSafe)
- else: invalidPragma(it)
- else:
- discard "no checking if used as a code block"
- of wPacked:
- noVal(it)
- if sym.typ == nil: invalidPragma(it)
- else: incl(sym.typ.flags, tfPacked)
- of wHint: message(it.info, hintUser, expectStrLit(c, it))
- of wWarning: message(it.info, warnUser, expectStrLit(c, it))
- of 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(it)
- incl(sym.flags, sfError)
- else:
- localError(it.info, errUser, expectStrLit(c, it))
- of wFatal: fatal(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: extccomp.addLinkOption(expectStrLit(c, it))
- of wPassc: extccomp.addCompileOption(expectStrLit(c, it))
- 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:
- processPragma(c, n, i)
- result = true
- of wDiscardable:
- noVal(it)
- if sym != nil: incl(sym.flags, sfDiscardable)
- of wNoInit:
- noVal(it)
- if sym != nil: incl(sym.flags, sfNoInit)
- of wCodegenDecl: processCodegenDecl(c, it, sym)
- of wChecks, wObjChecks, wFieldChecks, wRangechecks, wBoundchecks,
- wOverflowchecks, wNilchecks, wAssertions, wWarnings, wHints,
- wLinedir, wStacktrace, wLinetrace, wOptimization,
- wCallconv,
- wDebugger, wProfiler, wFloatchecks, wNanChecks, wInfChecks,
- wPatterns:
- if processOption(c, it):
- # calling conventions (boring...):
- localError(it.info, errOptionExpected)
- of FirstCallConv..LastCallConv:
- assert(sym != nil)
- if sym.typ == nil: invalidPragma(it)
- else: sym.typ.callConv = wordToCallConv(k)
- of wEmit: pragmaEmit(c, it)
- of wUnroll: pragmaUnroll(c, it)
- of wLinearScanEnd, wComputedGoto: noVal(it)
- of wEffects:
- # is later processed in effect analysis:
- noVal(it)
- of wIncompleteStruct:
- noVal(it)
- if sym.typ == nil: invalidPragma(it)
- else: incl(sym.typ.flags, tfIncompleteStruct)
- of wUnchecked:
- noVal(it)
- if sym.typ == nil: invalidPragma(it)
- else: incl(sym.typ.flags, tfUncheckedArray)
- of wUnion:
- noVal(it)
- if sym.typ == nil: invalidPragma(it)
- else: incl(sym.typ.flags, tfUnion)
- of wRequiresInit:
- noVal(it)
- if sym.typ == nil: invalidPragma(it)
- else: incl(sym.typ.flags, tfNeedsInit)
- of wByRef:
- noVal(it)
- if sym == nil or sym.typ == nil:
- if processOption(c, it): localError(it.info, errOptionExpected)
- else:
- incl(sym.typ.flags, tfByRef)
- of wByCopy:
- noVal(it)
- if sym.kind != skType or sym.typ == nil: invalidPragma(it)
- else: incl(sym.typ.flags, tfByCopy)
- of wPartial:
- noVal(it)
- if sym.kind != skType or sym.typ == nil: invalidPragma(it)
- else:
- incl(sym.typ.flags, tfPartial)
- # .partial types can only work with dead code elimination
- # to prevent the codegen from doing anything before we compiled
- # the whole program:
- incl gGlobalOptions, optDeadCodeElim
- of wInject, wGensym:
- # We check for errors, but do nothing with these pragmas otherwise
- # as they are handled directly in 'evalTemplate'.
- noVal(it)
- if sym == nil: invalidPragma(it)
- of wLine: pragmaLine(c, it)
- of wRaises, wTags: pragmaRaisesOrTags(c, it)
- of wLocks:
- if sym == nil: pragmaLockStmt(c, it)
- elif sym.typ == nil: invalidPragma(it)
- else: sym.typ.lockLevel = pragmaLocks(c, it)
- of wBitsize:
- if sym == nil or sym.kind != skField or it.kind != nkExprColonExpr:
- invalidPragma(it)
- else:
- sym.bitsize = expectIntLit(c, it)
- of wGuard:
- if sym == nil or sym.kind notin {skVar, skLet, skField}:
- invalidPragma(it)
- else:
- sym.guard = pragmaGuard(c, it, sym.kind)
- of wGoto:
- if sym == nil or sym.kind notin {skVar, skLet}:
- invalidPragma(it)
- else:
- sym.flags.incl sfGoto
- of wExportNims:
- if sym == nil: invalidPragma(it)
- else: magicsys.registerNimScriptSymbol(sym)
- of wInjectStmt:
- if it.kind != nkExprColonExpr:
- localError(it.info, errExprExpected)
- else:
- it.sons[1] = c.semExpr(c, it.sons[1])
- of wExperimental:
- noVal(it)
- if isTopLevel(c):
- c.module.flags.incl sfExperimental
- else:
- localError(it.info, "'experimental' pragma only valid as toplevel statement")
- of wThis:
- if it.kind == nkExprColonExpr:
- c.selfName = considerQuotedIdent(it[1])
- else:
- c.selfName = getIdent("self")
- of wNoRewrite:
- noVal(it)
- of wBase:
- noVal(it)
- sym.flags.incl sfBase
- of wIntDefine:
- sym.magic = mIntDefine
- of wStrDefine:
- sym.magic = mStrDefine
- of wUsed:
- noVal(it)
- if sym == nil: invalidPragma(it)
- else: sym.flags.incl sfUsed
- else: invalidPragma(it)
- else: invalidPragma(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(n.info)
- for i in countup(0, sonsLen(o) - 1):
- if singlePragma(c, sym, o, i, validPragmas):
- internalError(n.info, "implicitPragmas")
- popInfoContext()
- if lfExportLib in sym.loc.flags and sfExportc notin sym.flags:
- localError(n.info, errDynlibRequiresExportc)
- 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 or n.sons == nil:
- return false
- for p in n.sons:
- var key = if p.kind == nkExprColonExpr: 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
- for i in countup(0, sonsLen(n) - 1):
- if n.sons[i].kind == nkPragma: pragmaRec(c, sym, n.sons[i], validPragmas)
- elif singlePragma(c, sym, n, i, validPragmas): break
- proc pragma(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords) =
- if n == nil: return
- pragmaRec(c, sym, n, validPragmas)
- implicitPragmas(c, sym, n, validPragmas)
|