Raise IndexDefect when deleting element at out of bounds index (#17821)

Co-authored-by: Timothee Cour <timothee.cour2@gmail.com>
Co-authored-by: Heiko Nickerl <mail@hnicke.de>
Co-authored-by: Heiko Nickerl <heiko.nickerl@flipapp.de>
This commit is contained in:
Heiko Nickerl 2021-06-20 17:56:33 +02:00 • committed by GitHub
commit 92cb765714
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 36 deletions

View file

@ -28,6 +28,9 @@
- `math.round` now is rounded "away from zero" in JS backend which is consistent - `math.round` now is rounded "away from zero" in JS backend which is consistent
with other backends. See #9125. Use `-d:nimLegacyJsRound` for previous behavior. with other backends. See #9125. Use `-d:nimLegacyJsRound` for previous behavior.
- Instead of deleting the element at the last index,
`system.delete()` now raises `IndexDefect` when given index is out of bounds.
- Changed the behavior of `uri.decodeQuery` when there are unencoded `=` - Changed the behavior of `uri.decodeQuery` when there are unencoded `=`
characters in the decoded values. Prior versions would raise an error. This is characters in the decoded values. Prior versions would raise an error. This is
no longer the case to comply with the HTML spec and other languages no longer the case to comply with the HTML spec and other languages

View file

@ -1325,30 +1325,6 @@ proc del*[T](x: var seq[T], i: Natural) {.noSideEffect.} =
movingCopy(x[i], x[xl]) movingCopy(x[i], x[xl])
setLen(x, xl) setLen(x, xl)
proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} =
## Deletes the item at index `i` by moving all `x[i+1..]` items by one position.
##
## This is an `O(n)` operation.
##
## See also:
## * `del <#del,seq[T],Natural>`_ for O(1) operation
##
## .. code-block:: Nim
## var i = @[1, 2, 3, 4, 5]
## i.delete(2) # => @[1, 2, 4, 5]
template defaultImpl =
let xl = x.len
for j in i.int..xl-2: movingCopy(x[j], x[j+1])
setLen(x, xl-1)
when nimvm:
defaultImpl()
else:
when defined(js):
{.emit: "`x`.splice(`i`, 1);".}
else:
defaultImpl()
proc insert*[T](x: var seq[T], item: sink T, i = 0.Natural) {.noSideEffect.} = proc insert*[T](x: var seq[T], item: sink T, i = 0.Natural) {.noSideEffect.} =
## Inserts `item` into `x` at position `i`. ## Inserts `item` into `x` at position `i`.
## ##
@ -2154,6 +2130,40 @@ const
import system/dollars import system/dollars
export dollars export dollars
proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} =
## Deletes the item at index `i` by moving all `x[i+1..^1]` items by one position.
##
## This is an `O(n)` operation.
##
## See also:
## * `del <#del,seq[T],Natural>`_ for O(1) operation
##
runnableExamples:
var s = @[1, 2, 3, 4, 5]
s.delete(2)
doAssert s == @[1, 2, 4, 5]
doAssertRaises(IndexDefect):
s.delete(4)
if i > high(x):
# xxx this should call `raiseIndexError2(i, high(x))` after some refactoring
raise (ref IndexDefect)(msg: "index out of bounds: '" & $i & "' < '" & $x.len & "' failed")
template defaultImpl =
let xl = x.len
for j in i.int..xl-2: movingCopy(x[j], x[j+1])
setLen(x, xl-1)
when nimvm:
defaultImpl()
else:
when defined(js):
{.emit: "`x`.splice(`i`, 1);".}
else:
defaultImpl()
const const
NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch
## is the version of Nim as a string. ## is the version of Nim as a string.

View file

@ -17,14 +17,10 @@ for i in 0..1024:
s.add(obj) s.add(obj)
proc limit*[t](a: var seq[t]) = proc limit*[t](a: var seq[t]) =
var loop = s.len() - 512 while s.len > 0:
for i in 0..loop:
#echo i
#GC_fullCollect()
if getOccupiedMem() > 3000_000: quit("still a leak!") if getOccupiedMem() > 3000_000: quit("still a leak!")
s.delete(i) s.delete(0)
s.limit() s.limit()
echo "no leak: ", getOccupiedMem() echo "no leak: ", getOccupiedMem()

View file

@ -2,9 +2,10 @@ discard """
targets: "c cpp js" targets: "c cpp js"
""" """
import stdtest/testutils
# TODO: in future work move existing `system` tests here, where they belong # TODO: in future work move existing `system` tests here, where they belong
import stdtest/testutils
template main = template main =
block: # closure block: # closure
@ -42,5 +43,34 @@ template main =
# doAssert rawEnv(inner3) != rawEnv(inner1) # because `a` vs `b` # this doesn't hold # doAssert rawEnv(inner3) != rawEnv(inner1) # because `a` vs `b` # this doesn't hold
outer() outer()
block: # system.delete
block:
var s = @[1]
s.delete(0)
doAssert s == @[]
block:
var s = @["foo", "bar"]
s.delete(1)
doAssert s == @["foo"]
block:
var s: seq[string]
doAssertRaises(IndexDefect):
s.delete(0)
block:
doAssert not compiles(@["foo"].delete(-1))
block: # bug #6710
var s = @["foo"]
s.delete(0)
doAssert s == @[]
block: # bug #16544: deleting out of bounds index should raise
var s = @["foo"]
doAssertRaises(IndexDefect):
s.delete(1)
static: main() static: main()
main() main()

View file

@ -59,11 +59,6 @@ doAssert high(float) > low(float)
doAssert high(float32) > low(float32) doAssert high(float32) > low(float32)
doAssert high(float64) > low(float64) doAssert high(float64) > low(float64)
# bug #6710
var s = @[1]
s.delete(0)
proc foo(a: openArray[int]) = proc foo(a: openArray[int]) =
for x in a: echo x for x in a: echo x