Open yanok opened 2 months ago
Reduced testcase:
auto setCallback(alias F)() {
return __traits(identifier, __traits(parent, F));
}
void foo()
{
static void closure(void delegate() ) {}
closure({
static f() {}
setCallback!f;
});
}
> bin/ldc2 -c gh4759.d --unittest --mtriple=x86_64-pc-linux-gnu
Assertion failed: (!isaStruct(t)), function DtoBitCast, file tollvm.cpp, line 603.
Hint towards the issue:
auto setCallback(alias F)() {
auto a = __traits(parent, F);
}
void closure(void delegate() ) {}
void foo()
{
closure({
static f() {}
setCallback!f;
});
}
❯ bin/ldc2 -c gh4759.d --unittest --mtriple=x86_64-pc-linux-gnu
gh4759.d(2): Error: forward reference to inferred return type of function call `__lambda1()`
gh4759.d(10): Error: template instance `gh4759.setCallback!(f)` error instantiating
Reduced testcase:
void closure(void delegate() ) {}
void foo()
{
closure({
static f() {}
auto a = __traits(parent, f); // Error: forward reference to inferred return type of function call `__lambda1()`
auto a = __traits(identifier, __traits(parent, f)); // --> crash
});
}
Yeah thanks for the report, we're hitting assertions earlier, related to the dark corners around DFuncValue
(doesn't handle Tdelegate
, DtoRVal()
yields the function pointer only). I'll try to fix that.
My reduced test case. Thanks @kinke
void func(void delegate())
{
enum ident(alias F) = __traits(identifier, __traits(parent, F));
// uncaught forward reference
func({ cast(void)ident!({}); });
}
Thanks for the reduced examples!
I'm really new to D, so I have some noob questions. Do I understand it correctly that:
__traits(parent, f)
for f
defined inside lambda body will never work? Because lambda's return type is still to be inferred? But parent symbol has to reference it?__traits(identifier, ...)
somehow masks the problem of getting parent from above? So the desired behavior here is a compiler error?BTW, I believe dmd
is also affected: it doesn't crash, but the emitted code does (the same with compiling for Apple silicon on Mac).
__traits(parent, f)
is to get the parent symbol attached to the given symbol (see https://dlang.org/spec/traits.html#parent ). So, e.g. a inner function, gets the outer function, a function in the global scope of the module, gets the module symbol, etc, etc... From what I can understand, it shouldn't require to resolve the return type (unless I'm missing something), actually, just resolve which parent is even if its a forward reference, but, as far as I know, the compiler only seem to resolve it after the semantic3 (the last semantic pass), which implicitly requires the return type to be resolved, hence the error on __traits(parent, f)
alone..sizeof
done inside an inner struct of the outer struct, there's many situations like this, specially when mixin
's are in the equation. Imo, the DMD frontend is not capable of solving these inter-dependencies in a correct way, they naively try their best and some times just give up and give erroneous information to the rest of the code. There's checks LDC add to warn the user about it, e.g. one I know of is mangling mismatch (different AST symbols generating the same mangling). You can take another example, attribute inference, which is something we suffer a lot by having missing symbols at link-time, because one compilation unit inferred one attribute and another, other attributes, yielding a different mangle, and therefore possibly undefined references (at best, duplicate unused symbols).Last time I talked with compiler frontend people, at Dconf, I suggested an optional glue code coherency checker of the AST, which is something I have planned to add as a compiler plugin, so we can detect possible incoherent state like uncaught forward references, attributes inference mismatch, alongside other checks that might be expensive to run by default, but worth running in a compiler debugging session. It would be a complete AST traversal with additional checks, after semantics, pretty much.
BTW, I believe dmd is also affected: it doesn't crash, but the emitted code does (the same with compiling for Apple silicon on Mac).
Yeah, DMD backend is a toy to me, I wouldn't trust the codegen of DMD backend for anything in production. There's many situations where DMD goes happy and the user goes mad.
I see a crash trying to compile this code (already minified by dustmite):
Compile command:
ldc2 -c crash.d --unittest
.Stack trace:
The problem goes away if I move
static f() {}
definition out of the function body.Checked on Ubuntu with
and on M3 Mac with
Though on Mac I have to pass
--mtripple=x86_64-pc-linux-gnu
to trigger the crash.