Comment #0 by dlang-bugzilla — 2021-06-09T06:23:46Z
///////////////////////////////////// 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; }
}
/////////////////////////////////////////////////////////////////////////////
Comment #1 by jlourenco5691 — 2021-06-09T12:08:08Z
I think this is because it uses `match` internally. You are passing a `const Ref!T` in your `opEquals` which makes its `TagTuple` a `const(TagTuple)`. When trying to match it won't be able to.
As a workaround fix, removing `const` from `opEquals` solves your issue.
Also, refactoring your `opEquals` to a template (to allow the program to compile), gives a more propper error when comparing the types:
```d
/+dub.sdl:
dependency "sumtype" version="~>1.1.1"
+/
import 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()
{
S s;
assert(S.init == s);
}
```
Produces (tested with sumtype-1.1.1):
---
.dub/packages/sumtype-1.1.1/sumtype/src/sumtype.d(1718,13): Error: need `this` for `tags` of type `ulong[2]`
.dub/packages/sumtype-1.1.1/sumtype/src/sumtype.d(1718,13): Error: need `this` for `tags` of type `ulong[2]`
Error: `this` for `__invariant430` needs to be type `TagTuple` not type `const(TagTuple)`
.dub/packages/sumtype-1.1.1/sumtype/src/sumtype.d(1828,4): Error: static assert: "`handlers[0]` of type `template` never matches"
.dub/packages/sumtype-1.1.1/sumtype/src/sumtype.d(1448,46): instantiated from here: `matchImpl!(const(SumType!(S)), const(SumType!(S)))`
.dub/packages/sumtype-1.1.1/sumtype/src/sumtype.d(578,31): instantiated from here: `match!(const(SumType!(S)), const(SumType!(S)))`
.dub/packages/sumtype-1.1.1/sumtype/src/sumtype.d(587,11): instantiated from here: `opEquals!(const(SumType!(S)), const(SumType!(S)))`
onlineapp.d(14,53): instantiated from here: `opEquals!(SumType!(S), const(SumType!(S)))`
onlineapp.d(10,9): instantiated from here: `opEquals!()`
dmd failed with exit code 1.
---
This should work though.
Comment #2 by dlang-bugzilla — 2021-06-09T12:32:57Z
(In reply to João Lourenço from comment #1)
> I think this is because it uses `match` internally.
A link error like this should never happen no matter what the code does (unless it's something intentional like an extern declaration with no definition, of course.)
> As a workaround fix, removing `const` from `opEquals` solves your issue.
Thanks but this is a reduced example meant to illustrate the compiler bug.
Comment #3 by jlourenco5691 — 2021-06-09T17:21:20Z
(In reply to Vladimir Panteleev from comment #2)
> Thanks but this is a reduced example meant to illustrate the compiler bug.
Yes, I mislead the issue and thought it was about SumType itself.
Comment #4 by pro.mathias.lang — 2022-07-06T15:51:43Z
I believe I hit this issue as well. The key seems to be the mutual recursion.
In my case, the code is the following:
```
import std.sumtype;
import std.stdio;
alias ST = SumType!(int, long, Custom[]);
struct Custom
{
int a;
ST b;
}
void main ()
{
const SumType!(int, long, Custom[]) st = [ Custom(42), Custom(69) ];
version(all) writeln(st);
assert(st.toString() == "NULL");
}
```
With this, I get a compiler error:
```
/usr/include/dmd/phobos/std/sumtype.d(1945): Error: need `this` for `tags` of type `ulong[1]`
Error: `this` for `__invariant1322` needs to be type `TagTuple` not type `const(TagTuple)`
/usr/include/dmd/phobos/std/sumtype.d(2102): Error: static assert: "No matching handler for types `(const(Custom[]))`"
/usr/include/dmd/phobos/std/sumtype.d(1662): instantiated from here: `matchImpl!(const(SumType!(int, long, Custom[])))`
/usr/include/dmd/phobos/std/sumtype.d(787): instantiated from here: `match!(const(SumType!(int, long, Custom[])))`
st.d(16): instantiated from here: `toString!(const(SumType!(int, long, Custom[])))`
```
Now if you remove the `writeln` in `version(all)`, you end up with:
```
ld: error: undefined symbol: _D3std6format5write__T11formatValueTSQBj5array__T8AppenderTAyaZQoTAxS2st6CustomTaZQCiFKQBzKQzMKxSQDrQDq4spec__T10FormatSpecTaZQpZv
>>> referenced by st.d
>>> st.o:(_D3std4conv__T5toStrTAyaTAxS2st6CustomZQzFQrZQy)
collect2: error: ld returned 1 exit status
Error: linker exited with status 1
```
Looking at SumType's code, it seems the instantiation of `formatValue` happens here:
https://github.com/dlang/phobos/blob/dba1bbe271a9b2d7f24edeebbc77846e29904e41/std/sumtype.d#L807
Comment #5 by robert.schadek — 2024-12-13T19:16:55Z