new feature: --staticBoundChecks:on to enforce static array index checking (#10965)

This commit is contained in:
Andreas Rumpf 2020-03-18 14:25:10 +01:00 • committed by GitHub
commit 3f29911a94
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 152 additions and 50 deletions

View file

@ -182,6 +182,7 @@ proc getModule*(s: PSym): PSym =
assert((result.kind == skModule) or (result.owner != result)) assert((result.kind == skModule) or (result.owner != result))
while result != nil and result.kind != skModule: result = result.owner 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 = proc getSymFromList*(list: PNode, ident: PIdent, start: int = 0): PSym =
for i in start..<list.len: for i in start..<list.len:
if list[i].kind == nkSym: if list[i].kind == nkSym:

View file

@ -297,6 +297,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool
of "boundchecks": result = contains(conf.options, optBoundsCheck) of "boundchecks": result = contains(conf.options, optBoundsCheck)
of "refchecks": result = contains(conf.options, optRefCheck) of "refchecks": result = contains(conf.options, optRefCheck)
of "overflowchecks": result = contains(conf.options, optOverflowCheck) of "overflowchecks": result = contains(conf.options, optOverflowCheck)
of "staticboundchecks": result = contains(conf.options, optStaticBoundsCheck)
of "stylechecks": result = contains(conf.options, optStyleCheck) of "stylechecks": result = contains(conf.options, optStyleCheck)
of "linedir": result = contains(conf.options, optLineDir) of "linedir": result = contains(conf.options, optLineDir)
of "assertions", "a": result = contains(conf.options, optAssert) 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 "boundchecks": processOnOffSwitch(conf, {optBoundsCheck}, arg, pass, info)
of "refchecks": processOnOffSwitch(conf, {optRefCheck}, arg, pass, info) of "refchecks": processOnOffSwitch(conf, {optRefCheck}, arg, pass, info)
of "overflowchecks": processOnOffSwitch(conf, {optOverflowCheck}, 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 "stylechecks": processOnOffSwitch(conf, {optStyleCheck}, arg, pass, info)
of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info) of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info)
of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info) of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info)

View file

