deprecate unsafeAddr; extend addr (#19373)

* deprecate unsafeAddr; extend addr

addr is now available for all addressable locations, unsafeAddr is deprecated and become an alias for addr

* follow @Vindaar's advice

* change the signature of addr

* unsafeAddr => addr (stdlib)

* Update changelog.md

* unsafeAddr => addr (tests)

* Revert "unsafeAddr => addr (stdlib)"

This reverts commit ab83c99c507048a8396e636bf22d55fdd84d7d1c.

* doc changes; thanks to @konsumlamm

Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com>

Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com>
This commit is contained in:
flywind 2022-01-16 18:08:38 +08:00 • committed by GitHub
commit d102b2f54c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
30 changed files with 69 additions and 66 deletions

View file

@ -17,6 +17,9 @@
- `std/sharedstrings` module is removed. - `std/sharedstrings` module is removed.
- Constants `colors.colPaleVioletRed` and `colors.colMediumPurple` changed to match the CSS color standard. - Constants `colors.colPaleVioletRed` and `colors.colMediumPurple` changed to match the CSS color standard.
- `addr` is now available for all addressable locations, `unsafeAddr` is deprecated and
becomes an alias for `addr`.
## Standard library additions and changes ## Standard library additions and changes
- `macros.parseExpr` and `macros.parseStmt` now accept an optional - `macros.parseExpr` and `macros.parseStmt` now accept an optional

View file

@ -71,7 +71,7 @@ proc isValueOnlyType(t: PType): bool =
proc canAlias*(arg, ret: PType): bool = proc canAlias*(arg, ret: PType): bool =
if isValueOnlyType(arg): if isValueOnlyType(arg):
# can alias only with unsafeAddr(arg.x) and we don't care if it is not safe # can alias only with addr(arg.x) and we don't care if it is not safe
result = false result = false
else: else:
var marker = initIntSet() var marker = initIntSet()

View file

@ -14,13 +14,8 @@ proc semAddrArg(c: PContext; n: PNode; isUnsafeAddr = false): PNode =
let x = semExprWithType(c, n) let x = semExprWithType(c, n)
if x.kind == nkSym: if x.kind == nkSym:
x.sym.flags.incl(sfAddrTaken) x.sym.flags.incl(sfAddrTaken)
if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}: if isAssignable(c, x, true) notin {arLValue, arLocalLValue}:
# Do not suggest the use of unsafeAddr if this expression already is a localError(c.config, n.info, errExprHasNoAddress)
# unsafeAddr
if isUnsafeAddr:
localError(c.config, n.info, errExprHasNoAddress)
else:
localError(c.config, n.info, errExprHasNoAddress & "; maybe use 'unsafeAddr'")
result = x result = x
proc semTypeOf(c: PContext; n: PNode): PNode = proc semTypeOf(c: PContext; n: PNode): PNode =

View file

@ -3502,8 +3502,9 @@ location is `T`, the `addr` operator result is of the type `ptr T`. An
address is always an untraced reference. Taking the address of an object that address is always an untraced reference. Taking the address of an object that
resides on the stack is **unsafe**, as the pointer may live longer than the resides on the stack is **unsafe**, as the pointer may live longer than the
object on the stack and can thus reference a non-existing object. One can get object on the stack and can thus reference a non-existing object. One can get
the address of variables, but one can't use it on variables declared through the address of variables. For easier interoperability with other compiled languages
`let` statements: such as C, retrieving the address of a `let` variable, a parameter,
or a `for` loop variable can be accomplished too:
.. code-block:: nim .. code-block:: nim
@ -3515,24 +3516,18 @@ the address of variables, but one can't use it on variables declared through
# --> ref 0x7fff6b71b670 --> 0x10bb81050"Hello" # --> ref 0x7fff6b71b670 --> 0x10bb81050"Hello"
echo cast[ptr string](t3)[] echo cast[ptr string](t3)[]
# --> Hello # --> Hello
# The following line doesn't compile: # The following line also works
echo repr(addr(t1)) echo repr(addr(t1))
# Error: expression has no address
The unsafeAddr operator The unsafeAddr operator
----------------------- -----------------------
For easier interoperability with other compiled languages such as C, retrieving The `unsafeAddr` operator is a deprecated alias for the `addr` operator:
the address of a `let` variable, a parameter, or a `for` loop variable can
be accomplished by using the `unsafeAddr` operation:
.. code-block:: nim .. code-block:: nim
let myArray = [1, 2, 3] let myArray = [1, 2, 3]
foreignProcThatTakesAnAddr(unsafeAddr myArray) foreignProcThatTakesAnAddr(unsafeAddr myArray)
Procedures Procedures
========== ==========

View file

@ -603,7 +603,7 @@ has `source` as the owner. A path expression `e` is defined recursively:
- Object field access `e.field` is a path expression. - Object field access `e.field` is a path expression.
- `system.toOpenArray(e, ...)` is a path expression. - `system.toOpenArray(e, ...)` is a path expression.
- Pointer dereference `e[]` is a path expression. - Pointer dereference `e[]` is a path expression.
- An address `addr e`, `unsafeAddr e` is a path expression. - An address `addr e` is a path expression.
- A type conversion `T(e)` is a path expression. - A type conversion `T(e)` is a path expression.
- A cast expression `cast[T](e)` is a path expression. - A cast expression `cast[T](e)` is a path expression.
- `f(e, ...)` is a path expression if `f`'s return type is a view type. - `f(e, ...)` is a path expression if `f`'s return type is a view type.

View file

@ -190,8 +190,16 @@ when defined(nimHasDeclaredMagic):
else: else:
proc declaredInScope*(x: untyped): bool {.magic: "DefinedInScope", noSideEffect, compileTime.} proc declaredInScope*(x: untyped): bool {.magic: "DefinedInScope", noSideEffect, compileTime.}
proc `addr`*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} = proc `addr`*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} =
## Builtin `addr` operator for taking the address of a memory location. ## Builtin `addr` operator for taking the address of a memory location.
##
## .. note:: This works for `let` variables or parameters
## for better interop with C. When you use it to write a wrapper
## for a C library and take the address of `let` variables or parameters,
## you should always check that the original library
## does never write to data behind the pointer that is returned from
## this procedure.
##
## Cannot be overloaded. ## Cannot be overloaded.
## ##
## See also: ## See also:
@ -205,15 +213,17 @@ proc `addr`*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} =
## echo p[] # b ## echo p[] # b
discard discard
proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect,
deprecated: "'unsafeAddr' is a deprecated alias for 'addr'".} =
## Builtin `addr` operator for taking the address of a memory ## Builtin `addr` operator for taking the address of a memory
## location. This works even for `let` variables or parameters ## location.
## for better interop with C and so it is considered even more
## unsafe than the ordinary `addr <#addr,T>`_.
## ##
## **Note**: When you use it to write a wrapper for a C library, you should ## .. note:: This works for `let` variables or parameters
## always check that the original library does never write to data behind the ## for better interop with C. When you use it to write a wrapper
## pointer that is returned from this procedure. ## for a C library and take the address of `let` variables or parameters,
## you should always check that the original library
## does never write to data behind the pointer that is returned from
## this procedure.
## ##
## Cannot be overloaded. ## Cannot be overloaded.
discard discard

View file

@ -130,7 +130,7 @@ proc createSeq*[T](elems: varargs[T]): myseq[T] =
result.data = cast[type(result.data)](alloc0(result.cap * sizeof(T))) result.data = cast[type(result.data)](alloc0(result.cap * sizeof(T)))
inc allocCount inc allocCount
when supportsCopyMem(T): when supportsCopyMem(T):
copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T)) copyMem(result.data, addr(elems[0]), result.cap * sizeof(T))
else: else:
for i in 0..<result.len: for i in 0..<result.len:
result.data[i] = elems[i] result.data[i] = elems[i]

View file

@ -18,7 +18,7 @@ type
proc set1(x: float): m256d {.importc: "_mm256_set1_pd", header: "immintrin.h".} proc set1(x: float): m256d {.importc: "_mm256_set1_pd", header: "immintrin.h".}
func `+`(a,b: m256d): m256d {.importc: "_mm256_add_pd", header: "immintrin.h".} func `+`(a,b: m256d): m256d {.importc: "_mm256_add_pd", header: "immintrin.h".}
proc `$`(a: m256d): string = proc `$`(a: m256d): string =
result = $(cast[ptr float](a.unsafeAddr)[]) result = $(cast[ptr float](a.addr)[])
var res: seq[seq[m256d]] var res: seq[seq[m256d]]

