Skip to content

Commit

Permalink
Allow let to not have value when using importc (#14258)
Browse files Browse the repository at this point in the history
* Allow let to not have value when using importc

This allows a let statement with the `{.importc.}` pragma to not be
initialised with a value. This allows us to declare C constants as Nim
lets without putting the value in the Nim code (which can lead to
errors, and requires us to go looking for the value). Fixes #14253

* Proper fix and documentation + changelog entry

* Improve testcase with one from timotheecour

* Add test to verify it working with macros
  • Loading branch information
PMunch authored May 12, 2020
1 parent 82f0081 commit 9acbf99
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 5 deletions.
5 changes: 2 additions & 3 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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`.
Expand Down
5 changes: 3 additions & 2 deletions compiler/semstmts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand Down
15 changes: 15 additions & 0 deletions doc/manual.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
---------------
Expand Down Expand Up @@ -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
Expand Down
24 changes: 24 additions & 0 deletions tests/let/timportc.nim
Original file line number Diff line number Diff line change
@@ -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
8 changes: 8 additions & 0 deletions tests/let/timportc2.nim
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 9acbf99

Please sign in to comment.