diff options
author | Andreas Rumpf <rumpf_a@web.de> | 2020-03-18 14:25:10 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-18 14:25:10 +0100 |
commit | 3f29911a9496d82c355b84e23d91e992c3ddfce5 (patch) | |
tree | e7636e4f073b40c74d873e1f203fc014497f0360 /compiler | |
parent | ed263e174e7f9a7bce1863dc57979c1a752d2e66 (diff) | |
download | Nim-3f29911a9496d82c355b84e23d91e992c3ddfce5.tar.gz |
new feature: --staticBoundChecks:on to enforce static array index checking (#10965)
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/astalgo.nim | 1 | ||||
-rw-r--r-- | compiler/commands.nim | 2 | ||||
-rw-r--r-- | compiler/guards.nim | 47 | ||||
-rw-r--r-- | compiler/lineinfos.nim | 7 | ||||
-rw-r--r-- | compiler/options.nim | 4 | ||||
-rw-r--r-- | compiler/pragmas.nim | 7 | ||||
-rw-r--r-- | compiler/semparallel.nim | 16 | ||||
-rw-r--r-- | compiler/sempass2.nim | 67 | ||||
-rw-r--r-- | compiler/trees.nim | 1 | ||||
-rw-r--r-- | compiler/types.nim | 2 | ||||
-rw-r--r-- | compiler/wordrecg.nim | 4 |
11 files changed, 129 insertions, 29 deletions
diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index fd95b3780..968918ba9 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -182,6 +182,7 @@ proc getModule*(s: PSym): PSym = assert((result.kind == skModule) or (result.owner != result)) while result != nil and result.kind != skModule: result = result.owner +proc fromSystem*(op: PSym): bool {.inline.} = sfSystemModule in getModule(op).flags proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym = for i in start..<list.len: if list[i].kind == nkSym: diff --git a/compiler/commands.nim b/compiler/commands.nim index 62d4d75dd..bc6fdde8f 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -297,6 +297,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool of "boundchecks": result = contains(conf.options, optBoundsCheck) of "refchecks": result = contains(conf.options, optRefCheck) of "overflowchecks": result = contains(conf.options, optOverflowCheck) + of "staticboundchecks": result = contains(conf.options, optStaticBoundsCheck) of "stylechecks": result = contains(conf.options, optStyleCheck) of "linedir": result = contains(conf.options, optLineDir) of "assertions", "a": result = contains(conf.options, optAssert) @@ -591,6 +592,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "boundchecks": processOnOffSwitch(conf, {optBoundsCheck}, arg, pass, info) of "refchecks": processOnOffSwitch(conf, {optRefCheck}, arg, pass, info) of "overflowchecks": processOnOffSwitch(conf, {optOverflowCheck}, arg, pass, info) + of "staticboundchecks": processOnOffSwitch(conf, {optStaticBoundsCheck}, arg, pass, info) of "stylechecks": processOnOffSwitch(conf, {optStyleCheck}, arg, pass, info) of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info) of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info) diff --git a/compiler/guards.nim b/compiler/guards.nim index 5d0fa64eb..aaf7ecf0e 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -250,7 +250,6 @@ proc pred(n: PNode): PNode = result = n proc canon*(n: PNode; o: Operators): PNode = - # XXX for now only the new code in 'semparallel' uses this if n.safeLen >= 1: result = shallowCopy(n) for i in 0..<n.len: @@ -275,7 +274,7 @@ proc canon*(n: PNode; o: Operators): PNode = result = negate(result[1], result[2], result, o) of someLen: result[0] = o.opLen.newSymNode - of someLt: + of someLt - {mLtF64}: # x < y same as x <= y-1: let y = n[2].canon(o) let p = pred(y) @@ -315,12 +314,12 @@ proc canon*(n: PNode; o: Operators): PNode = if plus != nil and not isLetLocation(x, true): result = buildCall(result[0].sym, plus, y[1]) else: discard - elif x.isValue and y.getMagic in someAdd and y[2].isValue: + elif x.isValue and y.getMagic in someAdd and y[2].kind == x.kind: # 0 <= a.len + 3 # -3 <= a.len result[1] = x |-| y[2] result[2] = y[1] - elif x.isValue and y.getMagic in someSub and y[2].isValue: + elif x.isValue and y.getMagic in someSub and y[2].kind == x.kind: # 0 <= a.len - 3 # 3 <= a.len result[1] = x |+| y[2] @@ -340,6 +339,8 @@ proc usefulFact(n: PNode; o: Operators): PNode = if isLetLocation(n[1], true) or isLetLocation(n[2], true): # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1' result = n + elif n[1].getMagic in someLen or n[2].getMagic in someLen: + result = n of someLe+someLt: if isLetLocation(n[1], true) or isLetLocation(n[2], true): # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1' @@ -401,10 +402,20 @@ type TModel* = object s*: seq[PNode] # the "knowledge base" o*: Operators + beSmart*: bool proc addFact*(m: var TModel, nn: PNode) = let n = usefulFact(nn, m.o) - if n != nil: m.s.add n + if n != nil: + if not m.beSmart: + m.s.add n + else: + let c = canon(n, m.o) + if c.getMagic == mAnd: + addFact(m, c[1]) + addFact(m, c[2]) + else: + m.s.add c proc addFactNeg*(m: var TModel, n: PNode) = let n = n.neg(m.o) @@ -614,6 +625,9 @@ proc impliesGe(fact, x, c: PNode): TImplication = proc impliesLe(fact, x, c: PNode): TImplication = if not isLocation(x): + if c.isValue: + if leValue(x, x): return impYes + else: return impNo return impliesGe(fact, c, x) case fact[0].sym.magic of someEq: @@ -799,7 +813,7 @@ proc ple(m: TModel; a, b: PNode): TImplication = if lastOrd(nil, a.typ) <= b.intVal: return impYes # 3 <= x iff low(x) <= 3 if a.isValue and b.typ != nil and b.typ.isOrdinalType: - if firstOrd(nil, b.typ) <= a.intVal: return impYes + if a.intVal <= firstOrd(nil, b.typ): return impYes # x <= x if sameTree(a, b): return impYes @@ -810,7 +824,11 @@ proc ple(m: TModel; a, b: PNode): TImplication = # x <= y+c if 0 <= c and x <= y # x <= y+(-c) if c <= 0 and y >= x - if b.getMagic in someAdd and zero() <=? b[2] and a <=? b[1]: return impYes + if b.getMagic in someAdd: + if zero() <=? b[2] and a <=? b[1]: return impYes + # x <= y-c if x+c <= y + if b[2] <=? zero() and (canon(m.o.opSub.buildCall(a, b[2]), m.o) <=? b[1]): + return impYes # x+c <= y if c <= 0 and x <= y if a.getMagic in someAdd and a[2] <=? zero() and a[1] <=? b: return impYes @@ -899,10 +917,13 @@ proc pleViaModelRec(m: var TModel; a, b: PNode): TImplication = # --> true if (len-100) <= (len-1) let x = fact[1] let y = fact[2] - if sameTree(x, a) and y.getMagic in someAdd and b.getMagic in someAdd and - sameTree(y[1], b[1]): - if ple(m, b[2], y[2]) == impYes: - return impYes + # x <= y. + # Question: x <= b? True iff y <= b. + if sameTree(x, a): + if ple(m, y, b) == impYes: return impYes + if y.getMagic in someAdd and b.getMagic in someAdd and sameTree(y[1], b[1]): + if ple(m, b[2], y[2]) == impYes: + return impYes # x <= y implies a <= b if a <= x and y <= b if ple(m, a, x) == impYes: @@ -961,6 +982,10 @@ proc proveLe*(m: TModel; a, b: PNode): TImplication = proc addFactLe*(m: var TModel; a, b: PNode) = m.s.add canon(m.o.opLe.buildCall(a, b), m.o) +proc addFactLt*(m: var TModel; a, b: PNode) = + let bb = m.o.opAdd.buildCall(b, minusOne()) + addFactLe(m, a, bb) + proc settype(n: PNode): PType = result = newType(tySet, n.typ.owner) addSonSkipIntLit(result, n.typ) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 6eac5bf1b..033a98513 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -36,7 +36,8 @@ type warnUnusedImportX, warnInheritFromException, warnEachIdentIsTuple, - warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, + warnProveInit, warnProveField, warnProveIndex, + warnStaticIndexCheck, warnGcUnsafe, warnGcUnsafe2, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, warnInconsistentSpacing, warnCaseTransition, warnCycleCreated, warnUser, hintSuccess, hintSuccessX, hintCC, @@ -87,6 +88,7 @@ const warnProveInit: "Cannot prove that '$1' is initialized. This will become a compile time error in the future.", warnProveField: "cannot prove that field '$1' is accessible", warnProveIndex: "cannot prove index '$1' is valid", + warnStaticIndexCheck: "$1", warnGcUnsafe: "not GC-safe: '$1'", warnGcUnsafe2: "$1", warnUninit: "'$1' might not have been initialized", @@ -142,7 +144,8 @@ const "TypelessParam", "UseBase", "WriteToForeignHeap", "UnsafeCode", "UnusedImport", "InheritFromException", "EachIdentIsTuple", - "ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", + "ProveInit", "ProveField", "ProveIndex", + "IndexCheck", "GcUnsafe", "GcUnsafe2", "Uninit", "GcMem", "Destructor", "LockLevel", "ResultShadowed", "Spacing", "CaseTransition", "CycleCreated", "User"] diff --git a/compiler/options.nim b/compiler/options.nim index 26260a770..d9a7a06ef 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -26,7 +26,7 @@ type # please make sure we have under 32 options TOption* = enum # **keep binary compatible** optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, optOverflowCheck, optNilCheck, optRefCheck, - optNaNCheck, optInfCheck, optStyleCheck, + optNaNCheck, optInfCheck, optStaticBoundsCheck, optStyleCheck, optAssert, optLineDir, optWarns, optHints, optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support optLineTrace, # line tracing support (includes stack tracing) @@ -320,7 +320,7 @@ const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, par const ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck, optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck, - optStyleCheck, optRefCheck} + optStyleCheck} DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck, diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 3e89917ed..8196c1b4e 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -42,7 +42,8 @@ const wTags, wLocks, wGcSafe} exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNoSideEffect} stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangeChecks, - wBoundChecks, wOverflowChecks, wNilChecks, wStyleChecks, wAssertions, + wBoundChecks, wOverflowChecks, wNilChecks, wStaticBoundchecks, + wStyleChecks, wAssertions, wWarnings, wHints, wLineDir, wStackTrace, wLineTrace, wOptimization, wHint, wWarning, wError, wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop, @@ -344,6 +345,7 @@ proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} = of wFloatChecks: {optNaNCheck, optInfCheck} of wNanChecks: {optNaNCheck} of wInfChecks: {optInfCheck} + of wStaticBoundchecks: {optStaticBoundsCheck} of wStyleChecks: {optStyleCheck} of wAssertions: {optAssert} of wWarnings: {optWarns} @@ -1027,7 +1029,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wCodegenDecl: processCodegenDecl(c, it, sym) of wChecks, wObjChecks, wFieldChecks, wRangeChecks, wBoundChecks, wOverflowChecks, wNilChecks, wAssertions, wWarnings, wHints, - wLineDir, wOptimization, wStyleChecks, wCallconv, wDebugger, wProfiler, + wLineDir, wOptimization, wStaticBoundchecks, wStyleChecks, + wCallconv, wDebugger, wProfiler, wFloatChecks, wNanChecks, wInfChecks, wPatterns, wTrMacros: processOption(c, it, c.config.options) of wStackTrace, wLineTrace: diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index 62264ab40..5614f9ee3 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -23,7 +23,7 @@ import ast, astalgo, idents, lowerings, magicsys, guards, sempass2, msgs, - renderer, types, modulegraphs, options, spawn + renderer, types, modulegraphs, options, spawn, lineinfos from trees import getMagic from strutils import `%` @@ -129,10 +129,12 @@ template `?`(x): untyped = x.renderTree proc checkLe(c: AnalysisCtx; a, b: PNode) = case proveLe(c.guards, a, b) of impUnknown: - localError(c.graph.config, a.info, "cannot prove: " & ?a & " <= " & ?b & " (bounds check)") + message(c.graph.config, a.info, warnStaticIndexCheck, + "cannot prove: " & ?a & " <= " & ?b) of impYes: discard of impNo: - localError(c.graph.config, a.info, "can prove: " & ?a & " > " & ?b & " (bounds check)") + message(c.graph.config, a.info, warnStaticIndexCheck, + "can prove: " & ?a & " > " & ?b) proc checkBounds(c: AnalysisCtx; arr, idx: PNode) = checkLe(c, lowBound(c.graph.config, arr), idx) @@ -162,17 +164,17 @@ proc overlap(m: TModel; conf: ConfigRef; x,y,c,d: PNode) = case proveLe(m, x, d) of impNo: discard of impUnknown, impYes: - localError(conf, x.info, + message(conf, x.info, warnStaticIndexCheck, "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % [?c, ?y, ?x, ?y, ?c, ?d]) of impYes: case proveLe(m, x, d) of impUnknown: - localError(conf, x.info, + message(conf, x.info, warnStaticIndexCheck, "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % [?x, ?d, ?x, ?y, ?c, ?d]) of impYes: - localError(conf, x.info, "($#)..($#) not disjoint from ($#)..($#)" % + message(conf, x.info, warnStaticIndexCheck, "($#)..($#) not disjoint from ($#)..($#)" % [?c, ?y, ?x, ?y, ?c, ?d]) of impNo: discard of impNo: discard @@ -261,8 +263,6 @@ proc min(a, b: PNode): PNode = elif a.intVal < b.intVal: result = a else: result = b -proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags - template pushSpawnId(c, body) {.dirty.} = inc c.spawns let oldSpawnId = c.currentSpawnId diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index af706bee1..5d4ad658c 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -75,6 +75,7 @@ type gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool inEnforcedNoSideEffects: bool maxLockLevel, currLockLevel: TLockLevel + currOptions: TOptions config: ConfigRef graph: ModuleGraph c: PContext @@ -668,6 +669,32 @@ proc cstringCheck(tracked: PEffects; n: PNode) = a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}): message(tracked.config, n.info, warnUnsafeCode, renderTree(n)) +proc checkLe(c: PEffects; a, b: PNode) = + case proveLe(c.guards, a, b) + of impUnknown: + #for g in c.guards.s: + # if g != nil: echo "I Know ", g + message(c.config, a.info, warnStaticIndexCheck, + "cannot prove: " & $a & " <= " & $b) + of impYes: + discard + of impNo: + message(c.config, a.info, warnStaticIndexCheck, + "can prove: " & $a & " > " & $b) + +proc checkBounds(c: PEffects; arr, idx: PNode) = + checkLe(c, lowBound(c.config, arr), idx) + checkLe(c, idx, highBound(c.config, arr, c.guards.o)) + +proc checkRange(c: PEffects; value: PNode; typ: PType) = + if typ.skipTypes(abstractInst - {tyRange}).kind == tyRange: + let lowBound = nkIntLit.newIntNode(firstOrd(c.config, typ)) + lowBound.info = value.info + let highBound = nkIntLit.newIntNode(lastOrd(c.config, typ)) + highBound.info = value.info + checkLe(c, lowBound, value) + checkLe(c, value, highBound) + proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) = createTypeBoundOps(tracked.graph, tracked.c, typ, info) if (typ != nil and tfHasAsgn in typ.flags) or @@ -755,12 +782,17 @@ proc track(tracked: PEffects, n: PNode) = discard else: message(tracked.config, arg.info, warnProveInit, $arg) + # check required for 'nim check': if n[1].typ.len > 0: createTypeBoundOps(tracked, n[1].typ.lastSon, n.info) createTypeBoundOps(tracked, n[1].typ, n.info) # new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'? + elif a.kind == nkSym and a.sym.magic in {mArrGet, mArrPut} and + optStaticBoundsCheck in tracked.currOptions: + checkBounds(tracked, n[1], n[2]) + if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and tracked.owner.kind != skMacro: let opKind = find(AttachedOpToStr, a.sym.name.s.normalize) @@ -849,6 +881,28 @@ proc track(tracked: PEffects, n: PNode) = of nkForStmt, nkParForStmt: # we are very conservative here and assume the loop is never executed: let oldState = tracked.init.len + + let oldFacts = tracked.guards.s.len + let iterCall = n[n.len-2] + if optStaticBoundsCheck in tracked.currOptions and iterCall.kind in nkCallKinds: + let op = iterCall[0] + if op.kind == nkSym and fromSystem(op.sym): + let iterVar = n[0] + case op.sym.name.s + of "..", "countup", "countdown": + let lower = iterCall[1] + let upper = iterCall[2] + # for i in 0..n means 0 <= i and i <= n. Countdown is + # the same since only the iteration direction changes. + addFactLe(tracked.guards, lower, iterVar) + addFactLe(tracked.guards, iterVar, upper) + of "..<": + let lower = iterCall[1] + let upper = iterCall[2] + addFactLe(tracked.guards, lower, iterVar) + addFactLt(tracked.guards, iterVar, upper) + else: discard + for i in 0..<n.len-2: let it = n[i] track(tracked, it) @@ -858,7 +912,6 @@ proc track(tracked: PEffects, n: PNode) = createTypeBoundOps(tracked, x.typ, x.info) else: createTypeBoundOps(tracked, it.typ, it.info) - let iterCall = n[^2] let loopBody = n[^1] if tracked.owner.kind != skMacro and iterCall.safeLen > 1: # XXX this is a bit hacky: @@ -867,6 +920,7 @@ proc track(tracked: PEffects, n: PNode) = track(tracked, iterCall) track(tracked, loopBody) setLen(tracked.init, oldState) + setLen(tracked.guards.s, oldFacts) of nkObjConstr: when false: track(tracked, n[0]) let oldFacts = tracked.guards.s.len @@ -932,18 +986,27 @@ proc track(tracked: PEffects, n: PNode) = # a better solution will come up eventually. if n[1].typ.kind != tyString: createTypeBoundOps(tracked, n[1].typ, n[1].info) + if optStaticBoundsCheck in tracked.currOptions: + checkRange(tracked, n[1], n.typ) of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: if n.len == 1: track(tracked, n[0]) if tracked.owner.kind != skMacro: createTypeBoundOps(tracked, n.typ, n.info) createTypeBoundOps(tracked, n[0].typ, n[0].info) + if optStaticBoundsCheck in tracked.currOptions: + checkRange(tracked, n[0], n.typ) of nkBracket: for i in 0..<n.safeLen: track(tracked, n[i]) checkForSink(tracked.config, tracked.owner, n[i]) if tracked.owner.kind != skMacro: createTypeBoundOps(tracked, n.typ, n.info) + of nkBracketExpr: + if optStaticBoundsCheck in tracked.currOptions and n.len == 2: + if n[0].typ != nil and skipTypes(n[0].typ, abstractVar).kind != tyTuple: + checkBounds(tracked, n[0], n[1]) + for i in 0 ..< n.len: track(tracked, n[i]) else: for i in 0..<n.safeLen: track(tracked, n[i]) @@ -1028,6 +1091,8 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PC t.init = @[] t.guards.s = @[] t.guards.o = initOperators(g) + t.currOptions = g.config.options + s.options + t.guards.beSmart = optStaticBoundsCheck in t.currOptions t.locked = @[] t.graph = g t.config = g.config diff --git a/compiler/trees.nim b/compiler/trees.nim index 10ccacffc..3b715053e 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -74,6 +74,7 @@ proc sameTree*(a, b: PNode): bool = result = true proc getMagic*(op: PNode): TMagic = + if op == nil: return mNone case op.kind of nkCallKinds: case op[0].kind diff --git a/compiler/types.nim b/compiler/types.nim index c7fe529e1..4fcc3a2cf 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -716,7 +716,7 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 = of tyOrdinal: if t.len > 0: result = firstOrd(conf, lastSon(t)) else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') - of tyUncheckedArray: + of tyUncheckedArray, tyCString: result = Zero else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index ed5d2649f..28b2f9c05 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -50,7 +50,7 @@ type wNimcall, wStdcall, wCdecl, wSafecall, wSyscall, wInline, wNoInline, wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangeChecks, wBoundChecks, wOverflowChecks, wNilChecks, - wFloatChecks, wNanChecks, wInfChecks, wStyleChecks, + wFloatChecks, wNanChecks, wInfChecks, wStyleChecks, wStaticBoundchecks, wNonReloadable, wExecuteOnReload, wAssertions, wPatterns, wTrMacros, wSinkInference, wWarnings, wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags, @@ -137,7 +137,7 @@ const "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure", "noconv", "on", "off", "checks", "rangechecks", "boundchecks", "overflowchecks", "nilchecks", - "floatchecks", "nanchecks", "infchecks", "stylechecks", + "floatchecks", "nanchecks", "infchecks", "stylechecks", "staticboundchecks", "nonreloadable", "executeonreload", "assertions", "patterns", "trmacros", "sinkinference", "warnings", "hints", |