Bug 19303 – hasMember fails to recognize member (interaction with mixin template)

Status
NEW
Severity
normal
Priority
P3
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2018-10-12T13:59:40Z
Last change time
2024-12-13T19:00:53Z
Assigned to
No Owner
Creator
Luís Marques
Moved to GitHub: dmd#19495 →

Comments

Comment #0 by luis — 2018-10-12T13:59:40Z
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
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/19495 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB