summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--compiler/options.nim3
-rw-r--r--compiler/pragmas.nim23
-rw-r--r--doc/manual.rst154
-rw-r--r--lib/pure/strscans.nim2
-rw-r--r--lib/system.nim2
-rw-r--r--tests/assert/tfailedassert_stacktrace.nim35
-rw-r--r--tests/async/tasyncfilewrite.nim8
-rw-r--r--tests/misc/tnoforward.nim3
-rw-r--r--tests/modules/treorder.nim3
-rw-r--r--tests/pragmas/treorder.nim5
10 files changed, 167 insertions, 71 deletions
diff --git a/compiler/options.nim b/compiler/options.nim
index 02f284676..a95d9930a 100644
--- a/compiler/options.nim
+++ b/compiler/options.nim
@@ -121,7 +121,8 @@ type
     notnil,
     dynamicBindSym,
     forLoopMacros,
-    caseStmtMacros
+    caseStmtMacros,
+    codeReordering,
 
   SymbolFilesOption* = enum
     disabledSf, writeOnlySf, readOnlySf, v2Sf
diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim
index e6e4eff38..b8dae8123 100644
--- a/compiler/pragmas.nim
+++ b/compiler/pragmas.nim
@@ -231,8 +231,17 @@ proc onOff(c: PContext, n: PNode, op: TOptions, resOptions: var TOptions) =
   else: resOptions = resOptions - op
 
 proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) =
-  if isTurnedOn(c, n): incl(c.module.flags, flag)
-  else: excl(c.module.flags, flag)
+  if isTurnedOn(c, n):
+    incl(c.module.flags, flag)
+    c.features.incl codeReordering
+  else:
+    excl(c.module.flags, flag)
+    # c.features.excl codeReordering
+
+  # deprecated as of 0.18.1
+  message(c.config, n.info, warnDeprecated,
+          "use {.experimental: \"codeReordering.\".} instead; " &
+          (if flag == sfNoForward: "{.noForward.}" else: "{.reorder.}"))
 
 proc processCallConv(c: PContext, n: PNode) =
   if n.kind in nkPragmaCallKinds and n.len == 2 and n.sons[1].kind == nkIdent:
@@ -351,7 +360,13 @@ proc processExperimental(c: PContext; n: PNode) =
     case n[1].kind
     of nkStrLit, nkRStrLit, nkTripleStrLit:
       try:
-        c.features.incl parseEnum[Feature](n[1].strVal)
+        let feature = parseEnum[Feature](n[1].strVal)
+        c.features.incl feature
+        if feature == codeReordering:
+          if not isTopLevel(c):
+              localError(c.config, n.info,
+                         "Code reordering experimental pragma only valid at toplevel")
+          c.module.flags.incl sfReorder
       except ValueError:
         localError(c.config, n[1].info, "unknown experimental feature")
     else:
@@ -817,7 +832,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int,
         incl(sym.flags, {sfThread, sfGlobal})
       of wDeadCodeElimUnused: discard  # deprecated, dead code elim always on
       of wNoForward: pragmaNoForward(c, it)
-      of wReorder: pragmaNoForward(c, it, sfReorder)
+      of wReorder: pragmaNoForward(c, it, flag = sfReorder)
       of wMagic: processMagic(c, it, sym)
       of wCompileTime:
         noVal(c, it)
diff --git a/doc/manual.rst b/doc/manual.rst
index 6dc6794f1..7946e88dd 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -6534,6 +6534,111 @@ iterator in which case the overloading resolution takes place:
   var x = 4
   write(stdout, x) # not ambiguous: uses the module C's x
 
