Skip to content

Commit

Permalink
revive #10228 (fix #9880) (#10610)
Browse files Browse the repository at this point in the history
* Make index out of bounds more useful by including the 'bounds'.
* fixes #9880 index out of bounds (remaining cases); revives #10228
* change err msg to: `index 3 not in 0 .. 1`
  • Loading branch information
timotheecour authored and Araq committed Feb 10, 2019
1 parent 352b52a commit 4910608
Show file tree
Hide file tree
Showing 11 changed files with 88 additions and 36 deletions.
4 changes: 2 additions & 2 deletions compiler/semfold.nim
Original file line number Diff line number Diff line change
Expand Up @@ -493,11 +493,11 @@ proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode =
result = x.sons[int(idx)]
if result.kind == nkExprColonExpr: result = result.sons[1]
else:
localError(g.config, n.info, formatErrorIndexBound(idx, sonsLen(x)+1) & $n)
localError(g.config, n.info, formatErrorIndexBound(idx, sonsLen(x)-1) & $n)
of nkBracket:
idx = idx - firstOrd(g.config, x.typ)
if idx >= 0 and idx < x.len: result = x.sons[int(idx)]
else: localError(g.config, n.info, formatErrorIndexBound(idx, x.len+1) & $n)
else: localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n)
of nkStrLit..nkTripleStrLit:
result = newNodeIT(nkCharLit, x.info, n.typ)
if idx >= 0 and idx < len(x.strVal):
Expand Down
22 changes: 11 additions & 11 deletions compiler/vm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
## An instruction is 1-3 int32s in memory, it is a register based VM.

import ast except getstr
import system/indexerrors

import
strutils, astalgo, msgs, vmdef, vmgen, nimsets, types, passes,
Expand Down Expand Up @@ -473,7 +474,6 @@ proc setLenSeq(c: PCtx; node: PNode; newLen: int; info: TLineInfo) =
node.sons[i] = getNullValue(typ.sons[0], info, c.config)

const
errIndexOutOfBounds = "index out of bounds"
errNilAccess = "attempt to access a nil address"
errOverOrUnderflow = "over- or underflow"
errConstantDivisionByZero = "division by zero"
Expand Down Expand Up @@ -577,19 +577,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
# a = b[c]
decodeBC(rkNode)
if regs[rc].intVal > high(int):
stackTrace(c, tos, pc, errIndexOutOfBounds)
stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int)))
let idx = regs[rc].intVal.int
let src = regs[rb].node
if src.kind in {nkStrLit..nkTripleStrLit}:
if idx <% src.strVal.len:
regs[ra].node = newNodeI(nkCharLit, c.debug[pc])
regs[ra].node.intVal = src.strVal[idx].ord
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.strVal.len-1))
elif src.kind notin {nkEmpty..nkFloat128Lit} and idx <% src.len:
regs[ra].node = src.sons[idx]
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.len-1))
of opcLdStrIdx:
decodeBC(rkInt)
let idx = regs[rc].intVal.int
Expand All @@ -599,7 +599,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
elif idx == s.len and optLaxStrings in c.config.options:
regs[ra].intVal = 0
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
stackTrace(c, tos, pc, formatErrorIndexBound(idx, s.len-1))
of opcWrArr:
# a[b] = c
decodeBC(rkNode)
Expand All @@ -609,11 +609,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
if idx <% arr.strVal.len:
arr.strVal[idx] = chr(regs[rc].intVal)
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
stackTrace(c, tos, pc, formatErrorIndexBound(idx, arr.strVal.len-1))
elif idx <% arr.len:
writeField(arr.sons[idx], regs[rc])
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
stackTrace(c, tos, pc, formatErrorIndexBound(idx, arr.len-1))
of opcLdObj:
# a = b.c
decodeBC(rkNode)
Expand Down Expand Up @@ -644,7 +644,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
if idx <% regs[ra].node.strVal.len:
regs[ra].node.strVal[idx] = chr(regs[rc].intVal)
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
stackTrace(c, tos, pc, formatErrorIndexBound(idx, regs[ra].node.strVal.len-1))
of opcAddrReg:
decodeB(rkRegisterAddr)
regs[ra].regAddr = addr(regs[rb])
Expand Down Expand Up @@ -1361,15 +1361,15 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
if src.kind notin {nkEmpty..nkNilLit} and idx <% src.len:
regs[ra].node = src.sons[idx]
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.len-1))
of opcNSetChild:
decodeBC(rkNode)
let idx = regs[rb].intVal.int
var dest = regs[ra].node
if dest.kind notin {nkEmpty..nkNilLit} and idx <% dest.len:
dest.sons[idx] = regs[rc].node
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
stackTrace(c, tos, pc, formatErrorIndexBound(idx, dest.len-1))
of opcNAdd:
decodeBC(rkNode)
var u = regs[rb].node
Expand Down Expand Up @@ -1774,7 +1774,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
if contains(g.cacheSeqs, destKey) and idx <% g.cacheSeqs[destKey].len:
regs[ra].node = g.cacheSeqs[destKey][idx.int]
else:
stackTrace(c, tos, pc, errIndexOutOfBounds)
stackTrace(c, tos, pc, formatErrorIndexBound(idx, g.cacheSeqs[destKey].len-1))
of opcNctPut:
let g = c.graph
let destKey = regs[ra].node.strVal
Expand Down
10 changes: 6 additions & 4 deletions lib/core/typeinfo.nim
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
include "system/inclrtl.nim"
include "system/hti.nim"

