new syntax for lvalue references: var b {.byaddr.} = expr (#13508)
* new syntax for lvalue references: `var b {.byaddr.} = expr`
* on type mismatch, `???(0, 0)` not shown anymore
* * compiler now lowers `var a: {.foo.}: MyType = expr` to foo(a, MyType, expr)
* new pragmas.byaddr defined in pure library code exploiting this lowering
* skip `template foo() {.pragma.}`
This commit is contained in:
parent
fa06203e90
commit
913bc95964
4 changed files with 139 additions and 1 deletions
|
|
@ -149,7 +149,7 @@ echo f
|
||||||
- `std/oswalkdir` was buggy, it's now deprecated and reuses `std/os` procs
|
- `std/oswalkdir` was buggy, it's now deprecated and reuses `std/os` procs
|
||||||
- `net.newContext` now performs SSL Certificate checking on Linux and OSX.
|
- `net.newContext` now performs SSL Certificate checking on Linux and OSX.
|
||||||
Define `nimDisableCertificateValidation` to disable it globally.
|
Define `nimDisableCertificateValidation` to disable it globally.
|
||||||
|
- new syntax for lvalue references: `var b {.byaddr.} = expr` enabled by `import pragmas`
|
||||||
|
|
||||||
## Language additions
|
## Language additions
|
||||||
|
|
||||||
|
|
@ -159,6 +159,9 @@ echo f
|
||||||
- `=sink` type bound operator is now optional. Compiler can now use combination
|
- `=sink` type bound operator is now optional. Compiler can now use combination
|
||||||
of `=destroy` and `copyMem` to move objects efficiently.
|
of `=destroy` and `copyMem` to move objects efficiently.
|
||||||
|
|
||||||
|
- `var a {.foo.}: MyType = expr` now lowers to `foo(a, MyType, expr)` for non builtin pragmas,
|
||||||
|
enabling things like lvalue references, see `pragmas.byaddr`
|
||||||
|
|
||||||
## Language changes
|
## Language changes
|
||||||
|
|
||||||
- Unsigned integer operators have been fixed to allow promotion of the first operand.
|
- Unsigned integer operators have been fixed to allow promotion of the first operand.
|
||||||
|
|
|
||||||
|
|
@ -431,7 +431,53 @@ proc setVarType(c: PContext; v: PSym, typ: PType) =
|
||||||
"; new type is: " & typeToString(typ, preferDesc))
|
"; new type is: " & typeToString(typ, preferDesc))
|
||||||
v.typ = typ
|
v.typ = typ
|
||||||
|
|
||||||
|
proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode =
|
||||||
|
var b = a[0]
|
||||||
|
if b.kind == nkPragmaExpr:
|
||||||
|
if b[1].len != 1:
|
||||||
|
# we could in future support pragmas w args eg: `var foo {.bar:"goo".} = expr`
|
||||||
|
return nil
|
||||||
|
let nodePragma = b[1][0]
|
||||||
|
# see: `singlePragma`
|
||||||
|
if nodePragma.kind notin {nkIdent, nkAccQuoted}:
|
||||||
|
return nil
|
||||||
|
let ident = considerQuotedIdent(c, nodePragma)
|
||||||
|
var userPragma = strTableGet(c.userPragmas, ident)
|
||||||
|
if userPragma != nil: return nil
|
||||||
|
|
||||||
|
let w = nodePragma.whichPragma
|
||||||
|
if n.kind == nkVarSection and w in varPragmas or
|
||||||
|
n.kind == nkLetSection and w in letPragmas or
|
||||||
|
n.kind == nkConstSection and w in constPragmas:
|
||||||
|
return nil
|
||||||
|
|
||||||
|
let sym = searchInScopes(c, ident)
|
||||||
|
if sfCustomPragma in sym.flags: return nil # skip `template myAttr() {.pragma.}`
|
||||||
|
let lhs = b[0]
|
||||||
|
let clash = strTableGet(c.currentScope.symbols, lhs.ident)
|
||||||
|
if clash != nil:
|
||||||
|
# refs https://github.com/nim-lang/Nim/issues/8275
|
||||||
|
wrongRedefinition(c, lhs.info, lhs.ident.s, clash.info)
|
||||||
|
|
||||||
|
result = newTree(nkCall)
|
||||||
|
doAssert nodePragma.kind in {nkIdent, nkAccQuoted}, $nodePragma.kind
|
||||||
|
result.add nodePragma
|
||||||
|
result.add lhs
|
||||||
|
if a[1].kind != nkEmpty:
|
||||||
|
result.add a[1]
|
||||||
|
else:
|
||||||
|
result.add newNodeIT(nkNilLit, a.info, c.graph.sysTypes[tyNil])
|
||||||
|
result.add a[2]
|
||||||
|
result.info = a.info
|
||||||
|
let ret = newNodeI(nkStmtList, a.info)
|
||||||
|
ret.add result
|
||||||
|
result = semExprNoType(c, ret)
|
||||||
|
|
||||||
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
|
if n.len == 1:
|
||||||
|
result = semLowerLetVarCustomPragma(c, n[0], n)
|
||||||
|
if result!=nil: return result
|
||||||
|
|
||||||
var b: PNode
|
var b: PNode
|
||||||
result = copyNode(n)
|
result = copyNode(n)
|
||||||
for i in 0..<n.len:
|
for i in 0..<n.len:
|
||||||
|
|
|
||||||
19
lib/std/pragmas.nim
Normal file
19
lib/std/pragmas.nim
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
# see `semLowerLetVarCustomPragma` for compiler support that enables these
|
||||||
|
# lowerings
|
||||||
|
|
||||||
|
template byaddr*(lhs, typ, expr) =
|
||||||
|
## Allows a syntax for lvalue reference, exact analog to
|
||||||
|
## `auto& a = expr;` in C++
|
||||||
|
runnableExamples:
|
||||||
|
var s = @[10,11,12]
|
||||||
|
var a {.byaddr.} = s[0]
|
||||||
|
a+=100
|
||||||
|
doAssert s == @[110,11,12]
|
||||||
|
doAssert a is int
|
||||||
|
var b {.byaddr.}: int = s[0]
|
||||||
|
doAssert a.addr == b.addr
|
||||||
|
when typ is type(nil):
|
||||||
|
let tmp = addr(expr)
|
||||||
|
else:
|
||||||
|
let tmp: ptr typ = addr(expr)
|
||||||
|
template lhs: untyped = tmp[]
|
||||||
70
tests/stdlib/tpragmas.nim
Normal file
70
tests/stdlib/tpragmas.nim
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
import std/pragmas
|
||||||
|
|
||||||
|
block:
|
||||||
|
var s = @[10,11,12]
|
||||||
|
var a {.byaddr.} = s[0]
|
||||||
|
a+=100
|
||||||
|
doAssert s == @[110,11,12]
|
||||||
|
doAssert a is int
|
||||||
|
var b {.byaddr.}: int = s[0]
|
||||||
|
doAssert a.addr == b.addr
|
||||||
|
|
||||||
|
doAssert not compiles(block:
|
||||||
|
# redeclaration not allowed
|
||||||
|
var foo = 0
|
||||||
|
var foo {.byaddr.} = s[0])
|
||||||
|
|
||||||
|
doAssert not compiles(block:
|
||||||
|
# ditto
|
||||||
|
var foo {.byaddr.} = s[0]
|
||||||
|
var foo {.byaddr.} = s[0])
|
||||||
|
|
||||||
|
block:
|
||||||
|
var b {.byaddr.} = s[1] # redeclaration ok in sub scope
|
||||||
|
b = 123
|
||||||
|
|
||||||
|
doAssert s == @[110,123,12]
|
||||||
|
|
||||||
|
b = b * 10
|
||||||
|
doAssert s == @[1100,123,12]
|
||||||
|
|
||||||
|
doAssert not compiles(block:
|
||||||
|
var b2 {.byaddr.}: float = s[2])
|
||||||
|
|
||||||
|
doAssert compiles(block:
|
||||||
|
var b2 {.byaddr.}: int = s[2])
|
||||||
|
|
||||||
|
## We can define custom pragmas in user code
|
||||||
|
template byUnsafeAddr(lhs, typ, expr) =
|
||||||
|
when typ is type(nil):
|
||||||
|
let tmp = unsafeAddr(expr)
|
||||||
|
else:
|
||||||
|
let tmp: ptr typ = unsafeAddr(expr)
|
||||||
|
template lhs: untyped = tmp[]
|
||||||
|
|
||||||
|
block:
|
||||||
|
let s = @["foo", "bar"]
|
||||||
|
let a {.byUnsafeAddr.} = s[0]
|
||||||
|
doAssert a == "foo"
|
||||||
|
doAssert a[0].unsafeAddr == s[0][0].unsafeAddr
|
||||||
|
|
||||||
|
block: # nkAccQuoted
|
||||||
|
# shows using a keyword, which requires nkAccQuoted
|
||||||
|
template `cast`(lhs, typ, expr) =
|
||||||
|
when typ is type(nil):
|
||||||
|
let tmp = unsafeAddr(expr)
|
||||||
|
else:
|
||||||
|
let tmp: ptr typ = unsafeAddr(expr)
|
||||||
|
template lhs: untyped = tmp[]
|
||||||
|
|
||||||
|
block:
|
||||||
|
let s = @["foo", "bar"]
|
||||||
|
let a {.`byUnsafeAddr`.} = s[0]
|
||||||
|
doAssert a == "foo"
|
||||||
|
doAssert a[0].unsafeAddr == s[0][0].unsafeAddr
|
||||||
|
|
||||||
|
block:
|
||||||
|
let s = @["foo", "bar"]
|
||||||
|
let a {.`cast`.} = s[0]
|
||||||
|
doAssert a == "foo"
|
||||||
|
doAssert a[0].unsafeAddr == s[0][0].unsafeAddr
|
||||||
Loading…
Add table
Add a link
Reference in a new issue