View file

@ -234,7 +234,7 @@ when not defined(windows):
if fd == -1: if fd == -1:
raiseOsError(osLastError()) raiseOsError(osLastError())
let length = len(data).cint let length = len(data).cint
if posix.write(fd, cast[pointer](unsafeAddr data[0]), if posix.write(fd, cast[pointer](addr data[0]),
len(data).cint) != length: len(data).cint) != length:
raiseOsError(osLastError()) raiseOsError(osLastError())
if posix.close(fd) == -1: if posix.close(fd) == -1:

View file

@ -7,7 +7,7 @@ type
field {.noalias.}: ptr UncheckedArray[int] field {.noalias.}: ptr UncheckedArray[int]
proc p(x {.noalias.}: openArray[char]) = proc p(x {.noalias.}: openArray[char]) =
var q {.noalias.}: pointer = unsafeAddr(x[0]) var q {.noalias.}: pointer = addr(x[0])
var bn: BigNum var bn: BigNum
p "abc" p "abc"

View file

@ -7,7 +7,7 @@ bar
proc test(strings: seq[string]) = proc test(strings: seq[string]) =
for s in strings: for s in strings:
var p3 = unsafeAddr(s) var p3 = addr(s)
echo p3[] echo p3[]
test(@["foo", "bar"]) test(@["foo", "bar"])

View file

@ -484,10 +484,10 @@ type
var address = pointer(nil) var address = pointer(nil)
proc prod(r: var QuadraticExt, b: QuadraticExt) = proc prod(r: var QuadraticExt, b: QuadraticExt) =
if address == nil: if address == nil:
address = unsafeAddr b address = addr b
prod(r, b) prod(r, b)
else: else:
assert address == unsafeAddr b assert address == addr b
type type
Fp2[N: static int, T] {.byref.} = object Fp2[N: static int, T] {.byref.} = object

View file

@ -9,11 +9,11 @@ copy length and contents 1 @[42]
proc test() = proc test() =
var sq1 = @[42] var sq1 = @[42]
echo "showing original type, length, and contents ", sq1.typeof, " ", sq1.len, " ", sq1 echo "showing original type, length, and contents ", sq1.typeof, " ", sq1.len, " ", sq1
doAssert cast[int](sq1[0].unsafeAddr) != 0 doAssert cast[int](sq1[0].addr) != 0
var sq2 = sq1 # copy of original var sq2 = sq1 # copy of original
echo "copy length and contents ", sq2.len, " ", sq2 echo "copy length and contents ", sq2.len, " ", sq2
doAssert cast[int](sq2[0].unsafeAddr) != 0 doAssert cast[int](sq2[0].addr) != 0
doAssert cast[int](sq1[0].unsafeAddr) != 0 doAssert cast[int](sq1[0].addr) != 0
test() test()

View file

@ -63,7 +63,7 @@ proc fillPages*(mem: UserProcessMemory, start: uint32, data: seq[byte]) =
#echo cast[uint64](addr mem.pageAccess[i]) #echo cast[uint64](addr mem.pageAccess[i])
let page = mem.pageAccess[i] let page = mem.pageAccess[i]
assert page != nil assert page != nil
#copyMem(page, unsafeAddr data[i * 0x1000 - start], 0x1000) #copyMem(page, addr data[i * 0x1000 - start], 0x1000)
const base = 0x00100000 const base = 0x00100000

View file

@ -121,7 +121,7 @@ proc createSeq*[T](elems: varargs[T]): myseq[T] =
result.data = cast[type(result.data)](alloc(result.cap * sizeof(T))) result.data = cast[type(result.data)](alloc(result.cap * sizeof(T)))
inc allocCount inc allocCount
when supportsCopyMem(T): when supportsCopyMem(T):
copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T)) copyMem(result.data, addr(elems[0]), result.cap * sizeof(T))
else: else:
for i in 0..<result.len: for i in 0..<result.len:
result.data[i] = elems[i] result.data[i] = elems[i]

View file

