diff options
author | Timothee Cour <timothee.cour2@gmail.com> | 2020-03-23 03:15:45 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-23 11:15:45 +0100 |
commit | 913bc95964458d3eb2fc4d8a108031e6b9398da5 (patch) | |
tree | f66dc374d3ff53ff444e4975cfd40ec14916291c | |
parent | fa06203e90d9bb9211d0b6b9726fc9f2c5dc80ad (diff) | |
download | Nim-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.md | 5 | ||||
-rw-r--r-- | compiler/semstmts.nim | 46 | ||||
-rw-r--r-- | lib/std/pragmas.nim | 19 | ||||
-rw-r--r-- | tests/stdlib/tpragmas.nim | 70 |
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 |