www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 22010] New: Link error with mutually recursive SumType /

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: nobody puremagic.com
          Reporter: dlang-bugzilla 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; }
}
/////////////////////////////////////////////////////////////////////////////

--
Jun 08