@ -250,7 +250,6 @@ proc pred(n: PNode): PNode =
result = n result = n
proc canon*(n: PNode; o: Operators): PNode = proc canon*(n: PNode; o: Operators): PNode =
# XXX for now only the new code in 'semparallel' uses this
if n.safeLen >= 1: if n.safeLen >= 1:
result = shallowCopy(n) result = shallowCopy(n)
for i in 0..<n.len: 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) result = negate(result[1], result[2], result, o)
of someLen: of someLen:
result[0] = o.opLen.newSymNode result[0] = o.opLen.newSymNode
of someLt: of someLt - {mLtF64}:
# x < y same as x <= y-1: # x < y same as x <= y-1:
let y = n[2].canon(o) let y = n[2].canon(o)
let p = pred(y) let p = pred(y)
@ -315,12 +314,12 @@ proc canon*(n: PNode; o: Operators): PNode =
if plus != nil and not isLetLocation(x, true): if plus != nil and not isLetLocation(x, true):
result = buildCall(result[0].sym, plus, y[1]) result = buildCall(result[0].sym, plus, y[1])
else: discard 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 # 0 <= a.len + 3
# -3 <= a.len # -3 <= a.len
result[1] = x |-| y[2] result[1] = x |-| y[2]
result[2] = y[1] 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 # 0 <= a.len - 3
# 3 <= a.len # 3 <= a.len
result[1] = x |+| y[2] 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): if isLetLocation(n[1], true) or isLetLocation(n[2], true):
# XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1' # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
result = n result = n
elif n[1].getMagic in someLen or n[2].getMagic in someLen:
result = n
of someLe+someLt: of someLe+someLt:
if isLetLocation(n[1], true) or isLetLocation(n[2], true): if isLetLocation(n[1], true) or isLetLocation(n[2], true):
# XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1' # XXX algebraic simplifications! 'i-1 < a.len' --> 'i < a.len+1'
@ -401,10 +402,20 @@ type
TModel* = object TModel* = object
s*: seq[PNode] # the "knowledge base" s*: seq[PNode] # the "knowledge base"
o*: Operators o*: Operators
beSmart*: bool
proc addFact*(m: var TModel, nn: PNode) = proc addFact*(m: var TModel, nn: PNode) =
let n = usefulFact(nn, m.o) 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) = proc addFactNeg*(m: var TModel, n: PNode) =
let n = n.neg(m.o) let n = n.neg(m.o)
@ -614,6 +625,9 @@ proc impliesGe(fact, x, c: PNode): TImplication =
proc impliesLe(fact, x, c: PNode): TImplication = proc impliesLe(fact, x, c: PNode): TImplication =
if not isLocation(x): if not isLocation(x):
if c.isValue:
if leValue(x, x): return impYes
else: return impNo
return impliesGe(fact, c, x) return impliesGe(fact, c, x)
case fact[0].sym.magic case fact[0].sym.magic
of someEq: of someEq:
@ -799,7 +813,7 @@ proc ple(m: TModel; a, b: PNode): TImplication =
if lastOrd(nil, a.typ) <= b.intVal: return impYes if lastOrd(nil, a.typ) <= b.intVal: return impYes
# 3 <= x iff low(x) <= 3 # 3 <= x iff low(x) <= 3
if a.isValue and b.typ != nil and b.typ.isOrdinalType: 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 # x <= x
if sameTree(a, b): return impYes 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 0 <= c and x <= y
# x <= y+(-c) if c <= 0 and y >= x # 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 # x+c <= y if c <= 0 and x <= y
if a.getMagic in someAdd and a[2] <=? zero() and a[1] <=? b: return impYes 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) # --> true if (len-100) <= (len-1)
let x = fact[1] let x = fact[1]
let y = fact[2] let y = fact[2]
if sameTree(x, a) and y.getMagic in someAdd and b.getMagic in someAdd and # x <= y.
sameTree(y[1], b[1]): # Question: x <= b? True iff y <= b.
if ple(m, b[2], y[2]) == impYes: if sameTree(x, a):
return impYes 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 # x <= y implies a <= b if a <= x and y <= b
if ple(m, a, x) == impYes: 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) = proc addFactLe*(m: var TModel; a, b: PNode) =
m.s.add canon(m.o.opLe.buildCall(a, b), m.o) 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 = proc settype(n: PNode): PType =
result = newType(tySet, n.typ.owner) result = newType(tySet, n.typ.owner)
addSonSkipIntLit(result, n.typ) addSonSkipIntLit(result, n.typ)

View file

@ -36,7 +36,8 @@ type
warnUnusedImportX, warnUnusedImportX,
warnInheritFromException, warnInheritFromException,
warnEachIdentIsTuple, warnEachIdentIsTuple,
warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, warnProveInit, warnProveField, warnProveIndex,
warnStaticIndexCheck, warnGcUnsafe, warnGcUnsafe2,
warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed,
warnInconsistentSpacing, warnCaseTransition, warnCycleCreated, warnUser, warnInconsistentSpacing, warnCaseTransition, warnCycleCreated, warnUser,
hintSuccess, hintSuccessX, hintCC, 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.", 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", warnProveField: "cannot prove that field '$1' is accessible",
warnProveIndex: "cannot prove index '$1' is valid", warnProveIndex: "cannot prove index '$1' is valid",
warnStaticIndexCheck: "$1",
warnGcUnsafe: "not GC-safe: '$1'", warnGcUnsafe: "not GC-safe: '$1'",
warnGcUnsafe2: "$1", warnGcUnsafe2: "$1",
warnUninit: "'$1' might not have been initialized", warnUninit: "'$1' might not have been initialized",
@ -142,7 +144,8 @@ const
"TypelessParam", "UseBase", "WriteToForeignHeap", "TypelessParam", "UseBase", "WriteToForeignHeap",
"UnsafeCode", "UnusedImport", "InheritFromException", "UnsafeCode", "UnusedImport", "InheritFromException",
"EachIdentIsTuple", "EachIdentIsTuple",
"ProveInit", "ProveField", "ProveIndex", "GcUnsafe", "GcUnsafe2", "Uninit", "ProveInit", "ProveField", "ProveIndex",
"IndexCheck", "GcUnsafe", "GcUnsafe2", "Uninit",
"GcMem", "Destructor", "LockLevel", "ResultShadowed", "GcMem", "Destructor", "LockLevel", "ResultShadowed",
"Spacing", "CaseTransition", "CycleCreated", "User"] "Spacing", "CaseTransition", "CycleCreated", "User"]

