https://issues.dlang.org/show_bug.cgi?id=22010
Issue ID: 22010 Summary: Link error with mutually recursive SumType / struct with opEquals Product: D Version: D2 Hardware: All OS: All Status: NEW Keywords: link-failure Severity: normal Priority: P1 Component: dmd Assignee: nob...@puremagic.com Reporter: dlang-bugzi...@thecybershadow.net ///////////////////////////////////// test.d //////////////////////////////////// import std.sumtype; struct S { Ref!Node node; } alias Node = SumType!S; private struct Ref(T) { private T* _ref_ptr; this(ref T value) { _ref_ptr = &value; } /// bool opEquals(ref const Ref!T other) { return *_ref_ptr == *other._ref_ptr; } } void main() {} ///////////////////////////////////////////////////////////////////////////////// This produces: --- /usr/sbin/ld: test.o: in function `_D3std7sumtype__T7SumTypeTS4test1SZQs__T8opEqualsTSQBxQBw__TQBrTQBmZQBzTxSQCuQCt__TQCoTQCjZQCwZQCdMFKxQBdZb': test.d:(.text._D3std7sumtype__T7SumTypeTS4test1SZQs__T8opEqualsTSQBxQBw__TQBrTQBmZQBzTxSQCuQCt__TQCoTQCjZQCwZQCdMFKxQBdZb[_D3std7sumtype__T7SumTypeTS4test1SZQs__T8opEqualsTSQBxQBw__TQBrTQBmZQBzTxSQCuQCt__TQCoTQCjZQCwZQCdMFKxQBdZb]+0x21): undefined reference to `_D3std7sumtype__T7SumTypeTS4test1SZQs__T8opEqualsTxSQByQBx__TQBsTQBnZQCaTxQxZQBlMxFNbKxQBkZb' collect2: error: ld returned 1 exit status Error: linker exited with status 1 --- Demangled: --- /usr/sbin/ld: test.o: in function `bool std.sumtype.SumType!(test.S).SumType.opEquals!(std.sumtype.SumType!(test.S).SumType, const(std.sumtype.SumType!(test.S).SumType)).opEquals(ref const(std.sumtype.SumType!(test.S).SumType))': test.d:(.text.bool std.sumtype.SumType!(test.S).SumType.opEquals!(std.sumtype.SumType!(test.S).SumType, const(std.sumtype.SumType!(test.S).SumType)).opEquals(ref const(std.sumtype.SumType!(test.S).SumType))[bool std.sumtype.SumType!(test.S).SumType.opEquals!(std.sumtype.SumType!(test.S).SumType, const(std.sumtype.SumType!(test.S).SumType)).opEquals(ref const(std.sumtype.SumType!(test.S).SumType))]+0x21): undefined reference to `const nothrow bool std.sumtype.SumType!(test.S).SumType.opEquals!(const(std.sumtype.SumType!(test.S).SumType), const(std.sumtype.SumType!(test.S).SumType)).opEquals(ref const(std.sumtype.SumType!(test.S).SumType))' collect2: error: ld returned 1 exit status Error: linker exited with status 1 --- After dustmite-ing std.sumtype: /////////////////////////////////// test.d ////////////////////////////////// import std.meta: AliasSeq, Map = staticMap; import std.traits ; import std.typecons ; struct This {} struct SumType(Types) { alias Types = AliasSeq!( ReplaceTypeUnless!( isSumTypeInstance, This, typeof(this), TemplateArgsOf!SumType) ); union Storage { // Workaround for https://issues.dlang.org/show_bug.cgi?id=20068 template memberName(T) { mixin("enum memberName = `values_", "`;"); } static foreach (T; Types) mixin("T ", memberName!T, ";"); } Storage storage; inout(T) get(T)() inout { return __traits(getMember, storage, Storage.memberName!T); } bool opEquals(this This, Rhs)(Rhs rhs) { static if (is(This == Rhs)) AliasSeq!(this, rhs).match!((value, rhsValue) { value == rhsValue; }); alias CommonSumType = Rhs; return cast(CommonSumType) this == rhs; } } enum isSumTypeInstance(T) = is(Args); template match(handlers...) { auto match(SumTypes...)(SumTypes args) { matchImpl!handlers(args); } } template canMatch(alias handler, Ts...) { enum canMatch = (Ts args) => handler(args); } template Iota(size_t n) { alias Iota = AliasSeq!(1, 1); } template matchImpl(handlers...) { auto matchImpl(SumTypes...)(SumTypes args) { struct TagTuple { size_t[SumTypes.length] tags; alias tags this; static fromCaseId() { TagTuple result; // Most-significant to least-significant static foreach(i; 0 .. result.length) return result; } } template valueTypes(size_t caseId) { enum tags = TagTuple.fromCaseId; template getType(size_t i) { alias T = SumTypes[i].Types; alias getType = typeof(args[i].get!T()); } alias valueTypes = Map!(getType, Iota!(tags.length)); } enum numCases = SumTypes.length; static foreach (caseId; 0 .. numCases) foreach (handler; handlers) static if (canMatch!(handler, valueTypes!caseId)) { } } } struct S { Ref!Node node; } alias Node = SumType!S; struct Ref(T) { T* _ref_ptr; bool opEquals(const Ref!T other) { return *_ref_ptr == *other._ref_ptr; } } ///////////////////////////////////////////////////////////////////////////// --