+Code reordering
+~~~~~~~~~~~~~~~
+
+**Note**: Code reordering is experimental and must be enabled via the
+``{.experimental.}`` pragma.
+
+The code reordering feature can implicitly rearrange procedure, template, and
+macro definitions along with variable declarations and initializations at the top
+level scope so that, to a large extent, a programmer should not have to worry
+about ordering definitions correctly or be forced to use forward declarations to
+preface definitions inside a module.
+
+..
+   NOTE: The following was documentation for the code reordering precursor,
+   which was {.noForward.}.
+   
+   In this mode, procedure definitions may appear out of order and the compiler
+   will postpone their semantic analysis and compilation until it actually needs
+   to generate code using the definitions. In this regard, this mode is similar
+   to the modus operandi of dynamic scripting languages, where the function
+   calls are not resolved until the code is executed. Here is the detailed
+   algorithm taken by the compiler:
+
+   1. When a callable symbol is first encountered, the compiler will only note
+   the symbol callable name and it will add it to the appropriate overload set
+   in the current scope. At this step, it won't try to resolve any of the type
+   expressions used in the signature of the symbol (so they can refer to other
+   not yet defined symbols).
+
+   2. When a top level call is encountered (usually at the very end of the
+   module), the compiler will try to determine the actual types of all of the
+   symbols in the matching overload set. This is a potentially recursive process
+   as the signatures of the symbols may include other call expressions, whose
+   types will be resolved at this point too.
+
+   3. Finally, after the best overload is picked, the compiler will start
+   compiling the body of the respective symbol. This in turn will lead the
+   compiler to discover more call expressions that need to be resolved and steps
+   2 and 3 will be repeated as necessary.
+
+   Please note that if a callable symbol is never used in this scenario, its
+   body will never be compiled. This is the default behavior leading to best
+   compilation times, but if exhaustive compilation of all definitions is
+   required, using ``nim check`` provides this option as well.
+
+Example:
+
+.. code-block:: nim
+
+  {.experimental: "codeReordering".}
+
+  proc foo(x: int) =
+    bar(x)
+
+  proc bar(x: int) =
+    echo(x)
+
+  foo(10)
+
+Variables can also be reordered as well. Variables that are *initialized* (i.e.
+variables that have their declaration and assignment combined in a single
+statement) can have their entire initialization statement reordered. Be wary of
+what code is executed at the top level:
+
+.. code-block:: nim
+  {.experimental: "codeReordering".}
+
+  proc a() =
+    echo(foo)
+
+  var foo = 5
+
+  a() # outputs: "5"
+
+..
+   TODO: Let's table this for now. This is an *experimental feature* and so the
+   specific manner in which ``declared`` operates with it can be decided in
+   eventuality, because right now it works a bit weirdly.
+
+   The values of expressions involving ``declared`` are decided *before* the
+   code reordering process, and not after. As an example, the output of this
+   code is the same as it would be with code reordering disabled.
+
+   .. code-block:: nim
+     {.experimental: "codeReordering".}
+
+     proc x() =
+       echo(declared(foo))
+
+     var foo = 4
+
+     x() # "false"
+
+It is important to note that reordering *only* works for symbols at top level
+scope. Therefore, the following will *fail to compile:*
+
+.. code-block:: nim
+  {.experimental: "codeReordering".}
+
+  proc a() =
+    b()
+    proc b() =
+      echo("Hello!")
+
+  a()
 
 Compiler Messages
 =================
@@ -6943,55 +7048,6 @@ the created global variables within a module is not defined, but all of them
 will be initialized after any top-level variables in their originating module
 and before any variable in a module that imports it.
 
-
-..
-  NoForward pragma
-  ----------------
-  The ``noforward`` pragma can be used to turn on and off a special compilation
-  mode that to large extent eliminates the need for forward declarations. In this
-  mode, the proc definitions may appear out of order and the compiler will postpone
-  their semantic analysis and compilation until it actually needs to generate code
-  using the definitions. In this regard, this mode is similar to the modus operandi
-  of dynamic scripting languages, where the function calls are not resolved until
-  the code is executed. Here is the detailed algorithm taken by the compiler:
-
-  1. When a callable symbol is first encountered, the compiler will only note the
-  symbol callable name and it will add it to the appropriate overload set in the
-  current scope. At this step, it won't try to resolve any of the type expressions
-  used in the signature of the symbol (so they can refer to other not yet defined
-  symbols).
-
-  2. When a top level call is encountered (usually at the very end of the module),
-  the compiler will try to determine the actual types of all of the symbols in the
-  matching overload set. This is a potentially recursive process as the signatures
-  of the symbols may include other call expressions, whose types will be resolved
-  at this point too.
-
-  3. Finally, after the best overload is picked, the compiler will start
-  compiling the body of the respective symbol. This in turn will lead the
-  compiler to discover more call expressions that need to be resolved and steps
-  2 and 3 will be repeated as necessary.
-
-  Please note that if a callable symbol is never used in this scenario, its body
-  will never be compiled. This is the default behavior leading to best compilation
-  times, but if exhaustive compilation of all definitions is required, using
-  ``nim check`` provides this option as well.
-
-  Example:
-
-  .. code-block:: nim
-
-    {.noforward: on.}
-
-    proc foo(x: int) =
-      bar x
-
-    proc bar(x: int) =
-      echo x
-
-    foo(10)
-
-
 pragma pragma
 -------------
 
diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim
index b17eee6ff..734317e67 100644
--- a/lib/pure/strscans.nim
+++ b/lib/pure/strscans.nim
@@ -317,7 +317,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b
 
   template at(s: string; i: int): char = (if i < s.len: s[i] else: '\0')
   template matchError() =
-    error("type mismatch between pattern '$" & pattern[p] & "' (position: " & $p & ") and " & $getType(results[i]) &
+    error("type mismatch between pattern '$" & pattern[p] & "' (position: " & $p & ") and " & repr(getType(results[i])) &
           " var '" & repr(results[i]) & "'")
 
   var i = 0
diff --git a/lib/system.nim b/lib/system.nim
index ef6138ad1..353500a4a 100644
--- a/lib/system.nim
+++ b/lib/system.nim
@@ -1237,6 +1237,8 @@ proc cmp*[T](x, y: T): int {.procvar.} =
 
 proc cmp*(x, y: string): int {.noSideEffect, procvar.}
   ## Compare proc for strings. More efficient than the generic version.
+  ## **Note**: The precise result values depend on the used C runtime library and
+  ## can differ between operating systems!
 
 proc `@`* [IDX, T](a: array[IDX, T]): seq[T] {.
   magic: "ArrToSeq", nosideeffect.}
diff --git a/tests/assert/tfailedassert_stacktrace.nim b/tests/assert/tfailedassert_stacktrace.nim
index 43171ef6c..6505f189c 100644
--- a/tests/assert/tfailedassert_stacktrace.nim
+++ b/tests/assert/tfailedassert_stacktrace.nim
@@ -1,13 +1,31 @@
 discard """
-  output: '''
-tfailedassert_stacktrace.nim(16) tfailedassert_stacktrace
-tfailedassert_stacktrace.nim(15) foo
-system.nim(3778)         failedAssertImpl
-system.nim(3771)         raiseAssert
-system.nim(2818)         sysFatal
-'''
+  output: '''true'''
 """
 
+const expected = """
+tfailedassert_stacktrace.nim(34) tfailedassert_stacktrace
+tfailedassert_stacktrace.nim(33) foo
+system.nim(*)         failedAssertImpl
+system.nim(*)         raiseAssert
+system.nim(*)         sysFatal"""
+
+proc tmatch(x, p: string): bool =
+  var i = 0
+  var k = 0
+  while i < p.len:
+    if p[i] == '*':
+      let oldk = k
+      while k < x.len and x[k] in {'0'..'9'}: inc k
+      # no digit skipped?
+      if oldk == k: return false
+      inc i
+    elif k < x.len and p[i] == x[k]:
+      inc i
+      inc k
+    else:
+      return false
+  while k < x.len and x[k] in {' ', '\L', '\C'}: inc k
+  result = i >= p.len and k >= x.len
 
 
 try:
@@ -16,4 +34,5 @@ try:
   foo()
 except AssertionError:
   let e = getCurrentException()
-  echo e.getStackTrace
+  let trace = e.getStackTrace
+  echo tmatch(trace, expected)
diff --git a/tests/async/tasyncfilewrite.nim b/tests/async/tasyncfilewrite.nim
index cda612bae..8439778ca 100644
--- a/tests/async/tasyncfilewrite.nim
+++ b/tests/async/tasyncfilewrite.nim
@@ -6,12 +6,14 @@ string 3'''
 # bug #5532
 import os, asyncfile, asyncdispatch
 
-removeFile("test.txt")
-let f = openAsync("test.txt", fmWrite)
+const F = "test_async.txt"
+
+removeFile(F)
+let f = openAsync(F, fmWrite)
 var futs = newSeq[Future[void]]()
 for i in 1..3:
   futs.add(f.write("string " & $i & "\n"))
 waitFor(all(futs))
 f.close()
-echo readFile("test.txt")
+echo readFile(F)
 
diff --git a/tests/misc/tnoforward.nim b/tests/misc/tnoforward.nim
index 342e757b8..3e96e3489 100644
--- a/tests/misc/tnoforward.nim
+++ b/tests/misc/tnoforward.nim
@@ -2,7 +2,8 @@ discard """
   disabled: true
 """
 
-{. noforward: on .}
+# {. noforward: on .}
+{.experimental: "codeReordering".}
 
 proc foo(x: int) =
   bar x
diff --git a/tests/modules/treorder.nim b/tests/modules/treorder.nim
index 8715e4548..c81715cd8 100644
--- a/tests/modules/treorder.nim
+++ b/tests/modules/treorder.nim
@@ -6,8 +6,7 @@ defined
 3'''
 """
 
-{.reorder: on.}
-{.experimental.}
+{.experimental: "codeReordering".}
 
 proc bar(x: T)
 
diff --git a/tests/pragmas/treorder.nim b/tests/pragmas/treorder.nim
index 1006af527..659a6f644 100644
--- a/tests/pragmas/treorder.nim
+++ b/tests/pragmas/treorder.nim
@@ -6,7 +6,8 @@ output:'''0
 """
 
 import macros
-{.reorder: on .}
+# {.reorder: on .}
+{.experimental: "codeReordering".}
 
 echo foo(-1)
 echo callWithFoo(0)
@@ -71,4 +72,4 @@ macro make(arg: untyped): untyped =
 proc first(i: int): void =
   make(second)
 
-var ss {.compileTime.}: string = ""
\ No newline at end of file
+var ss {.compileTime.}: string = ""