Allow let to not have value when using importc (#14258)
* 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
This commit is contained in:
parent
82f008158c
commit
9acbf99efb
5 changed files with 52 additions and 5 deletions
|
|
@ -122,11 +122,11 @@
|
||||||
deallocShared(x.val)
|
deallocShared(x.val)
|
||||||
x.val = nil
|
x.val = nil
|
||||||
```
|
```
|
||||||
|
|
||||||
- getImpl() on enum type symbols now returns field syms instead of idents. This helps
|
- 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 writing typed macros. Old behavior for backwards compatiblity can be restored
|
||||||
with command line switch `--useVersion:1.0`.
|
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.
|
- The keyword `from` is now usable as an operator.
|
||||||
- Exceptions inheriting from `system.Defect` are no longer tracked with
|
- Exceptions inheriting from `system.Defect` are no longer tracked with
|
||||||
the `.raises: []` exception tracking mechanism. This is more consistent with the
|
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
|
The reason for this is that `DivByZeroDefect` inherits from `Defect` and
|
||||||
with `--panics:on` `Defects` become unrecoverable errors.
|
with `--panics:on` `Defects` become unrecoverable errors.
|
||||||
|
|
||||||
|
|
||||||
## Compiler changes
|
## Compiler changes
|
||||||
|
|
||||||
- Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`.
|
- Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`.
|
||||||
|
|
|
||||||
|
|
@ -531,8 +531,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
# special type inference rule: 'var it = ownedPointer' is turned
|
# special type inference rule: 'var it = ownedPointer' is turned
|
||||||
# into an unowned pointer.
|
# into an unowned pointer.
|
||||||
typ = typ.lastSon
|
typ = typ.lastSon
|
||||||
else:
|
|
||||||
if symkind == skLet: localError(c.config, a.info, errLetNeedsInit)
|
|
||||||
|
|
||||||
# this can only happen for errornous var statements:
|
# this can only happen for errornous var statements:
|
||||||
if typ == nil: continue
|
if typ == nil: continue
|
||||||
|
|
@ -620,6 +618,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode =
|
||||||
defaultConstructionError(c, v.typ, v.info)
|
defaultConstructionError(c, v.typ, v.info)
|
||||||
else:
|
else:
|
||||||
checkNilable(c, v)
|
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:
|
if sfCompileTime in v.flags:
|
||||||
var x = newNodeI(result.kind, v.info)
|
var x = newNodeI(result.kind, v.info)
|
||||||
x.add result[i]
|
x.add result[i]
|
||||||
|
|
|
||||||
|
|
@ -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.
|
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
|
Tuple unpacking
|
||||||
---------------
|
---------------
|
||||||
|
|
@ -7090,6 +7095,16 @@ spelled*:
|
||||||
.. code-block::
|
.. code-block::
|
||||||
proc printf(formatstr: cstring) {.header: "<stdio.h>", importc: "printf", varargs.}
|
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
|
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
|
js backend for js objects and functions. : Other backends do provide
|
||||||
the same feature under the same name. Also, when the target language
|
the same feature under the same name. Also, when the target language
|
||||||
|
|
|
||||||
24
tests/let/timportc.nim
Normal file
24
tests/let/timportc.nim
Normal file
|
|
@ -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
tests/let/timportc2.nim
Normal file
8
tests/let/timportc2.nim
Normal file
|
|
@ -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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue