When creating a boolean in a macro and passing it to other macro, it is transformed to a static[int]
import macros
macro fails(b: static[bool]): untyped =
echo b
result = newStmtList()
macro works(b: static[int]): untyped =
echo b
result = newStmtList()
macro foo(): untyped =
var b = false
## Fails
result = quote do:
fails(`b`)
## Works
# result = quote do:
# works(`b`)
foo()
Error: type mismatch: got <static[int literal(0)](0)>
but expected one of:
macro fails(b: static[bool]): untyped
expression: fails(0)
I would expect a conversion to static[bool] instead of static[int]
originally posted in #10719
It seems that passing true/false literals within macros gets them converted to integer literals internally, which further results in errors if you try to use them as booleans. This worked as expected in 0.18.0.
import macros
macro someMacro*(): typed =
template tmpl(boolean) =
when boolean:
echo "it's true!"
result = getAst(tmpl(true))
someMacro()
test.nim(9, 10) template/generic instantiation of `someMacro` from here
test.nim(7, 23) Error: type mismatch: got <int literal(1)> but expected 'bool'
Couple of updates.
import macros
macro foo(): untyped =
result = quote do: `littleEndian`
doAssert littleEndian == foo() # Error: type mismatch: got <Endianness, int literal(0)>
when will have type mismatch:Test case:
import macros
macro eqSym(x, y: untyped): untyped =
let eq = $x == $y # Unfortunately eqIdent compares to string.
result = quote do: `eq`
var r, a, b: int
template fma(result: var int, a, b: int, op: untyped) =
# fused multiple-add
when eqSym(op, `+=`):
echo "+="
else:
echo "+"
fma(r, a, b, `+=`)
Impacted lib: https://github.com/status-im/nim-stint/blob/bd734b6845342feff426a849240f478020e6ee85/stint/private/uint_mul.nim#L83-L109
Instead of
result = quote do: `eq`
either use result = newLit eq
or use
result = quote do: Endianness(`eq`)
Turns out the regression was unrelated to this bug.
I just hit this bug coming from another case;
unfortunately the workaround (result = newLit eq) doesn't work so well in my case where the expression inside quote do can be complex (and turning it into AST via newCall etc, is undesirable)
#[
KEY bool converted to int in quote do
https://github.com/nim-lang/Nim/issues/7375
]#
import macros
macro foo(): untyped=
let isReq = true
result = quote do:
echo `isReq`
# or, complexExpression(`isReq`)
foo()
The issue is not specific to quote, it looks like getAst is also affected:
import macros
template test(boolArg: bool) =
static:
echo "type is: ", type(boolArg)
let x: bool = boolArg # compile error here, because boolArg became an int
macro testWrapped1(boolArg: bool): untyped =
# forwarding boolArg directly works
result = getAst(test(boolArg))
macro testWrapped2(boolArg: bool): untyped =
# forwarding boolArg via a local variable also works
let b = boolArg
result = getAst(test(b))
macro testWrapped3(boolArg: bool): untyped =
# but using a literal `true` as a local variable will be converted to int
let b = true
result = getAst(test(b))
test(true) # ok
testWrapped1(true) # ok
testWrapped2(true) # ok
testWrapped3(true) # error
I would say it is an regression, because the godot bindings were relying on getAst with local bools. The work-around with wrapping them in newLit also works with getAst.
The issue here is that -
https://github.com/nim-lang/Nim/blob/a1e268e3dccdde4df9b11a0ee87971e1143fbb43/lib/system.nim#L42-L44
compiler converts enum fields to integers in the semantic phase -
https://github.com/nim-lang/Nim/blob/a1e268e3dccdde4df9b11a0ee87971e1143fbb43/compiler/semfold.nim#L551-L552
So if at compile time - the boolean value gets converted to integer value, then at runtime there is no boolean value, only integer value. (Internally in the compiler there is no notion of a bool, .intVal is checked for being 0 or 1, similarly VM does not know anything about bool)
I would like to reference my new RFC here, because it would be affected:
https://github.com/nim-lang/Nim/issues/10412
Effectively the pattern to use newLit would be enforced.
I did some investigations. So first of all, enum values are atcually represented as integer literals, but with a type that says that they are actually enum values. So there is nothing wrong with that. Except that the type is lost in translation.
For getAst a macro call is generated here:
The argument is passed as an immediate int here:
The type field is not serialized.
In the vm the register in translated back into a node here:
https://github.com/nim-lang/Nim/blob/417d27c54406ac29ab00ac0b459aa7546af4b83a/compiler/vm.nim#L1118
This node is an integer literal node without any type information (nil).
Then in semConstExpr鈫抯emExprWithType鈫抯emExpr the missing type is set to be actually of type int (tyInt).
This crashes then in forceBool, because an integer is not a boolean.
For when this is later fixed; here is another bit of sample code to test against. Very likely the same bug involving a template called from macro:
@JohanAD just make sure all arguments to the template you want to call with getAst arg of type NimNode.
macro joe(body: untyped): untyped =
result = newStmtList()
let t = newLit(true)
let temp = getAst test(t)
result.add(temp)
Most helpful comment
I did some investigations. So first of all, enum values are atcually represented as integer literals, but with a type that says that they are actually enum values. So there is nothing wrong with that. Except that the type is lost in translation.
For
getAsta macro call is generated here:https://github.com/nim-lang/Nim/blob/417d27c54406ac29ab00ac0b459aa7546af4b83a/compiler/vmgen.nim#L1289
The argument is passed as an immediate int here:
https://github.com/nim-lang/Nim/blob/417d27c54406ac29ab00ac0b459aa7546af4b83a/compiler/vmgen.nim#L1989
The type field is not serialized.
In the vm the register in translated back into a node here:
https://github.com/nim-lang/Nim/blob/417d27c54406ac29ab00ac0b459aa7546af4b83a/compiler/vm.nim#L1118
This node is an integer literal node without any type information (
nil).Then in
semConstExpr鈫抯emExprWithType鈫抯emExprthe missing type is set to be actually of typeint(tyInt).https://github.com/nim-lang/Nim/blob/417d27c54406ac29ab00ac0b459aa7546af4b83a/compiler/semexprs.nim#L2148
This crashes then in
forceBool, because an integer is not a boolean.