View file

@ -26,7 +26,7 @@ type # please make sure we have under 32 options
TOption* = enum # **keep binary compatible** TOption* = enum # **keep binary compatible**
optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, optNone, optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck,
optOverflowCheck, optNilCheck, optRefCheck, optOverflowCheck, optNilCheck, optRefCheck,
optNaNCheck, optInfCheck, optStyleCheck, optNaNCheck, optInfCheck, optStaticBoundsCheck, optStyleCheck,
optAssert, optLineDir, optWarns, optHints, optAssert, optLineDir, optWarns, optHints,
optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support optOptimizeSpeed, optOptimizeSize, optStackTrace, # stack tracing support
optLineTrace, # line tracing support (includes stack tracing) optLineTrace, # line tracing support (includes stack tracing)
@ -320,7 +320,7 @@ const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, par
const const
ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck, ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optNilCheck,
optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck, optOverflowCheck, optBoundsCheck, optAssert, optNaNCheck, optInfCheck,
optStyleCheck, optRefCheck} optStyleCheck}
DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck, DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck,
optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck, optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck,

View file

@ -42,7 +42,8 @@ const
wTags, wLocks, wGcSafe} wTags, wLocks, wGcSafe}
exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNoSideEffect} exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNoSideEffect}
stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangeChecks, stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangeChecks,
wBoundChecks, wOverflowChecks, wNilChecks, wStyleChecks, wAssertions, wBoundChecks, wOverflowChecks, wNilChecks, wStaticBoundchecks,
wStyleChecks, wAssertions,
wWarnings, wHints, wWarnings, wHints,
wLineDir, wStackTrace, wLineTrace, wOptimization, wHint, wWarning, wError, wLineDir, wStackTrace, wLineTrace, wOptimization, wHint, wWarning, wError,
wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop, wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop,
@ -344,6 +345,7 @@ proc pragmaToOptions(w: TSpecialWord): TOptions {.inline.} =
of wFloatChecks: {optNaNCheck, optInfCheck} of wFloatChecks: {optNaNCheck, optInfCheck}
of wNanChecks: {optNaNCheck} of wNanChecks: {optNaNCheck}
of wInfChecks: {optInfCheck} of wInfChecks: {optInfCheck}
of wStaticBoundchecks: {optStaticBoundsCheck}
of wStyleChecks: {optStyleCheck} of wStyleChecks: {optStyleCheck}
of wAssertions: {optAssert} of wAssertions: {optAssert}
of wWarnings: {optWarns} 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 wCodegenDecl: processCodegenDecl(c, it, sym)
of wChecks, wObjChecks, wFieldChecks, wRangeChecks, wBoundChecks, of wChecks, wObjChecks, wFieldChecks, wRangeChecks, wBoundChecks,
wOverflowChecks, wNilChecks, wAssertions, wWarnings, wHints, wOverflowChecks, wNilChecks, wAssertions, wWarnings, wHints,
wLineDir, wOptimization, wStyleChecks, wCallconv, wDebugger, wProfiler, wLineDir, wOptimization, wStaticBoundchecks, wStyleChecks,
wCallconv, wDebugger, wProfiler,
wFloatChecks, wNanChecks, wInfChecks, wPatterns, wTrMacros: wFloatChecks, wNanChecks, wInfChecks, wPatterns, wTrMacros:
processOption(c, it, c.config.options) processOption(c, it, c.config.options)
of wStackTrace, wLineTrace: of wStackTrace, wLineTrace:

