Implemented mSlice on the VM allowing toOpenArray to work at compile time. (#20586)
* Implemented opcSlice to make 'toOpenArray' work on the VM * Added nkOpenArray for VM to reduce bodgeness * Fixed range issues and erraneous comments * Range check correctly for openArrays in opcLdArr * Inverted logic for ldArr checking * vm now supports slicing strings * Added string tests * Removed usage of 'nkOpenArray' and redundant operations * Refactored vmSlice implementation, removing redundant and incorrect code * Made tuples go throw opcWrObj for field assignment * All strkinds should be considered for openarrays
This commit is contained in:
parent
ff2cb113ad
commit
4aa67ad7fd
4 changed files with 204 additions and 30 deletions
135
compiler/vm.nim
135
compiler/vm.nim
|
|
@ -535,6 +535,15 @@ template takeAddress(reg, source) =
|
||||||
reg.nodeAddr = addr source
|
reg.nodeAddr = addr source
|
||||||
GC_ref source
|
GC_ref source
|
||||||
|
|
||||||
|
proc takeCharAddress(c: PCtx, src: PNode, index: BiggestInt, pc: int): TFullReg =
|
||||||
|
let typ = newType(tyPtr, nextTypeId c.idgen, c.module.owner)
|
||||||
|
typ.add getSysType(c.graph, c.debug[pc], tyChar)
|
||||||
|
var node = newNodeIT(nkIntLit, c.debug[pc], typ) # xxx nkPtrLit
|
||||||
|
node.intVal = cast[int](src.strVal[index].addr)
|
||||||
|
node.flags.incl nfIsPtr
|
||||||
|
TFullReg(kind: rkNode, node: node)
|
||||||
|
|
||||||
|
|
||||||
proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
var pc = start
|
var pc = start
|
||||||
var tos = tos
|
var tos = tos
|
||||||
|
|
@ -668,6 +677,48 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
else:
|
else:
|
||||||
ensureKind(rkNode)
|
ensureKind(rkNode)
|
||||||
regs[ra].node = nb
|
regs[ra].node = nb
|
||||||
|
of opcSlice:
|
||||||
|
# A bodge, but this takes in `toOpenArray(rb, rc, rc)` and emits
|
||||||
|
# nkTupleConstr(x, y, z) into the `regs[ra]`. These can later be used for calculating the slice we have taken.
|
||||||
|
decodeBC(rkNode)
|
||||||
|
let
|
||||||
|
collection = regs[ra].node
|
||||||
|
leftInd = regs[rb].intVal
|
||||||
|
rightInd = regs[rc].intVal
|
||||||
|
|
||||||
|
proc rangeCheck(left, right: BiggestInt, safeLen: BiggestInt) =
|
||||||
|
if left < 0:
|
||||||
|
stackTrace(c, tos, pc, formatErrorIndexBound(left, safeLen))
|
||||||
|
|
||||||
|
if right > safeLen:
|
||||||
|
stackTrace(c, tos, pc, formatErrorIndexBound(right, safeLen))
|
||||||
|
|
||||||
|
case collection.kind
|
||||||
|
of nkTupleConstr: # slice of a slice
|
||||||
|
let safeLen = collection[2].intVal - collection[1].intVal
|
||||||
|
rangeCheck(leftInd, rightInd, safeLen)
|
||||||
|
let
|
||||||
|
leftInd = leftInd + collection[1].intVal # Slice is from the start of the old
|
||||||
|
rightInd = rightInd + collection[1].intVal
|
||||||
|
|
||||||
|
regs[ra].node = newTree(
|
||||||
|
nkTupleConstr,
|
||||||
|
collection[0],
|
||||||
|
newIntNode(nkIntLit, BiggestInt leftInd),
|
||||||
|
newIntNode(nkIntLit, BiggestInt rightInd)
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
let safeLen = safeArrLen(collection) - 1
|
||||||
|
rangeCheck(leftInd, rightInd, safeLen)
|
||||||
|
regs[ra].node = newTree(
|
||||||
|
nkTupleConstr,
|
||||||
|
collection,
|
||||||
|
newIntNode(nkIntLit, BiggestInt leftInd),
|
||||||
|
newIntNode(nkIntLit, BiggestInt rightInd)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
of opcLdArr:
|
of opcLdArr:
|
||||||
# a = b[c]
|
# a = b[c]
|
||||||
decodeBC(rkNode)
|
decodeBC(rkNode)
|
||||||
|
|
@ -675,7 +726,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int)))
|
stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int)))
|
||||||
let idx = regs[rc].intVal.int
|
let idx = regs[rc].intVal.int
|
||||||
let src = regs[rb].node
|
let src = regs[rb].node
|
||||||
if src.kind in {nkStrLit..nkTripleStrLit}:
|
case src.kind
|
||||||
|
of nkTupleConstr: # refer to `of opcSlice`
|
||||||
|
let
|
||||||
|
left = src[1].intVal
|
||||||
|
right = src[2].intVal
|
||||||
|
realIndex = left + idx
|
||||||
|
if idx in 0..(right - left):
|
||||||
|
case src[0].kind
|
||||||
|
of nkStrKinds:
|
||||||
|
regs[ra].node = newIntNode(nkCharLit, ord src[0].strVal[int realIndex])
|
||||||
|
of nkBracket:
|
||||||
|
regs[ra].node = src[0][int realIndex]
|
||||||
|
else:
|
||||||
|
stackTrace(c, tos, pc, "opcLdArr internal error")
|
||||||
|
else:
|
||||||
|
stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right))
|
||||||
|
|
||||||
|
of nkStrLit..nkTripleStrLit:
|
||||||
if idx <% src.strVal.len:
|
if idx <% src.strVal.len:
|
||||||
regs[ra].node = newNodeI(nkCharLit, c.debug[pc])
|
regs[ra].node = newNodeI(nkCharLit, c.debug[pc])
|
||||||
regs[ra].node.intVal = src.strVal[idx].ord
|
regs[ra].node.intVal = src.strVal[idx].ord
|
||||||
|
|
@ -692,10 +760,27 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int)))
|
stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int)))
|
||||||
let idx = regs[rc].intVal.int
|
let idx = regs[rc].intVal.int
|
||||||
let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
|
let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[]
|
||||||
if src.kind notin {nkEmpty..nkTripleStrLit} and idx <% src.len:
|
case src.kind
|
||||||
takeAddress regs[ra], src.sons[idx]
|
of nkTupleConstr:
|
||||||
|
let
|
||||||
|
left = src[1].intVal
|
||||||
|
right = src[2].intVal
|
||||||
|
realIndex = left + idx
|
||||||
|
if idx in 0..(right - left): # Refer to `opcSlice`
|
||||||
|
case src[0].kind
|
||||||
|
of nkStrKinds:
|
||||||
|
regs[ra] = takeCharAddress(c, src[0], realIndex, pc)
|
||||||
|
of nkBracket:
|
||||||
|
takeAddress regs[ra], src.sons[0].sons[realIndex]
|
||||||
|
else:
|
||||||
|
stackTrace(c, tos, pc, "opcLdArrAddr internal error")
|
||||||
|
else:
|
||||||
|
stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right))
|
||||||
else:
|
else:
|
||||||
stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.safeLen-1))
|
if src.kind notin {nkEmpty..nkTripleStrLit} and idx <% src.len:
|
||||||
|
takeAddress regs[ra], src.sons[idx]
|
||||||
|
else:
|
||||||
|
stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.safeLen-1))
|
||||||
of opcLdStrIdx:
|
of opcLdStrIdx:
|
||||||
decodeBC(rkInt)
|
decodeBC(rkInt)
|
||||||
let idx = regs[rc].intVal.int
|
let idx = regs[rc].intVal.int
|
||||||
|
|
@ -712,13 +797,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
let idx = regs[rc].intVal.int
|
let idx = regs[rc].intVal.int
|
||||||
let s = regs[rb].node.strVal.addr # or `byaddr`
|
let s = regs[rb].node.strVal.addr # or `byaddr`
|
||||||
if idx <% s[].len:
|
if idx <% s[].len:
|
||||||
# `makePtrType` not accessible from vm.nim
|
regs[ra] = takeCharAddress(c, regs[rb].node, idx, pc)
|
||||||
let typ = newType(tyPtr, nextTypeId c.idgen, c.module.owner)
|
|
||||||
typ.add getSysType(c.graph, c.debug[pc], tyChar)
|
|
||||||
let node = newNodeIT(nkIntLit, c.debug[pc], typ) # xxx nkPtrLit
|
|
||||||
node.intVal = cast[int](s[][idx].addr)
|
|
||||||
node.flags.incl nfIsPtr
|
|
||||||
regs[ra].node = node
|
|
||||||
else:
|
else:
|
||||||
stackTrace(c, tos, pc, formatErrorIndexBound(idx, s[].len-1))
|
stackTrace(c, tos, pc, formatErrorIndexBound(idx, s[].len-1))
|
||||||
of opcWrArr:
|
of opcWrArr:
|
||||||
|
|
@ -726,7 +805,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
decodeBC(rkNode)
|
decodeBC(rkNode)
|
||||||
let idx = regs[rb].intVal.int
|
let idx = regs[rb].intVal.int
|
||||||
let arr = regs[ra].node
|
let arr = regs[ra].node
|
||||||
if arr.kind in {nkStrLit..nkTripleStrLit}:
|
case arr.kind
|
||||||
|
of nkTupleConstr: # refer to `opcSlice`
|
||||||
|
let
|
||||||
|
src = arr[0]
|
||||||
|
left = arr[1].intVal
|
||||||
|
right = arr[2].intVal
|
||||||
|
realIndex = left + idx
|
||||||
|
if idx in 0..(right - left):
|
||||||
|
case src.kind
|
||||||
|
of nkStrKinds:
|
||||||
|
src.strVal[int(realIndex)] = char(regs[rc].intVal)
|
||||||
|
of nkBracket:
|
||||||
|
src[int(realIndex)] = regs[rc].node
|
||||||
|
else:
|
||||||
|
stackTrace(c, tos, pc, "opcWrArr internal error")
|
||||||
|
else:
|
||||||
|
stackTrace(c, tos, pc, formatErrorIndexBound(idx, int right))
|
||||||
|
of {nkStrLit..nkTripleStrLit}:
|
||||||
if idx <% arr.strVal.len:
|
if idx <% arr.strVal.len:
|
||||||
arr.strVal[idx] = chr(regs[rc].intVal)
|
arr.strVal[idx] = chr(regs[rc].intVal)
|
||||||
else:
|
else:
|
||||||
|
|
@ -884,14 +980,21 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg =
|
||||||
of opcLenSeq:
|
of opcLenSeq:
|
||||||
decodeBImm(rkInt)
|
decodeBImm(rkInt)
|
||||||
#assert regs[rb].kind == nkBracket
|
#assert regs[rb].kind == nkBracket
|
||||||
let high = (imm and 1) # discard flags
|
let
|
||||||
|
high = (imm and 1) # discard flags
|
||||||
|
node = regs[rb].node
|
||||||
if (imm and nimNodeFlag) != 0:
|
if (imm and nimNodeFlag) != 0:
|
||||||
# used by mNLen (NimNode.len)
|
# used by mNLen (NimNode.len)
|
||||||
regs[ra].intVal = regs[rb].node.safeLen - high
|
regs[ra].intVal = regs[rb].node.safeLen - high
|
||||||
else:
|
else:
|
||||||
# safeArrLen also return string node len
|
case node.kind
|
||||||
# used when string is passed as openArray in VM
|
of nkTupleConstr: # refer to `of opcSlice`
|
||||||
regs[ra].intVal = regs[rb].node.safeArrLen - high
|
regs[ra].intVal = node[2].intVal - node[1].intVal + 1 - high
|
||||||
|
else:
|
||||||
|
# safeArrLen also return string node len
|
||||||
|
# used when string is passed as openArray in VM
|
||||||
|
regs[ra].intVal = node.safeArrLen - high
|
||||||
|
|
||||||
of opcLenStr:
|
of opcLenStr:
|
||||||
decodeBImm(rkInt)
|
decodeBImm(rkInt)
|
||||||
assert regs[rb].kind == rkNode
|
assert regs[rb].kind == rkNode
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ type
|
||||||
opcWrStrIdx,
|
opcWrStrIdx,
|
||||||
opcLdStrIdx, # a = b[c]
|
opcLdStrIdx, # a = b[c]
|
||||||
opcLdStrIdxAddr, # a = addr(b[c])
|
opcLdStrIdxAddr, # a = addr(b[c])
|
||||||
|
opcSlice, # toOpenArray(collection, left, right)
|
||||||
|
|
||||||
opcAddInt,
|
opcAddInt,
|
||||||
opcAddImmInt,
|
opcAddImmInt,
|
||||||
|
|
|
||||||
|
|
@ -640,9 +640,19 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags
|
||||||
proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
|
proc genAsgnPatch(c: PCtx; le: PNode, value: TRegister) =
|
||||||
case le.kind
|
case le.kind
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
let dest = c.genx(le[0], {gfNode})
|
let
|
||||||
let idx = c.genIndex(le[1], le[0].typ)
|
dest = c.genx(le[0], {gfNode})
|
||||||
c.gABC(le, opcWrArr, dest, idx, value)
|
idx = c.genIndex(le[1], le[0].typ)
|
||||||
|
collTyp = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc})
|
||||||
|
|
||||||
|
case collTyp.kind
|
||||||
|
of tyString, tyCstring:
|
||||||
|
c.gABC(le, opcWrStrIdx, dest, idx, value)
|
||||||
|
of tyTuple:
|
||||||
|
c.gABC(le, opcWrObj, dest, int le[1].intVal, value)
|
||||||
|
else:
|
||||||
|
c.gABC(le, opcWrArr, dest, idx, value)
|
||||||
|
|
||||||
c.freeTemp(dest)
|
c.freeTemp(dest)
|
||||||
c.freeTemp(idx)
|
c.freeTemp(idx)
|
||||||
of nkCheckedFieldExpr:
|
of nkCheckedFieldExpr:
|
||||||
|
|
@ -1054,6 +1064,18 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||||
of tyString: genUnaryABI(c, n, dest, opcLenStr)
|
of tyString: genUnaryABI(c, n, dest, opcLenStr)
|
||||||
of tyCstring: genUnaryABI(c, n, dest, opcLenCstring)
|
of tyCstring: genUnaryABI(c, n, dest, opcLenCstring)
|
||||||
else: doAssert false, $n[1].typ.kind
|
else: doAssert false, $n[1].typ.kind
|
||||||
|
of mSlice:
|
||||||
|
var
|
||||||
|
d = c.genx(n[1])
|
||||||
|
left = c.genIndex(n[2], n[1].typ)
|
||||||
|
right = c.genIndex(n[3], n[1].typ)
|
||||||
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
|
c.gABC(n, opcNodeToReg, dest, d)
|
||||||
|
c.gABC(n, opcSlice, dest, left, right)
|
||||||
|
c.freeTemp(left)
|
||||||
|
c.freeTemp(right)
|
||||||
|
c.freeTemp(d)
|
||||||
|
|
||||||
of mIncl, mExcl:
|
of mIncl, mExcl:
|
||||||
unused(c, n, dest)
|
unused(c, n, dest)
|
||||||
var d = c.genx(n[1])
|
var d = c.genx(n[1])
|
||||||
|
|
@ -1179,7 +1201,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) =
|
||||||
var d = c.genx(n[1])
|
var d = c.genx(n[1])
|
||||||
# XXX use ldNullOpcode() here?
|
# XXX use ldNullOpcode() here?
|
||||||
c.gABx(n, opcLdNull, d, c.genType(n[1].typ))
|
c.gABx(n, opcLdNull, d, c.genType(n[1].typ))
|
||||||
c.gABx(n, opcNodeToReg, d, d)
|
c.gABC(n, opcNodeToReg, d, d)
|
||||||
c.genAsgnPatch(n[1], d)
|
c.genAsgnPatch(n[1], d)
|
||||||
of mDefault, mZeroDefault:
|
of mDefault, mZeroDefault:
|
||||||
if dest < 0: dest = c.getTemp(n.typ)
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
|
|
@ -1507,12 +1529,16 @@ proc preventFalseAlias(c: PCtx; n: PNode; opc: TOpcode;
|
||||||
proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
|
proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) =
|
||||||
case le.kind
|
case le.kind
|
||||||
of nkBracketExpr:
|
of nkBracketExpr:
|
||||||
let dest = c.genx(le[0], {gfNode})
|
let
|
||||||
let idx = c.genIndex(le[1], le[0].typ)
|
dest = c.genx(le[0], {gfNode})
|
||||||
let tmp = c.genx(ri)
|
idx = c.genIndex(le[1], le[0].typ)
|
||||||
if le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in {
|
tmp = c.genx(ri)
|
||||||
tyString, tyCstring}:
|
collTyp = le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc})
|
||||||
|
case collTyp.kind
|
||||||
|
of tyString, tyCstring:
|
||||||
c.preventFalseAlias(le, opcWrStrIdx, dest, idx, tmp)
|
c.preventFalseAlias(le, opcWrStrIdx, dest, idx, tmp)
|
||||||
|
of tyTuple:
|
||||||
|
c.preventFalseAlias(le, opcWrObj, dest, int le[1].intVal, tmp)
|
||||||
else:
|
else:
|
||||||
c.preventFalseAlias(le, opcWrArr, dest, idx, tmp)
|
c.preventFalseAlias(le, opcWrArr, dest, idx, tmp)
|
||||||
c.freeTemp(tmp)
|
c.freeTemp(tmp)
|
||||||
|
|
@ -1676,9 +1702,7 @@ proc genArrAccessOpcode(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode;
|
||||||
c.freeTemp(a)
|
c.freeTemp(a)
|
||||||
c.freeTemp(b)
|
c.freeTemp(b)
|
||||||
|
|
||||||
proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
proc genObjAccessAux(c: PCtx; n: PNode; a, b: int, dest: var TDest; flags: TGenFlags) =
|
||||||
let a = c.genx(n[0], flags)
|
|
||||||
let b = genField(c, n[1])
|
|
||||||
if dest < 0: dest = c.getTemp(n.typ)
|
if dest < 0: dest = c.getTemp(n.typ)
|
||||||
if {gfNodeAddr} * flags != {}:
|
if {gfNodeAddr} * flags != {}:
|
||||||
c.gABC(n, opcLdObjAddr, dest, a, b)
|
c.gABC(n, opcLdObjAddr, dest, a, b)
|
||||||
|
|
@ -1691,6 +1715,11 @@ proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||||
c.gABC(n, opcLdObj, dest, a, b)
|
c.gABC(n, opcLdObj, dest, a, b)
|
||||||
c.freeTemp(a)
|
c.freeTemp(a)
|
||||||
|
|
||||||
|
proc genObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||||
|
genObjAccessAux(c, n, c.genx(n[0], flags), genField(c, n[1]), dest, flags)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||||
internalAssert c.config, n.kind == nkCheckedFieldExpr
|
internalAssert c.config, n.kind == nkCheckedFieldExpr
|
||||||
# nkDotExpr to access the requested field
|
# nkDotExpr to access the requested field
|
||||||
|
|
@ -1758,10 +1787,13 @@ proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||||
|
|
||||||
proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) =
|
||||||
let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind
|
let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind
|
||||||
if arrayType in {tyString, tyCstring}:
|
case arrayType
|
||||||
|
of tyString, tyCstring:
|
||||||
let opc = if gfNodeAddr in flags: opcLdStrIdxAddr else: opcLdStrIdx
|
let opc = if gfNodeAddr in flags: opcLdStrIdxAddr else: opcLdStrIdx
|
||||||
genArrAccessOpcode(c, n, dest, opc, flags)
|
genArrAccessOpcode(c, n, dest, opc, flags)
|
||||||
elif arrayType == tyTypeDesc:
|
of tyTuple:
|
||||||
|
c.genObjAccessAux(n, c.genx(n[0], flags), int n[1].intVal, dest, flags)
|
||||||
|
of tyTypeDesc:
|
||||||
c.genTypeLit(n.typ, dest)
|
c.genTypeLit(n.typ, dest)
|
||||||
else:
|
else:
|
||||||
let opc = if gfNodeAddr in flags: opcLdArrAddr else: opcLdArr
|
let opc = if gfNodeAddr in flags: opcLdArrAddr else: opcLdArr
|
||||||
|
|
|
||||||
38
tests/vm/topenarrays.nim
Normal file
38
tests/vm/topenarrays.nim
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
proc mutate(a: var openarray[int]) =
|
||||||
|
var i = 0
|
||||||
|
for x in a.mitems:
|
||||||
|
x = i
|
||||||
|
inc i
|
||||||
|
|
||||||
|
proc mutate(a: var openarray[char]) =
|
||||||
|
var i = 1
|
||||||
|
for ch in a.mitems:
|
||||||
|
ch = 'a'
|
||||||
|
|
||||||
|
|
||||||
|
static:
|
||||||
|
var a = [10, 20, 30]
|
||||||
|
assert a.toOpenArray(1, 2).len == 2
|
||||||
|
|
||||||
|
mutate(a)
|
||||||
|
assert a.toOpenArray(0, 2) == [0, 1, 2]
|
||||||
|
assert a.toOpenArray(0, 0) == [0]
|
||||||
|
assert a.toOpenArray(1, 2) == [1, 2]
|
||||||
|
assert "Hello".toOpenArray(1, 4) == "ello"
|
||||||
|
var str = "Hello"
|
||||||
|
str.toOpenArray(2, 4).mutate()
|
||||||
|
assert str.toOpenArray(0, 4).len == 5
|
||||||
|
assert str.toOpenArray(0, 0).len == 1
|
||||||
|
assert str.toOpenArray(0, 0).high == 0
|
||||||
|
assert str == "Heaaa"
|
||||||
|
assert str.toOpenArray(0, 4) == "Heaaa"
|
||||||
|
|
||||||
|
var arr: array[3..4, int] = [1, 2]
|
||||||
|
assert arr.toOpenArray(3, 4) == [1, 2]
|
||||||
|
assert arr.toOpenArray(3, 4).len == 2
|
||||||
|
assert arr.toOpenArray(3, 3).high == 0
|
||||||
|
|
||||||
|
assert arr.toOpenArray(3, 4).toOpenArray(0, 0) == [1]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue