diff --git a/changelog.md b/changelog.md index d62e96bbed78b..5b22a174b5d77 100644 --- a/changelog.md +++ b/changelog.md @@ -69,6 +69,7 @@ - Added `nim --eval:cmd` to evaluate a command directly, see `nim --help`. +- VM now supports `addr(mystring[ind])` (index + index assignment) ## Tool changes diff --git a/compiler/vm.nim b/compiler/vm.nim index 17bbe65fc9845..34d76f21e7d8d 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -20,6 +20,7 @@ import from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate +from magicsys import getSysType const traceCode = defined(nimVMDebug) @@ -123,7 +124,7 @@ proc derefPtrToReg(address: BiggestInt, typ: PType, r: var TFullReg, isAssign: b else: r.ensureKind(rkind) let val = cast[ptr T](address)[] - when T is SomeInteger: + when T is SomeInteger | char: r.field = BiggestInt(val) else: r.field = val @@ -131,6 +132,7 @@ proc derefPtrToReg(address: BiggestInt, typ: PType, r: var TFullReg, isAssign: b ## see also typeinfo.getBiggestInt case typ.kind + of tyChar: fun(intVal, char, rkInt) of tyInt: fun(intVal, int, rkInt) of tyInt8: fun(intVal, int8, rkInt) of tyInt16: fun(intVal, int16, rkInt) @@ -677,6 +679,23 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = s[idx].ord else: stackTrace(c, tos, pc, formatErrorIndexBound(idx, s.len-1)) + of opcLdStrIdxAddr: + # a = addr(b[c]); similar to opcLdArrAddr + decodeBC(rkNode) + if regs[rc].intVal > high(int): + stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int))) + let idx = regs[rc].intVal.int + let s = regs[rb].node.strVal.addr # or `byaddr` + if idx <% s[].len: + # `makePtrType` not accessible from vm.nim + let typ = newType(tyPtr, nextId 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: + stackTrace(c, tos, pc, formatErrorIndexBound(idx, s[].len-1)) of opcWrArr: # a[b] = c decodeBC(rkNode) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 202087af771c2..6d6c552508a25 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -78,6 +78,7 @@ type opcWrDeref, opcWrStrIdx, opcLdStrIdx, # a = b[c] + opcLdStrIdxAddr, # a = addr(b[c]) opcAddInt, opcAddImmInt, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 84ff1a91d2673..29ab8739dd241 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1654,8 +1654,8 @@ proc genArrAccessOpcode(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; let a = c.genx(n[0], flags) let b = c.genIndex(n[1], n[0].typ) if dest < 0: dest = c.getTemp(n.typ) - if opc == opcLdArr and {gfNodeAddr} * flags != {}: - c.gABC(n, opcLdArrAddr, dest, a, b) + if opc in {opcLdArrAddr, opcLdStrIdxAddr} and gfNodeAddr in flags: + c.gABC(n, opc, dest, a, b) elif needsRegLoad(): var cc = c.getTemp(n.typ) c.gABC(n, opc, cc, a, b) @@ -1749,11 +1749,13 @@ proc genCheckedObjAccess(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 if arrayType in {tyString, tyCString}: - genArrAccessOpcode(c, n, dest, opcLdStrIdx, {}) + let opc = if gfNodeAddr in flags: opcLdStrIdxAddr else: opcLdStrIdx + genArrAccessOpcode(c, n, dest, opc, flags) elif arrayType == tyTypeDesc: c.genTypeLit(n.typ, dest) else: - genArrAccessOpcode(c, n, dest, opcLdArr, flags) + let opc = if gfNodeAddr in flags: opcLdArrAddr else: opcLdArr + genArrAccessOpcode(c, n, dest, opc, flags) proc getNullValueAux(t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) = if t != nil and t.len > 0 and t[0] != nil: diff --git a/tests/misc/taddr.nim b/tests/misc/taddr.nim index d743d4b4a4d9e..bac26896d32ab 100644 --- a/tests/misc/taddr.nim +++ b/tests/misc/taddr.nim @@ -196,10 +196,76 @@ template test14339() = # bug #14339 when not defined(js): # pending bug #16003 doAssert a.val == 5 +template testStatic15464() = # bug #15464 + proc access(s: var seq[char], i: int): var char = s[i] + proc access(s: var string, i: int): var char = s[i] + static: + var s = @['a', 'b', 'c'] + access(s, 2) = 'C' + doAssert access(s, 2) == 'C' + static: + var s = "abc" + access(s, 2) = 'C' + doAssert access(s, 2) == 'C' + +proc test15464() = # bug #15464 (v2) + proc access(s: var seq[char], i: int): var char = s[i] + proc access(s: var string, i: int): var char = s[i] + block: + var s = @['a', 'b', 'c'] + access(s, 2) = 'C' + doAssert access(s, 2) == 'C' + block: + var s = "abc" + access(s, 2) = 'C' + doAssert access(s, 2) == 'C' + +block: # bug #15939 + block: + const foo = "foo" + proc proc1(s: var string) = + if s[^1] notin {'a'..'z'}: + s = "" + proc proc2(f: string): string = + result = f + proc1(result) + const bar = proc2(foo) + doAssert bar == "foo" + +proc test15939() = # bug #15939 (v2) + template fn(a) = + let pa = a[0].addr + doAssert pa != nil + doAssert pa[] == 'a' + pa[] = 'x' + doAssert pa[] == 'x' + doAssert a == "xbc" + when not defined js: # otherwise overflows + let pa2 = cast[ptr char](cast[int](pa) + 1) + doAssert pa2[] == 'b' + pa2[] = 'B' + doAssert a == "xBc" + + # mystring[ind].addr + var a = "abc" + fn(a) + + # mycstring[ind].addr + template cstringTest = + var a2 = "abc" + var b2 = a2.cstring + fn(b2) + when nimvm: cstringTest() + else: # can't take address of cstring element in js + when not defined(js): cstringTest() + template main = # xxx wrap all other tests here like that so they're also tested in VM test14420() test14339() + test15464() + test15939() +testStatic15464() static: main() main()