www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.bugs - [Issue 19407] New: Separate compilation breaks

https://issues.dlang.org/show_bug.cgi?id=19407

          Issue ID: 19407
           Summary: Separate compilation breaks hasElaborateDestructor
                    with recursive type
           Product: D
           Version: D2
          Hardware: x86_64
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P1
         Component: dmd
          Assignee: nobody puremagic.com
          Reporter: atila.neves gmail.com

The code below compiles fine together but fails when compiled separately:

--------
// app.d
import type;
--------

---------
// type.d
import std.meta;
import std.traits;

struct Sum(Types...) if(Types.length > 0) {
    union {
        static foreach(i, T; Types) {
            import std.conv: text;
            mixin(`T value` ~ i.text ~ `;`);
        }
    }

    static if(anySatisfy!(hasElaborateDestructor, Types)) {

    }
}

alias Type = Sum!(Pointer, Bool);

struct Bool{}
struct Pointer{
    Type* pointee;
}
---------

% dmd -c app.d
/usr/include/dlang/dmd/std/traits.d(2678): Error: unable to determine fields of
Pointer because of forward references
/usr/include/dlang/dmd/std/traits.d(3760): Error: template instance
`std.traits.FieldTypeTuple!(Pointer)` error instantiating
/usr/include/dlang/dmd/std/meta.d(887):        instantiated from here:
F!(Pointer)
/usr/include/dlang/dmd/std/meta.d(892):        instantiated from here:
anySatisfy!(hasElaborateDestructor, Pointer)
type.d(12):        instantiated from here: anySatisfy!(hasElaborateDestructor,
Pointer, Bool)
type.d(17):        instantiated from here: Sum!(Pointer, Bool)


The error messages seems to suggest that it's impossible to use
`Fields!Pointer` but if the `static if` is removed and a `pragma(msg,
Fields!Pointer)` is added it works fine.

It seems to be a bizarre interaction between separate compilation, `anySatisfy`
and `hasElaborateDestructor`. Notably, just trying to call
`hasElaborateDestructor!Pointer` fails with the same error messages.

It's possible to write a new version of `hasElaborateDestructor` that can be
used on `Pointer`:

---------
template sumtypeHasElaborateDestructor(S)
{
    import std.meta : anySatisfy;
    import std.traits: hasMember, Fields, isStaticArray, Fields;

    static if (isStaticArray!S && S.length)
    {
        enum bool sumtypeHasElaborateDestructor =
sumtypeHasElaborateDestructor!(typeof(S.init[0]));
    }
    else static if (is(S == struct))
    {
        alias fields = Fields!S;

        template foo(int index = fields.length - 1) {
            static if(index == 0)
                enum foo = .sumtypeHasElaborateDestructor!(fields[0]);
            else
                enum foo = .sumtypeHasElaborateDestructor!(fields[index]) ||
foo!(index -1);
        }

        enum sumtypeHasElaborateDestructor = hasMember!(S, "__dtor")
            || .sumtypeAnySatisfy!(.sumtypeHasElaborateDestructor, fields);
        //|| foo!();
            //|| anySatisfy!(.sumtypeHasElaborateDestructor, Fields!S);
    }
    else
    {
        enum bool sumtypeHasElaborateDestructor = false;
    }
}

template sumtypeAnySatisfy(alias F, T...) {
    template helper(int index) {
        static if(index == 0)
            enum helper = F!(T[0]);
        else
            enum helper = F!(T[index]) || helper!(index -1);
    }

    static if(T.length == 0)
        enum sumtypeAnySatisfy = false;
    else
        enum sumtypeAnySatisfy = helper!(T.length - 1);
}
---------

And `sumtypeHasElaborateDestuctor!Pointer` compiles. But...
`sumtypeAnySatisfy!(sumtypeHasElaborateDestructor, Types)` fails again with the
same error messages as the standard Phobos versions.

--
Nov 16 2018