import system/indexerrors

{.pop.}

type
Expand Down Expand Up @@ -201,14 +203,14 @@ proc `[]`*(x: Any, i: int): Any =
of tyArray:
var bs = x.rawType.base.size
if i >=% x.rawType.size div bs:
raise newException(IndexError, "index out of bounds")
raise newException(IndexError, formatErrorIndexBound(i, x.rawType.size div bs))
return newAny(x.value +!! i*bs, x.rawType.base)
of tySequence:
var s = cast[ppointer](x.value)[]
if s == nil: raise newException(ValueError, "sequence is nil")
var bs = x.rawType.base.size
if i >=% cast[PGenSeq](s).len:
raise newException(IndexError, "index out of bounds")
raise newException(IndexError, formatErrorIndexBound(i, cast[PGenSeq](s).len-1))
return newAny(s +!! (GenericSeqSize+i*bs), x.rawType.base)
else: assert false

Expand All @@ -218,15 +220,15 @@ proc `[]=`*(x: Any, i: int, y: Any) =
of tyArray:
var bs = x.rawType.base.size
if i >=% x.rawType.size div bs:
raise newException(IndexError, "index out of bounds")
raise newException(IndexError, formatErrorIndexBound(i, x.rawType.size div bs))
assert y.rawType == x.rawType.base
genericAssign(x.value +!! i*bs, y.value, y.rawType)
of tySequence:
var s = cast[ppointer](x.value)[]
if s == nil: raise newException(ValueError, "sequence is nil")
var bs = x.rawType.base.size
if i >=% cast[PGenSeq](s).len:
raise newException(IndexError, "index out of bounds")
raise newException(IndexError, formatErrorIndexBound(i, cast[PGenSeq](s).len-1))
assert y.rawType == x.rawType.base
genericAssign(s +!! (GenericSeqSize+i*bs), y.value, y.rawType)
else: assert false
Expand Down
2 changes: 1 addition & 1 deletion lib/pure/collections/sharedstrings.nim
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
type
UncheckedCharArray = UncheckedArray[char]

import system/helpers2
import system/indexerrors

type
Buffer = ptr object
Expand Down
6 changes: 3 additions & 3 deletions lib/pure/os.nim
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
include "system/inclrtl"

import
strutils, pathnorm
strutils, pathnorm, system/indexerrors

const weirdTarget = defined(nimscript) or defined(js)

Expand Down Expand Up @@ -2551,7 +2551,7 @@ elif defined(windows):
ownArgv = parseCmdLine($getCommandLine())
ownParsedArgv = true
if i < ownArgv.len and i >= 0: return TaintedString(ownArgv[i])
raise newException(IndexError, "invalid index")
raise newException(IndexError, formatErrorIndexBound(i, ownArgv.len-1))

elif defined(genode):
proc paramStr*(i: int): TaintedString =
Expand All @@ -2570,7 +2570,7 @@ elif not defined(createNimRtl) and
proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} =
# Docstring in nimdoc block.
if i < cmdCount and i >= 0: return TaintedString($cmdLine[i])
raise newException(IndexError, "invalid index")
raise newException(IndexError, formatErrorIndexBound(i, cmdCount-1))

proc paramCount*(): int {.tags: [ReadIOEffect].} =
# Docstring in nimdoc block.
Expand Down
4 changes: 2 additions & 2 deletions lib/system/indexerrors.nim
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# imported by other modules, unlike helpers.nim which is included

