In the code quoted below, there is an inconsistency between these two lines:
pragma(msg, P.ElementType);
static assert(hasMember!(P, "ElementType"));
The pragma correctly prints the P.ElementType, but the hasMember assert fails. The interaction with the mixin template suggests this might be a compiler bug, but for now I'm only marking this as a Phobos bug. A workaround would be appreciated.
--
import std.traits;
void main()
{
alias V = Vec!Bool;
alias P = Port!(V);
pragma(msg, P.ElementType);
static assert(hasMember!(P, "ElementType"));
}
mixin template SignalOps()
{
static if(hasMember!(typeof(this), "ElementType")) { }
}
struct Bool {}
struct Port(SomeSignal)
{
mixin SignalOps;
static if(hasMember!(SomeSignal, "ElementType"))
alias ElementType = SomeSignal.ElementType;
}
struct Vec(SomeSignal)
{
alias ElementType = SomeSignal;
}
Comment #1 by edi33416 — 2018-11-28T10:12:54Z
This is most definitely a compiler bug which seems to be related/triggered my the template mixin, as `std.traits.hasMember` only calls the equivalent `__traits(hasMember, T, memberName)` compiler trait.
As a workaround, you can use
static assert(__traits(hasMember, P, "ElementType")); // passes
Also, weirdly enough, swapping the mixin instantiation with the `static if` block in `Port`, will pass the initial assert
```
struct Port(SomeSignal)
{
static if(hasMember!(SomeSignal, "ElementType"))
alias ElementType = SomeSignal.ElementType;
mixin SignalOps;
}
void main()
{
...
static assert(hasMember!(P, "ElementType")); // OK
static assert(__traits(hasMember, P, "ElementType")); // OK
}
```
You can check this on https://run.dlang.io/is/6aXcGq
Comment #2 by edi33416 — 2018-11-28T11:47:36Z
What is even weirder, if you define your own `hasMember` wrapper, that works just fine with the initial definition of Port
```
enum hasM(T, string name) = __traits(hasMember, T, name);
static assert(hasM!(P, "ElementType")); // ok
```
As reflected by https://run.dlang.io/is/pKYL9B
Comment #3 by boris2.9 — 2021-03-21T11:11:49Z
There are 2 issues here:
1) Currently, DMD can't see forward references that are declared inside subsequent declaration scopes (things like 'static if' or 'static foreach'), this is hard to solve because the compiler could jump to a later scope looking for what it needs but that scope could also need something from the previous, a chicken and egg problem.
2) Template instances are cached, if the first instance of 'hasMember!(S, ElementType")' evaluates to false, it will always return false.
Code:
---
enum hasM(T, string name) = __traits(hasMember, T, name);
struct S
{
// ElementType is not yet defined so it evaluates to false and the
// pragma(hasM) below will print false.
// Commenting out this 'static if' will make the pragma(hasM) print true
// because the three pragmas are evaluated much later than 'struct S'.
static if(hasM!(S, "ElementType")) { }
static if(1)
alias ElementType = int;
}
void main()
{
pragma(msg, S.ElementType); // int
pragma(msg, hasM!(S, "ElementType")); // depends on the first instantiation
pragma(msg, __traits(hasMember, S, "ElementType")); // true
}
---
Comment #4 by robert.schadek — 2024-12-13T19:00:53Z