Skip to content

Commit

Permalink
fixes #15939, fixes #15464, fixes #16169, fixes #16226 VM now support…
Browse files Browse the repository at this point in the history
…s `addr(mystring[ind])` (index + index assignment) (#15987)

* fix #15939, fix #15464 VM now supports `addr(mystring[ind])` (index + index assignment), var char return etc
* cleanups
* cstring tests
* add test for bug #15464
* improve test coverage
  • Loading branch information
timotheecour authored Dec 3, 2020
1 parent b1554cf commit c731f7a
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 5 deletions.
1 change: 1 addition & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 20 additions & 1 deletion compiler/vm.nim
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import

from semfold import leValueConv, ordinalValToString
from evaltempl import evalTemplate
from magicsys import getSysType

const
traceCode = defined(nimVMDebug)
Expand Down Expand Up @@ -123,14 +124,15 @@ 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
return true

## 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)
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions compiler/vmdef.nim
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ type
opcWrDeref,
opcWrStrIdx,
opcLdStrIdx, # a = b[c]
opcLdStrIdxAddr, # a = addr(b[c])

opcAddInt,
opcAddImmInt,
Expand Down
10 changes: 6 additions & 4 deletions compiler/vmgen.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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:
Expand Down
66 changes: 66 additions & 0 deletions tests/misc/taddr.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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()

0 comments on commit c731f7a

Please sign in to comment.