summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/parampatterns.nim20
-rw-r--r--compiler/semexprs.nim6
-rw-r--r--compiler/semmagic.nim6
-rw-r--r--contributing.rst2
-rw-r--r--lib/core/macros.nim8
-rw-r--r--lib/pure/asyncdispatch.nim25
-rw-r--r--lib/pure/times.nim16
-rw-r--r--lib/system.nim12
-rw-r--r--tests/ccgbugs/tunsafeaddr.nim19
-rw-r--r--todo.txt1
-rw-r--r--web/news.txt5
11 files changed, 89 insertions, 31 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/contributing.rst b/contributing.rst
index 3b495f01f..e6d34dcb0 100644
--- a/contributing.rst
+++ b/contributing.rst
@@ -217,4 +217,4 @@ General commit rules
 
 3. Describe your commit and use your common sense.
 
-.. include:: styleguide.rst
+.. include:: docstyle.rst
diff --git a/lib/core/macros.nim b/lib/core/macros.nim
index c89fa354a..d371a92cf 100644
--- a/lib/core/macros.nim
+++ b/lib/core/macros.nim
@@ -719,7 +719,13 @@ proc `$`*(node: NimNode): string {.compileTime.} =
 proc ident*(name: string): NimNode {.compileTime,inline.} = newIdentNode(name)
   ## Create a new ident node from a string
 
-iterator children*(n: NimNode): NimNode {.inline.}=
+iterator items*(n: NimNode): NimNode {.inline.} =
+  ## Iterates over the children of the NimNode ``n``.
+  for i in 0 ..< n.len:
+    yield n[i]
+
+iterator children*(n: NimNode): NimNode {.inline.} =
+  ## Iterates over the children of the NimNode ``n``.
   for i in 0 ..< n.len:
     yield n[i]
 
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 7523b29d5..f49388b17 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -124,7 +124,8 @@ export Port, SocketFlag
 ##
 ## * The effect system (``raises: []``) does not work with async procedures.
 ## * Can't await in a ``except`` body
-
+## * Forward declarations for async procs are broken,
+##   link includes workaround: https://github.com/nim-lang/Nim/issues/3182.
 
 # TODO: Check if yielded future is nil and throw a more meaningful exception
 
@@ -1412,12 +1413,12 @@ proc getName(node: NimNode): string {.compileTime.} =
   else:
     error("Unknown name.")
 
-macro async*(prc: stmt): stmt {.immediate.} =
-  ## Macro which processes async procedures into the appropriate
-  ## iterators and yield statements.
+proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} =
+  ## This macro transforms a single procedure into a closure iterator.
+  ## The ``async`` macro supports a stmtList holding multiple async procedures.
   if prc.kind notin {nnkProcDef, nnkLambda}:
-    error("Cannot transform this node kind into an async proc." &
-          " Proc definition or lambda node expected.")
+      error("Cannot transform this node kind into an async proc." &
+            " Proc definition or lambda node expected.")
 
   hint("Processing " & prc[0].getName & " as an async proc.")
 
@@ -1504,9 +1505,19 @@ macro async*(prc: stmt): stmt {.immediate.} =
   result[6] = outerProcBody
 
   #echo(treeRepr(result))
-  if prc[0].getName == "getAsync":
+  if prc[0].getName == "hubConnectionLoop":
     echo(toStrLit(result))
 
+macro async*(prc: stmt): stmt {.immediate.} =
+  ## Macro which processes async procedures into the appropriate
+  ## iterators and yield statements.
+  if prc.kind == nnkStmtList:
+    for oneProc in prc:
+      result = newStmtList()
+      result.add asyncSingleProc(oneProc)
+  else:
+    result = asyncSingleProc(prc)
+
 proc recvLine*(socket: AsyncFD): Future[string] {.async.} =
   ## Reads a line of data from ``socket``. Returned future will complete once
   ## a full line is read or an error occurs.
diff --git a/lib/pure/times.nim b/lib/pure/times.nim
index f1315a9fd..78629af71 100644
--- a/lib/pure/times.nim
+++ b/lib/pure/times.nim
@@ -26,11 +26,6 @@ type
   WeekDay* = enum ## represents a weekday
     dMon, dTue, dWed, dThu, dFri, dSat, dSun
 
-when not defined(JS):
-  var
-    timezone {.importc, header: "<time.h>".}: int
-    tzname {.importc, header: "<time.h>" .}: array[0..1, cstring]
-
 when defined(posix) and not defined(JS):
   type
     TimeImpl {.importc: "time_t", header: "<time.h>".} = int
@@ -49,6 +44,9 @@ when defined(posix) and not defined(JS):
   proc posix_gettimeofday(tp: var Timeval, unused: pointer = nil) {.
     importc: "gettimeofday", header: "<sys/time.h>".}
 
+  var
+    timezone {.importc, header: "<time.h>".}: int
+    tzname {.importc, header: "<time.h>" .}: array[0..1, cstring]
   # we also need tzset() to make sure that tzname is initialized
   proc tzset() {.importc, header: "<time.h>".}
   # calling tzset() implicitly to initialize tzname data.
@@ -60,11 +58,19 @@ elif defined(windows):
   when defined(vcc):
     # newest version of Visual C++ defines time_t to be of 64 bits
     type TimeImpl {.importc: "time_t", header: "<time.h>".} = int64
+    # visual c's c runtime exposes these under a different name
+    var
+      timezone {.importc: "_timezone", header: "<time.h>".}: int
+      tzname {.importc: "_tzname", header: "<time.h>"}: array[0..1, cstring]
   else:
     type TimeImpl {.importc: "time_t", header: "<time.h>".} = int32
+    var
+      timezone {.importc, header: "<time.h>".}: int
+      tzname {.importc, header: "<time.h>" .}: array[0..1, cstring]
 
   type
     Time* = distinct TimeImpl
+  
 
 elif defined(JS):
   type
diff --git a/lib/system.nim b/lib/system.nim
index 85d16708c..8cae7c5db 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.
@@ -178,7 +185,10 @@ proc new*[T](a: var ref T) {.magic: "New", noSideEffect.}
 
 proc new*(T: typedesc): auto =
   ## creates a new object of type ``T`` and returns a safe (traced)
-  ## reference to it as result value
+  ## reference to it as result value.
+  ##
+  ## When ``T`` is a ref type then the resulting type will be ``T``,
+  ## otherwise it will be ``ref T``. 
   when (T is ref):
       var r: T
   else:
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
   --------