summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAraq <rumpf_a@web.de>2015-08-04 17:31:00 +0200
committerAraq <rumpf_a@web.de>2015-08-04 17:31:18 +0200
commitab26298a035ed794fc73724a6ae06eefd6c764d3 (patch)
tree92eabb800ac80655ec3036633f8bbe519fe94060
parentf8f967720598ad75ec0e0db68a983cbcdd39145d (diff)
downloadNim-ab26298a035ed794fc73724a6ae06eefd6c764d3.tar.gz
added system.unsafeAddr
-rw-r--r--compiler/parampatterns.nim20
-rw-r--r--compiler/semexprs.nim6
-rw-r--r--compiler/semmagic.nim6
-rw-r--r--lib/system.nim7
-rw-r--r--tests/ccgbugs/tunsafeaddr.nim19
-rw-r--r--todo.txt1
-rw-r--r--web/news.txt5
7 files changed, 48 insertions, 16 deletions
diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim
index b7fe269df..ae391945a 100644
--- a/compiler/parampatterns.nim
+++ b/compiler/parampatterns.nim
@@ -178,13 +178,14 @@ type
     arDiscriminant,           # is a discriminant
     arStrange                 # it is a strange beast like 'typedesc[var T]'
 
-proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
+proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult =
   ## 'owner' can be nil!
   result = arNone
   case n.kind
   of nkSym:
-    # don't list 'skLet' here:
-    if n.sym.kind in {skVar, skResult, skTemp}:
+    let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet}
+                else: {skVar, skResult, skTemp}
+    if n.sym.kind in kinds:
       if owner != nil and owner.id == n.sym.owner.id and
           sfGlobal notin n.sym.flags:
         result = arLocalLValue
@@ -200,7 +201,7 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
         {tyVar, tyPtr, tyRef}:
       result = arLValue
     else:
-      result = isAssignable(owner, n.sons[0])
+      result = isAssignable(owner, n.sons[0], isUnsafeAddr)
     if result != arNone and sfDiscriminant in n.sons[1].sym.flags:
       result = arDiscriminant
   of nkBracketExpr:
@@ -208,23 +209,24 @@ proc isAssignable*(owner: PSym, n: PNode): TAssignableResult =
         {tyVar, tyPtr, tyRef}:
       result = arLValue
     else:
-      result = isAssignable(owner, n.sons[0])
+      result = isAssignable(owner, n.sons[0], isUnsafeAddr)
   of nkHiddenStdConv, nkHiddenSubConv, nkConv:
     # Object and tuple conversions are still addressable, so we skip them
     # XXX why is 'tyOpenArray' allowed here?
     if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in
         {tyOpenArray, tyTuple, tyObject}:
-      result = isAssignable(owner, n.sons[1])
+      result = isAssignable(owner, n.sons[1], isUnsafeAddr)
     elif compareTypes(n.typ, n.sons[1].typ, dcEqIgnoreDistinct):
       # types that are equal modulo distinction preserve l-value:
-      result = isAssignable(owner, n.sons[1])
+      result = isAssignable(owner, n.sons[1], isUnsafeAddr)
   of nkHiddenDeref, nkDerefExpr, nkHiddenAddr:
     result = arLValue
   of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr:
-    result = isAssignable(owner, n.sons[0])
+    result = isAssignable(owner, n.sons[0], isUnsafeAddr)
   of nkCallKinds:
     # builtin slice keeps lvalue-ness:
-    if getMagic(n) == mSlice: result = isAssignable(owner, n.sons[1])
+    if getMagic(n) == mSlice:
+      result = isAssignable(owner, n.sons[1], isUnsafeAddr)
   else:
     discard
 
diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim
index fba64776d..b4308def3 100644
--- a/compiler/semexprs.nim
+++ b/compiler/semexprs.nim
@@ -597,8 +597,8 @@ proc skipObjConv(n: PNode): PNode =
   of nkObjUpConv, nkObjDownConv: result = n.sons[0]
   else: result = n
 