@ -75,7 +75,7 @@ proc add*(self: var mystring; y: mystring) =
proc create*(lit: string): mystring = proc create*(lit: string): mystring =
let newLen = lit.len let newLen = lit.len
ensure(result, newLen) ensure(result, newLen)
copyMem(addr result.data[result.len], unsafeAddr lit[0], newLen + 1) copyMem(addr result.data[result.len], addr lit[0], newLen + 1)
result.len = newLen result.len = newLen
proc `&`*(a, b: mystring): mystring = proc `&`*(a, b: mystring): mystring =

View file

@ -18,7 +18,7 @@ proc `=sink`(self: var Foo; other: Foo) =
proc `=destroy`(self: var Foo) = discard proc `=destroy`(self: var Foo) = discard
template preventCursorInference(x) = template preventCursorInference(x) =
let p = unsafeAddr(x) let p = addr(x)
proc test(): Foo = proc test(): Foo =
result = Foo() result = Foo()

View file

@ -3,5 +3,5 @@ discard """
line: 7 line: 7
""" """
template foo(v: varargs[int]) = unsafeAddr v template foo(v: varargs[int]) = addr v
foo(1, 2) foo(1, 2)

View file

@ -34,7 +34,7 @@ proc write(rw: var MyReadWrite; value: SomeNumber): void =
proc write[T](rw: var MyReadWrite; value: seq[T]): void = proc write[T](rw: var MyReadWrite; value: seq[T]): void =
rw.write value.len rw.write value.len
let dst = cast[ptr SomeNumber](cast[uint](rw.memfile.mem) + uint(rw.offset)) let dst = cast[ptr SomeNumber](cast[uint](rw.memfile.mem) + uint(rw.offset))
let src = cast[pointer](value[0].unsafeAddr) let src = cast[pointer](value[0].addr)
let size = sizeof(T) * value.len let size = sizeof(T) * value.len
copyMem(dst, src, size) copyMem(dst, src, size)
rw.offset += size rw.offset += size

View file

@ -30,7 +30,7 @@ block:
block: block:
let a0 = addr(obj.f0) let a0 = addr(obj.f0)
echo a0[] echo a0[]
# let a1 = unsafeAddr(obj.f1) # let a1 = addr(obj.f1)
# echo a1[] # echo a1[]
doAssertRaises(FieldDefect): doAssertRaises(FieldDefect):
let a2 = addr(obj.f2) let a2 = addr(obj.f2)

View file

@ -22,12 +22,12 @@ template main2 = # bug #15958
let b = @[21,23] let b = @[21,23]
let ss = {1, 2, 3, 5} let ss = {1, 2, 3, 5}
doAssert byLent(a) == [11,12] doAssert byLent(a) == [11,12]
doAssert byLent(a).unsafeAddr == a.unsafeAddr doAssert byLent(a).addr == a.addr
doAssert byLent(b) == @[21,23] doAssert byLent(b) == @[21,23]
when not defined(js): # pending bug #16073 when not defined(js): # pending bug #16073
doAssert byLent(b).unsafeAddr == b.unsafeAddr doAssert byLent(b).addr == b.addr
doAssert byLent(ss) == {1, 2, 3, 5} doAssert byLent(ss) == {1, 2, 3, 5}
doAssert byLent(ss).unsafeAddr == ss.unsafeAddr doAssert byLent(ss).addr == ss.addr
let r = new(float) let r = new(float)
r[] = 10.0 r[] = 10.0
@ -41,9 +41,9 @@ template main2 = # bug #15958
proc byLent2[T](a: openArray[T]): lent T = a[0] proc byLent2[T](a: openArray[T]): lent T = a[0]
doAssert byLent2(a) == 11 doAssert byLent2(a) == 11
doAssert byLent2(a).unsafeAddr == a[0].unsafeAddr doAssert byLent2(a).addr == a[0].addr
doAssert byLent2(b) == 21 doAssert byLent2(b) == 21
doAssert byLent2(b).unsafeAddr == b[0].unsafeAddr doAssert byLent2(b).addr == b[0].addr
proc byLent3[T](a: varargs[T]): lent T = a[1] proc byLent3[T](a: varargs[T]): lent T = a[1]
let let

View file

@ -83,7 +83,7 @@ block:
# bug #14576 # bug #14576
# lots of these used to give: Error: internal error: genAddr: 2 # lots of these used to give: Error: internal error: genAddr: 2
proc byLent[T](a: T): lent T = a proc byLent[T](a: T): lent T = a
proc byPtr[T](a: T): ptr T = a.unsafeAddr proc byPtr[T](a: T): ptr T = a.addr
block: block:
let a = (10,11) let a = (10,11)

View file

@ -4,7 +4,7 @@ block:
template `[]`[T](p: ptr T, span: Slice[int]): untyped = template `[]`[T](p: ptr T, span: Slice[int]): untyped =
toOpenArray(cast[ptr array[0, T]](p)[], span.a, span.b) toOpenArray(cast[ptr array[0, T]](p)[], span.a, span.b)
doAssert $cast[ptr uint8](txt[0].unsafeAddr)[0 ..< txt.len] == doAssert $cast[ptr uint8](txt[0].addr)[0 ..< txt.len] ==
"[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]" "[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]"
@ -14,5 +14,5 @@ block:
template `[]`[T](p: ptr T, span: Slice[int]): untyped = template `[]`[T](p: ptr T, span: Slice[int]): untyped =
toOpenArray(cast[ptr array[0, T]](p)[], span.a, span.b) toOpenArray(cast[ptr array[0, T]](p)[], span.a, span.b)
doAssert $cast[ptr uint8](txt[0].unsafeAddr)[0 ..< txt.len] == doAssert $cast[ptr uint8](txt[0].addr)[0 ..< txt.len] ==
"[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]" "[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]"

View file

@ -42,7 +42,7 @@ proc `^`(x: vfloat, exp: static[float]): vfloat =
pow(x, exp) pow(x, exp)
proc `$`(x: vfloat): string = proc `$`(x: vfloat): string =
let y = cast[ptr float](unsafeAddr x) let y = cast[ptr float](addr x)
# xxx not sure if intentional in this issue, but this returns "" # xxx not sure if intentional in this issue, but this returns ""
echo y[] echo y[]

View file

@ -48,37 +48,37 @@ when false: # pending bug #13887
## We can define custom pragmas in user code ## We can define custom pragmas in user code
template byUnsafeAddr(lhs, typ, expr) = template byUnsafeAddr(lhs, typ, expr) =
when typ is type(nil): when typ is type(nil):
let tmp = unsafeAddr(expr) let tmp = addr(expr)
else: else:
let tmp: ptr typ = unsafeAddr(expr) let tmp: ptr typ = addr(expr)
template lhs: untyped = tmp[] template lhs: untyped = tmp[]
block: block:
let s = @["foo", "bar"] let s = @["foo", "bar"]
let a {.byUnsafeAddr.} = s[0] let a {.byUnsafeAddr.} = s[0]
doAssert a == "foo" doAssert a == "foo"
doAssert a[0].unsafeAddr == s[0][0].unsafeAddr doAssert a[0].addr == s[0][0].addr
block: # nkAccQuoted block: # nkAccQuoted
# shows using a keyword, which requires nkAccQuoted # shows using a keyword, which requires nkAccQuoted
template `cast`(lhs, typ, expr) = template `cast`(lhs, typ, expr) =
when typ is type(nil): when typ is type(nil):
let tmp = unsafeAddr(expr) let tmp = addr(expr)
else: else:
let tmp: ptr typ = unsafeAddr(expr) let tmp: ptr typ = addr(expr)
template lhs: untyped = tmp[] template lhs: untyped = tmp[]
block: block:
let s = @["foo", "bar"] let s = @["foo", "bar"]
let a {.`byUnsafeAddr`.} = s[0] let a {.`byUnsafeAddr`.} = s[0]
doAssert a == "foo" doAssert a == "foo"
doAssert a[0].unsafeAddr == s[0][0].unsafeAddr doAssert a[0].addr == s[0][0].addr
block: block:
let s = @["foo", "bar"] let s = @["foo", "bar"]
let a {.`cast`.} = s[0] let a {.`cast`.} = s[0]
doAssert a == "foo" doAssert a == "foo"
doAssert a[0].unsafeAddr == s[0][0].unsafeAddr doAssert a[0].addr == s[0][0].addr
block: # bug #15920 block: # bug #15920
template foo(lhs, typ, expr) = template foo(lhs, typ, expr) =

View file

@ -12,4 +12,4 @@ block: # cmpMem
doAssert cmpMem(a.addr, b.addr, sizeof(SomeHash)) > 0 doAssert cmpMem(a.addr, b.addr, sizeof(SomeHash)) > 0
doAssert cmpMem(b.addr, a.addr, sizeof(SomeHash)) < 0 doAssert cmpMem(b.addr, a.addr, sizeof(SomeHash)) < 0
doAssert cmpMem(a.addr, c.unsafeAddr, sizeof(SomeHash)) == 0 doAssert cmpMem(a.addr, c.addr, sizeof(SomeHash)) == 0

View file

@ -27,7 +27,7 @@ block tsqlitebindatas: ## db_sqlite binary data
db.exec(createTableStr) db.exec(createTableStr)
var dbuf = newSeq[byte](orig.len*sizeof(float64)) var dbuf = newSeq[byte](orig.len*sizeof(float64))
copyMem(unsafeAddr(dbuf[0]), unsafeAddr(orig[0]), dbuf.len) copyMem(addr(dbuf[0]), addr(orig[0]), dbuf.len)
var insertStmt = db.prepare("INSERT INTO test (id, name, data) VALUES (?, ?, ?)") var insertStmt = db.prepare("INSERT INTO test (id, name, data) VALUES (?, ?, ?)")
insertStmt.bindParams(1, origName, dbuf) insertStmt.bindParams(1, origName, dbuf)
@ -42,7 +42,7 @@ block tsqlitebindatas: ## db_sqlite binary data
var dataTest = db.getValue(sql"SELECT data FROM test WHERE id = ?", 1) var dataTest = db.getValue(sql"SELECT data FROM test WHERE id = ?", 1)
let seqSize = int(dataTest.len*sizeof(byte)/sizeof(float64)) let seqSize = int(dataTest.len*sizeof(byte)/sizeof(float64))
var res: seq[float64] = newSeq[float64](seqSize) var res: seq[float64] = newSeq[float64](seqSize)
copyMem(unsafeAddr(res[0]), addr(dataTest[0]), dataTest.len) copyMem(addr(res[0]), addr(dataTest[0]), dataTest.len)
doAssert res.len == orig.len doAssert res.len == orig.len
doAssert res == orig doAssert res == orig

View file

@ -194,7 +194,7 @@ proc testAliasChanging(a: Nilable) =
# # proc testPtrAlias(a: Nilable) = # # proc testPtrAlias(a: Nilable) =
# # # pointer to a: hm. # # # pointer to a: hm.
# # # alias to a? # # # alias to a?
# # var ptrA = a.unsafeAddr # {0, 1} # # var ptrA = a.addr # {0, 1}
# # if not a.isNil: # {0, 1} # # if not a.isNil: # {0, 1}
# # ptrA[] = nil # {0, 1} 0: MaybeNil 1: MaybeNil # # ptrA[] = nil # {0, 1} 0: MaybeNil 1: MaybeNil
# # echo a.a #[ tt.Warning # # echo a.a #[ tt.Warning

View file

@ -47,7 +47,7 @@ import strutils
let arr = ['H','e','l','l','o',' ','W','o','r','l','d','!','\0'] let arr = ['H','e','l','l','o',' ','W','o','r','l','d','!','\0']
doAssert $arr == "['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\\x00']" doAssert $arr == "['H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\\x00']"
doAssert $cstring(unsafeAddr arr) == "Hello World!" doAssert $cstring(addr arr) == "Hello World!"
proc takes(c: cstring) = proc takes(c: cstring) =
doAssert c == cstring"" doAssert c == cstring""

View file

@ -15,7 +15,7 @@ proc test_var(x: var MyObj): var int =
var x = MyObj(a: 5) var x = MyObj(a: 5)
doAssert: test_var(x).addr == x.a.addr doAssert: test_var(x).addr == x.a.addr
doAssert: test_lent(x).unsafeAddr == x.a.addr doAssert: test_lent(x).addr == x.a.addr
proc varProc(x: var int) = proc varProc(x: var int) =
x = 100 x = 100