View file

@ -23,7 +23,7 @@
import import
ast, astalgo, idents, lowerings, magicsys, guards, sempass2, msgs, ast, astalgo, idents, lowerings, magicsys, guards, sempass2, msgs,
renderer, types, modulegraphs, options, spawn renderer, types, modulegraphs, options, spawn, lineinfos
from trees import getMagic from trees import getMagic
from strutils import `%` from strutils import `%`
@ -129,10 +129,12 @@ template `?`(x): untyped = x.renderTree
proc checkLe(c: AnalysisCtx; a, b: PNode) = proc checkLe(c: AnalysisCtx; a, b: PNode) =
case proveLe(c.guards, a, b) case proveLe(c.guards, a, b)
of impUnknown: 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 impYes: discard
of impNo: 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) = proc checkBounds(c: AnalysisCtx; arr, idx: PNode) =
checkLe(c, lowBound(c.graph.config, arr), idx) 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) case proveLe(m, x, d)
of impNo: discard of impNo: discard
of impUnknown, impYes: of impUnknown, impYes:
localError(conf, x.info, message(conf, x.info, warnStaticIndexCheck,
"cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" %
[?c, ?y, ?x, ?y, ?c, ?d]) [?c, ?y, ?x, ?y, ?c, ?d])
of impYes: of impYes:
case proveLe(m, x, d) case proveLe(m, x, d)
of impUnknown: of impUnknown:
localError(conf, x.info, message(conf, x.info, warnStaticIndexCheck,
"cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" % "cannot prove: $# > $#; required for ($#)..($#) disjoint from ($#)..($#)" %
[?x, ?d, ?x, ?y, ?c, ?d]) [?x, ?d, ?x, ?y, ?c, ?d])
of impYes: of impYes:
localError(conf, x.info, "($#)..($#) not disjoint from ($#)..($#)" % message(conf, x.info, warnStaticIndexCheck, "($#)..($#) not disjoint from ($#)..($#)" %
[?c, ?y, ?x, ?y, ?c, ?d]) [?c, ?y, ?x, ?y, ?c, ?d])
of impNo: discard of impNo: discard
of impNo: discard of impNo: discard
@ -261,8 +263,6 @@ proc min(a, b: PNode): PNode =
elif a.intVal < b.intVal: result = a elif a.intVal < b.intVal: result = a
else: result = b else: result = b
proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags
template pushSpawnId(c, body) {.dirty.} = template pushSpawnId(c, body) {.dirty.} =
inc c.spawns inc c.spawns
let oldSpawnId = c.currentSpawnId let oldSpawnId = c.currentSpawnId

View file

@ -75,6 +75,7 @@ type
gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool
inEnforcedNoSideEffects: bool inEnforcedNoSideEffects: bool
maxLockLevel, currLockLevel: TLockLevel maxLockLevel, currLockLevel: TLockLevel
currOptions: TOptions
config: ConfigRef config: ConfigRef
graph: ModuleGraph graph: ModuleGraph
c: PContext c: PContext
@ -668,6 +669,32 @@ proc cstringCheck(tracked: PEffects; n: PNode) =
a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}): a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}):
message(tracked.config, n.info, warnUnsafeCode, renderTree(n)) 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) = proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) =
createTypeBoundOps(tracked.graph, tracked.c, typ, info) createTypeBoundOps(tracked.graph, tracked.c, typ, info)
if (typ != nil and tfHasAsgn in typ.flags) or if (typ != nil and tfHasAsgn in typ.flags) or
@ -755,12 +782,17 @@ proc track(tracked: PEffects, n: PNode) =
discard discard
else: else:
message(tracked.config, arg.info, warnProveInit, $arg) message(tracked.config, arg.info, warnProveInit, $arg)
# check required for 'nim check': # check required for 'nim check':
if n[1].typ.len > 0: if n[1].typ.len > 0:
createTypeBoundOps(tracked, n[1].typ.lastSon, n.info) createTypeBoundOps(tracked, n[1].typ.lastSon, n.info)
createTypeBoundOps(tracked, n[1].typ, n.info) createTypeBoundOps(tracked, n[1].typ, n.info)
# new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'? # 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 if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and
tracked.owner.kind != skMacro: tracked.owner.kind != skMacro:
let opKind = find(AttachedOpToStr, a.sym.name.s.normalize) let opKind = find(AttachedOpToStr, a.sym.name.s.normalize)
@ -849,6 +881,28 @@ proc track(tracked: PEffects, n: PNode) =
of nkForStmt, nkParForStmt: of nkForStmt, nkParForStmt:
# we are very conservative here and assume the loop is never executed: # we are very conservative here and assume the loop is never executed:
let oldState = tracked.init.len 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: for i in 0..<n.len-2:
let it = n[i] let it = n[i]
track(tracked, it) track(tracked, it)
@ -858,7 +912,6 @@ proc track(tracked: PEffects, n: PNode) =
createTypeBoundOps(tracked, x.typ, x.info) createTypeBoundOps(tracked, x.typ, x.info)
else: else:
createTypeBoundOps(tracked, it.typ, it.info) createTypeBoundOps(tracked, it.typ, it.info)
let iterCall = n[^2]
let loopBody = n[^1] let loopBody = n[^1]
if tracked.owner.kind != skMacro and iterCall.safeLen > 1: if tracked.owner.kind != skMacro and iterCall.safeLen > 1:
# XXX this is a bit hacky: # XXX this is a bit hacky:
@ -867,6 +920,7 @@ proc track(tracked: PEffects, n: PNode) =
track(tracked, iterCall) track(tracked, iterCall)
track(tracked, loopBody) track(tracked, loopBody)
setLen(tracked.init, oldState) setLen(tracked.init, oldState)
setLen(tracked.guards.s, oldFacts)
of nkObjConstr: of nkObjConstr:
when false: track(tracked, n[0]) when false: track(tracked, n[0])
let oldFacts = tracked.guards.s.len let oldFacts = tracked.guards.s.len
@ -932,18 +986,27 @@ proc track(tracked: PEffects, n: PNode) =
# a better solution will come up eventually. # a better solution will come up eventually.
if n[1].typ.kind != tyString: if n[1].typ.kind != tyString:
createTypeBoundOps(tracked, n[1].typ, n[1].info) 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: of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64:
if n.len == 1: if n.len == 1:
track(tracked, n[0]) track(tracked, n[0])
if tracked.owner.kind != skMacro: if tracked.owner.kind != skMacro:
createTypeBoundOps(tracked, n.typ, n.info) createTypeBoundOps(tracked, n.typ, n.info)
createTypeBoundOps(tracked, n[0].typ, n[0].info) createTypeBoundOps(tracked, n[0].typ, n[0].info)
if optStaticBoundsCheck in tracked.currOptions:
checkRange(tracked, n[0], n.typ)
of nkBracket: of nkBracket:
for i in 0..<n.safeLen: for i in 0..<n.safeLen:
track(tracked, n[i]) track(tracked, n[i])
checkForSink(tracked.config, tracked.owner, n[i]) checkForSink(tracked.config, tracked.owner, n[i])
if tracked.owner.kind != skMacro: if tracked.owner.kind != skMacro:
createTypeBoundOps(tracked, n.typ, n.info) 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: else:
for i in 0..<n.safeLen: track(tracked, n[i]) 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.init = @[]
t.guards.s = @[] t.guards.s = @[]
t.guards.o = initOperators(g) t.guards.o = initOperators(g)
t.currOptions = g.config.options + s.options
t.guards.beSmart = optStaticBoundsCheck in t.currOptions
t.locked = @[] t.locked = @[]
t.graph = g t.graph = g
t.config = g.config t.config = g.config