-proc isAssignable(c: PContext, n: PNode): TAssignableResult =
-  result = parampatterns.isAssignable(c.p.owner, n)
+proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult =
+  result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr)
 
 proc newHiddenAddrTaken(c: PContext, n: PNode): PNode =
   if n.kind == nkHiddenDeref and not (gCmd == cmdCompileToCpp or
@@ -1700,7 +1700,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode =
   case s.magic # magics that need special treatment
   of mAddr:
     checkSonsLen(n, 2)
-    result = semAddr(c, n.sons[1])
+    result = semAddr(c, n.sons[1], s.name.s == "unsafeAddr")
   of mTypeOf:
     checkSonsLen(n, 2)
     result = semTypeOf(c, n.sons[1])
diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim
index 0a7846f1d..0afbf1f07 100644
--- a/compiler/semmagic.nim
+++ b/compiler/semmagic.nim
@@ -10,10 +10,10 @@
 # This include file implements the semantic checking for magics.
 # included from sem.nim
 
-proc semAddr(c: PContext; n: PNode): PNode =
+proc semAddr(c: PContext; n: PNode; isUnsafeAddr=false): PNode =
   result = newNodeI(nkAddr, n.info)
   let x = semExprWithType(c, n)
-  if isAssignable(c, x) notin {arLValue, arLocalLValue}:
+  if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}:
     localError(n.info, errExprHasNoAddress)
   result.add x
   result.typ = makePtrType(c, x.typ)
@@ -119,7 +119,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode,
   case n[0].sym.magic
   of mAddr:
     checkSonsLen(n, 2)
-    result = semAddr(c, n.sons[1])
+    result = semAddr(c, n.sons[1], n[0].sym.name.s == "unsafeAddr")
   of mTypeOf:
     checkSonsLen(n, 2)
     result = semTypeOf(c, n.sons[1])
diff --git a/lib/system.nim b/lib/system.nim
index 93d76e78b..e5cae1336 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -153,6 +153,13 @@ proc `addr`*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} =
   ## Cannot be overloaded.
   discard
 
+proc unsafeAddr*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} =
+  ## Builtin 'addr' operator for taking the address of a memory location.
+  ## This works even for ``let`` variables or parameters for better interop
+  ## with C and so it is considered even more unsafe than the ordinary ``addr``.
+  ## Cannot be overloaded.
+  discard
+
 proc `type`*(x: expr): typeDesc {.magic: "TypeOf", noSideEffect.} =
   ## Builtin 'type' operator for accessing the type of an expression.
   ## Cannot be overloaded.
diff --git a/tests/ccgbugs/tunsafeaddr.nim b/tests/ccgbugs/tunsafeaddr.nim
new file mode 100644
index 000000000..4f05c7c21
--- /dev/null
+++ b/tests/ccgbugs/tunsafeaddr.nim
@@ -0,0 +1,19 @@
+discard """
+  output: '''12'''
+"""
+
+{.emit: """
+long sum(long* a, long len) {
+  long i, result = 0;
+  for (i = 0; i < len; ++i) result += a[i];
+  return result;
+}
+""".}
+
+proc sum(a: ptr int; len: int): int {.importc, nodecl.}
+
+proc main =
+  let foo = [8, 3, 1]
+  echo sum(unsafeAddr foo[0], foo.len)
+
+main()
diff --git a/todo.txt b/todo.txt
index 8734b8f19..2955f2917 100644
--- a/todo.txt
+++ b/todo.txt
@@ -1,7 +1,6 @@
 version 0.11.4
 ==============
 
-- ``unsafeAddr``
 - document special cased varargs[untyped] and varargs[typed]
 
 - The remaining bugs of the lambda lifting pass that is responsible to enable
diff --git a/web/news.txt b/web/news.txt
index b72ae56d2..518793a4a 100644
--- a/web/news.txt
+++ b/web/news.txt
@@ -57,6 +57,11 @@ News
   Language Additions
   ------------------
 
+  - ``system.unsafeAddr`` can be used to access the address of a ``let``
+    variable or parameter for C interoperability. Since technically this
+    makes parameters and ``let`` variables mutable, it is considered even more
+    unsafe than the ordinary ``addr`` builtin.
+
 
   Bugfixes
   --------