Error when returning values within macro trailing bodies and block expressions
Compiler throws an error when compiling such code:
// --- test.c3
fault SomeF
{
A, B, C
}
macro usz! @insertBody(;@body())
{
return {| @body(); |};
}
fn void main()
{
usz! res1 = @insertBody() { return SomeF.A?; };
}
Here is an error:
$ c3c compile-run test.c3
11:
12: fn void main()
13: {
14: usz! res1 = @insertBody() { return SomeF.A?; };
^^^^^^^^
(/home/starleks/coding/probe/c3-sandbox/main.c3:14:38) Error: It is not possible to cast
from 'void!' to 'void'.
Seems like trailing is supposed to return void only. By changing an optional to a regular value is not solving a problem:
// ...
usz! res1 = @insertBody() { return 23; };
// ...
$ c3c compile-run test.c3
11:
12: fn void main()
13: {
14: usz! res1 = @insertBody() { return 23; };
^^
(/home/starleks/coding/probe/c3-sandbox/main.c3:14:38) Error: 'int' cannot implicitly be
converted to 'void', but you may use a cast.
This is somewhat surprising perhaps, but correct. The body always executes in the scope in which is is created. So returning anything here is returning from main.
So here:
fn void main()
{
usz! res1 = @insertBody() { return SomeF.A?; };
}
This trailing body always evaluates in the defining scope, which is the main function in this case. This is very weird with the {| |} since one might assume that this one should transform the "return" here. However, the semantics for the trailing body macros is always in the external scope.
I think probably the problem here is rather that the {| |} doesn't issue an error.
I think it also highlights a missing feature, namely to pass in something that works as a macro into a macro in a normal way.
So for example, we might imagine this:
macro usz! @insertBody(#m)
{
return {| #m(3); |};
}
fn void main()
{
int a;
@insertBody(macro (x) {
if (a > x) return SomeF.A?;
return a;
});
}
Where we pass a macro that works like a closure. This is not yet a feature in C3 though.
Yeah, such feature would be cool.
Can I have some alternative for this, though? For example, to return from @body with some value?
How do you mean?
I mean something what you've mentioned above. Some kind of 'lambda macros' is the solution.
Regarding the semantic reason for the behavior I've written about originally, I understand the @body as something to be inserted inside macro. So it's not guaranteed to be in the context of calling function, for instance, when using @body inside lambdas and (the issue I was stuck) expression blocks:
macro usz @someMacro(; @body())
{
var inc = fn usz (usz i) {
@body();
i + 1;
};
return inc(2);
}
It's actually like the macro code floats around the code than the other way around.
So in the case of
fn void main()
{
int a;
@someMacro() { a++; };
}
For your case then the code (if it had been permitted) should expand to:
fn void main()
{
int a;
{|
var inc = fn usz(usz i) {
a++;
return i + 1;
};
return inc(2);
|};
}
But this could only work if C3 had closures in some way, but it doesn't, so the above would be a compile time error since a is not visible from the lambda anyway.
So this is a dead end, but passing macros into macros, that could work.
Fee free to file a feature request for placing macros in macros.
Closing this and instead track #2067