View file

@ -74,6 +74,7 @@ proc sameTree*(a, b: PNode): bool =
result = true result = true
proc getMagic*(op: PNode): TMagic = proc getMagic*(op: PNode): TMagic =
if op == nil: return mNone
case op.kind case op.kind
of nkCallKinds: of nkCallKinds:
case op[0].kind case op[0].kind

View file

@ -716,7 +716,7 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 =
of tyOrdinal: of tyOrdinal:
if t.len > 0: result = firstOrd(conf, lastSon(t)) if t.len > 0: result = firstOrd(conf, lastSon(t))
else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')
of tyUncheckedArray: of tyUncheckedArray, tyCString:
result = Zero result = Zero
else: else:
internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')')

View file

@ -50,7 +50,7 @@ type
wNimcall, wStdcall, wCdecl, wSafecall, wSyscall, wInline, wNoInline, wNimcall, wStdcall, wCdecl, wSafecall, wSyscall, wInline, wNoInline,
wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangeChecks, wFastcall, wClosure, wNoconv, wOn, wOff, wChecks, wRangeChecks,
wBoundChecks, wOverflowChecks, wNilChecks, wBoundChecks, wOverflowChecks, wNilChecks,
wFloatChecks, wNanChecks, wInfChecks, wStyleChecks, wFloatChecks, wNanChecks, wInfChecks, wStyleChecks, wStaticBoundchecks,
wNonReloadable, wExecuteOnReload, wNonReloadable, wExecuteOnReload,
wAssertions, wPatterns, wTrMacros, wSinkInference, wWarnings, wAssertions, wPatterns, wTrMacros, wSinkInference, wWarnings,
wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags, wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags,
@ -137,7 +137,7 @@ const
"cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure", "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "closure",
"noconv", "on", "off", "checks", "rangechecks", "boundchecks", "noconv", "on", "off", "checks", "rangechecks", "boundchecks",
"overflowchecks", "nilchecks", "overflowchecks", "nilchecks",
"floatchecks", "nanchecks", "infchecks", "stylechecks", "floatchecks", "nanchecks", "infchecks", "stylechecks", "staticboundchecks",
"nonreloadable", "executeonreload", "nonreloadable", "executeonreload",
"assertions", "patterns", "trmacros", "sinkinference", "warnings", "hints", "assertions", "patterns", "trmacros", "sinkinference", "warnings", "hints",

View file

@ -1,14 +1,14 @@
discard """ discard """
cmd: "nim c --gc:arc $file" cmd: "nim c --gc:arc $file"
action: "compile" action: "compile"
""" """
# bug #13269 # bug #13269
import posix import posix
proc foo*() = proc foo*() =
var last = newSeq[Stat]() var last = newSeq[Stat]()
var next = last var next = last
for i in 0..3: for i in 0..3:
last = next last = next
foo() foo()

View file

@ -10,8 +10,9 @@ proc check(a: TObj not nil) =
proc doit() = proc doit() =
var x : array[0..1, TObj] var x : array[0..1, TObj]
if x[0] != nil: let y = x[0]
check(x[0]) if y != nil:
check(y)
doit() doit()

View file

@ -1,6 +1,6 @@
discard """ discard """
errormsg: "can prove: i + 1 > 30" errormsg: "cannot prove (i)..(i) disjoint from (i + 1)..(i + 1)"
line: 21 line: 20
""" """
import threadpool import threadpool

View file

@ -1,6 +1,7 @@
discard """ discard """
errormsg: "(k)..(k) not disjoint from (k)..(k)" errormsg: "(k)..(k) not disjoint from (k)..(k)"
line: 23 line: 23
action: compile
""" """
# bug #1597 # bug #1597

View file

@ -1,3 +1,3 @@
import tables import tables
let dataEx* = {1: 2, 3: 4}.toTable let dataEx* = {1: 2, 3: 4}.toTable