summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--changelog.md5
-rw-r--r--compiler/semstmts.nim5
-rw-r--r--doc/manual.rst15
-rw-r--r--tests/let/timportc.nim24
-rw-r--r--tests/let/timportc2.nim8
5 files changed, 52 insertions, 5 deletions
diff --git a/changelog.md b/changelog.md
index af6217324..d54ed4c8c 100644
--- a/changelog.md
+++ b/changelog.md
@@ -122,11 +122,11 @@
       deallocShared(x.val)
       x.val = nil
   ```
-
 - getImpl() on enum type symbols now returns field syms instead of idents. This helps
   with writing typed macros. Old behavior for backwards compatiblity can be restored
   with command line switch `--useVersion:1.0`.
-
+- ``let`` statements can now be used without a value if declared with
+  ``importc``/``importcpp``/``importjs``/``importobjc``.
 - The keyword `from` is now usable as an operator.
 - Exceptions inheriting from `system.Defect` are no longer tracked with
   the `.raises: []` exception tracking mechanism. This is more consistent with the
@@ -152,7 +152,6 @@ proc mydiv(a, b): int {.raises: [].} =
   The reason for this is that `DivByZeroDefect` inherits from `Defect` and
   with `--panics:on` `Defects` become unrecoverable errors.
 
-
 ## Compiler changes
 
 - Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`.
diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim
index 0e018dd0b..3faa32808 100644
--- a/compiler/semstmts.nim
+++ b/compiler/semstmts.nim
@@ -531,8 +531,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
             # special type inference rule: 'var it = ownedPointer' is turned
             # into an unowned pointer.
             typ = typ.lastSon
-    else:
-      if symkind == skLet: localError(c.config, a.info, errLetNeedsInit)
 
     # this can only happen for errornous var statements:
     if typ == nil: continue
@@ -620,6 +618,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
           defaultConstructionError(c, v.typ, v.info)
         else:
           checkNilable(c, v)
+        # allow let to not be initialised if imported from C:
+        if v.kind == skLet and sfImportc notin v.flags:
+          localError(c.config, a.info, errLetNeedsInit)
       if sfCompileTime in v.flags:
         var x = newNodeI(result.kind, v.info)
         x.add result[i]
diff --git a/doc/manual.rst b/doc/manual.rst
index 82678f40e..8d4439644 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -2679,6 +2679,11 @@ nor can their address be taken. They cannot be assigned new values.
 
 For let variables the same pragmas are available as for ordinary variables.
 
+As ``let`` statements are immutable after creation they need to define a value
+when they are declared. The only exception to this is if the ``{.importc.}``
+pragma (or any of the other ``importX`` pragmas) is applied, in this case the
+value is expected to come from native code, typically a C/C++ ``const``.
+
 
 Tuple unpacking
 ---------------
@@ -7090,6 +7095,16 @@ spelled*:
 .. code-block::
   proc printf(formatstr: cstring) {.header: "<stdio.h>", importc: "printf", varargs.}
 
+When ``importc`` is applied to a ``let`` statement it can omit its value which
+will then be expected to come from C. This can be used to import a C ``const``:
+
+.. code-block::
+  {.emit: "const int cconst = 42;".}
+
+  let cconst {.importc, nodecl.}: cint
+
+  assert cconst == 42
+
 Note that this pragma has been abused in the past to also work in the
 js backend for js objects and functions. : Other backends do provide
 the same feature under the same name. Also, when the target language
diff --git a/tests/let/timportc.nim b/tests/let/timportc.nim
new file mode 100644
index 000000000..85244da9f
--- /dev/null
+++ b/tests/let/timportc.nim
@@ -0,0 +1,24 @@
+discard """
+targets: "c cpp js"
+"""
+
+when defined(c) or defined(cpp):
+  {.emit:"""
+  const int TEST1 = 123;
+  #define TEST2 321
+  """.}
+
+when defined(js):
+  {.emit:"""
+  const TEST1 = 123;
+  const TEST2 = 321; // JS doesn't have macros, so we just duplicate
+  """.}
+
+let
+  TEST0 = 1
+  TEST1 {.importc, nodecl.}: cint
+  TEST2 {.importc, nodecl.}: cint
+
+doAssert TEST0 == 1
+doAssert TEST1 == 123
+doAssert TEST2 == 321
diff --git a/tests/let/timportc2.nim b/tests/let/timportc2.nim
new file mode 100644
index 000000000..964305923
--- /dev/null
+++ b/tests/let/timportc2.nim
@@ -0,0 +1,8 @@
+discard """
+  errormsg: "'let' symbol requires an initialization"
+  line: "7"
+"""
+
+# Test that this still works when not annotated with importc
+let test: cint
+echo test