template formatErrorIndexBound*[T](i, a, b: T): string =
"index out of bounds: (a: " & $a & ") <= (i: " & $i & ") <= (b: " & $b & ") "
"index " & $i & " not in " & $a & " .. " & $b

template formatErrorIndexBound*[T](i, n: T): string =
"index out of bounds: (i: " & $i & ") <= (n: " & $n & ") "
formatErrorIndexBound(i, 0, n)
8 changes: 5 additions & 3 deletions lib/system/jssys.nim
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
# distribution, for details about the copyright.
#

import system/indexerrors

proc log*(s: cstring) {.importc: "console.log", varargs, nodecl.}

type
Expand Down Expand Up @@ -157,8 +159,8 @@ proc raiseDivByZero {.exportc: "raiseDivByZero", noreturn, compilerProc.} =
proc raiseRangeError() {.compilerproc, noreturn.} =
raise newException(RangeError, "value out of range")

proc raiseIndexError() {.compilerproc, noreturn.} =
raise newException(IndexError, "index out of bounds")
proc raiseIndexError(i, a, b: int) {.compilerproc, noreturn.} =
raise newException(IndexError, formatErrorIndexBound(int(i), int(a), int(b)))

proc raiseFieldError(f: string) {.compilerproc, noreturn.} =
raise newException(FieldError, f & " is not accessible")
Expand Down Expand Up @@ -626,7 +628,7 @@ proc arrayConstr(len: int, value: JSRef, typ: PNimType): JSRef {.

proc chckIndx(i, a, b: int): int {.compilerproc.} =
if i >= a and i <= b: return i
else: raiseIndexError()
else: raiseIndexError(i, a, b)

proc chckRange(i, a, b: int): int {.compilerproc.} =
if i >= a and i <= b: return i
Expand Down
23 changes: 23 additions & 0 deletions tests/exception/testindexerroroutput.nims
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
mode = ScriptMode.Verbose

case paramStr(3):
of "test1":
#543
block:
let s = "abc"
discard s[len(s)]
of "test2":
#537
block:
var s = "abc"
s[len(s)] = 'd'
of "test3":
#588
block:
let arr = ['a', 'b', 'c']
discard arr[len(arr)]
of "test4":
#588
block:
var arr = ['a', 'b', 'c']
arr[len(arr)] = 'd'
31 changes: 31 additions & 0 deletions tests/exception/tindexerrorformatbounds.nim
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import os, osproc, strutils

const characters = "abcdefghijklmnopqrstuvwxyz"
var s: string

# # chcks.nim:23
# # test formatErrorIndexBound returns correct bounds
block:
s = characters
try:
discard s[0..999]
except IndexError:
let msg = getCurrentExceptionMsg()
let expected = "index $# not in 0 .. $#" % [$len(s), $(len(s)-1)]
doAssert msg.contains expected, $(msg, expected)

block:
try:
discard paramStr(999)
except IndexError:
let msg = getCurrentExceptionMsg()
let expected = "index 999 not in 0 .. 0"
doAssert msg.contains expected, $(msg, expected)

block:
const nim = getCurrentCompilerExe()
for i in 1..4:
let (outp, errC) = execCmdEx("$# e tests/exception/testindexerroroutput.nims test$#" % [nim, $i])
let expected = "index 3 not in 0 .. 2"
doAssert errC != 0
doAssert outp.contains expected, $(outp, errC, expected, i)
6 changes: 3 additions & 3 deletions tests/misc/tinvalidarrayaccess.nim
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
discard """
errormsg: "index out of bounds: (a: 0) <= (i: 2) <= (b: 1) "
errormsg: "index 2 not in 0 .. 1"
line: 18
"""

block:
try:
let a = @[1,2]
echo a[3]
except Exception as e:
doAssert e.msg == "index out of bounds: (i:3) <= (n:1) "
doAssert e.msg == "index 3 not in 0 .. 1"
# note: this is not being tested, because the CT error happens before

block:
type TTestArr = array[0..1, int16]
Expand Down
8 changes: 1 addition & 7 deletions tests/misc/tinvalidarrayaccess2.nim
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
discard """
errormsg: "index out of bounds: (a: 0) <= (i: 3) <= (b: 1) "
errormsg: "index 3 not in 0 .. 1"
line: 9
"""

Expand All @@ -8,9 +8,3 @@ discard """
let a = [1,2]
echo a[3]

when false:
# TOOD: this case is not yet handled, giving: "index out of bounds"
proc fun()=
let a = @[1,2]
echo a[3]
static: fun()

0 comments on commit 4910608

Please sign in to comment.