summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorTimothee Cour <timothee.cour2@gmail.com>2020-03-23 03:15:45 -0700
committerGitHub <noreply@github.com>2020-03-23 11:15:45 +0100
commit913bc95964458d3eb2fc4d8a108031e6b9398da5 (patch)
treef66dc374d3ff53ff444e4975cfd40ec14916291c
parentfa06203e90d9bb9211d0b6b9726fc9f2c5dc80ad (diff)
downloadNim-913bc95964458d3eb2fc4d8a108031e6b9398da5.tar.gz
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.}`
-rw-r--r--changelog.md5
-rw-r--r--compiler/semstmts.nim46
-rw-r--r--lib/std/pragmas.nim19
-rw-r--r--tests/stdlib/tpragmas.nim70
4 files changed, 139 insertions, 1 deletions
diff --git a/changelog.md b/changelog.md
index 622f9de0b..daed6c02b 100644
--- a/changelog.md
+++ b/changelog.md
@@ -149,7 +149,7 @@ echo f
 - `std/oswalkdir` was buggy, it's now deprecated and reuses `std/os` procs
 - `net.newContext` now performs SSL Certificate checking on Linux and OSX.
   Define `nimDisableCertificateValidation` to disable it globally.
-
+- new syntax for lvalue references: `var b {.byaddr.} = expr` enabled by `import pragmas`
 
 ## Language additions
 
@@ -159,6 +159,9 @@ echo f
 - `=sink` type bound operator is now optional. Compiler can now use combination
   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
 
 - Unsigned integer operators have been fixed to allow promotion of the first operand.
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index cdb47d2a4..30f14b524 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -431,7 +431,53 @@ proc setVarType(c: PContext; v: PSym, typ: PType) =
         "; new type is: " & typeToString(typ, preferDesc))
   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 =
+  if n.len == 1:
+    result = semLowerLetVarCustomPragma(c, n[0], n)
+    if result!=nil: return result
+
   var b: PNode
   result = copyNode(n)
   for i in 0..<n.len:
diff --git a/lib/std/pragmas.nim b/lib/std/pragmas.nim
new file mode 100644
index 000000000..5dce04c11
--- /dev/null
+++ b/lib/std/pragmas.nim
@@ -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[]
diff --git a/tests/stdlib/tpragmas.nim b/tests/stdlib/tpragmas.nim
new file mode 100644
index 000000000..b91d7e547
--- /dev/null
+++ b/tests/stdlib/tpragmas.nim
@@ -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