mirror of
https://github.com/nim-lang/Nim.git
synced 2026-01-10 06:54:16 +00:00
new feature: --staticBoundChecks:on to enforce static array index checking (#10965)
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"]
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 & ')')
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
discard """
|
||||
cmd: "nim c --gc:arc $file"
|
||||
action: "compile"
|
||||
"""
|
||||
|
||||
# bug #13269
|
||||
|
||||
import posix
|
||||
proc foo*() =
|
||||
var last = newSeq[Stat]()
|
||||
var next = last
|
||||
for i in 0..3:
|
||||
last = next
|
||||
foo()
|
||||
discard """
|
||||
cmd: "nim c --gc:arc $file"
|
||||
action: "compile"
|
||||
"""
|
||||
|
||||
# bug #13269
|
||||
|
||||
import posix
|
||||
proc foo*() =
|
||||
var last = newSeq[Stat]()
|
||||
var next = last
|
||||
for i in 0..3:
|
||||
last = next
|
||||
foo()
|
||||
|
||||
@@ -10,8 +10,9 @@ proc check(a: TObj not nil) =
|
||||
proc doit() =
|
||||
var x : array[0..1, TObj]
|
||||
|
||||
if x[0] != nil:
|
||||
check(x[0])
|
||||
let y = x[0]
|
||||
if y != nil:
|
||||
check(y)
|
||||
|
||||
doit()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
discard """
|
||||
errormsg: "can prove: i + 1 > 30"
|
||||
line: 21
|
||||
errormsg: "cannot prove (i)..(i) disjoint from (i + 1)..(i + 1)"
|
||||
line: 20
|
||||
"""
|
||||
|
||||
import threadpool
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
discard """
|
||||
errormsg: "(k)..(k) not disjoint from (k)..(k)"
|
||||
line: 23
|
||||
action: compile
|
||||
"""
|
||||
|
||||
# bug #1597
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import tables
|
||||
|
||||
let dataEx* = {1: 2, 3: 4}.toTable
|
||||
import tables
|
||||
|
||||
let dataEx* = {1: 2, 3: 4}.toTable
|
||||
|
||||
Reference in New Issue
Block a user