std.variant.Algebraic, self referential types and delegate members

Jonathan M Davis via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sun Nov 8 03:27:52 PST 2015


On Sunday, November 08, 2015 10:31:11 Panke via Digitalmars-d-learn wrote:
> import std.variant, std.stdio;
>
> ---
> struct NodeTypeA(T) { T[] children; }
> struct NodeTypeB(T) { Tree children; }
> struct Leaf(T) { T delegate() dg; }
>
> alias Tree = Algebraic!(Leaf, NodeTypeA!This, NodeTypeB!This);
>
> void main()
> {
>    Tree t;
> }
> ---
>
> yields
>
> tmp.d(6): Error: functions cannot return opaque type This by value
> tmp.d(8): Error: template instance tmp.Leaf!(This) error
> instantiating
>
> This limitation seems arbitrary to me. What's the reason here?

Okay. Several things here. For starters, NodeTypeA, NodeTypeB, and Leaf are
not actually types. They're templates for types. If you want a type, you
have to instantiate them. So, something like Algebraic!Leaf doesn't make any
sense. You need an instantiation of Leaf - e.g. Algebraic!(Leaf!int) -
rather than just Leaf.

Next, you have a recursive template instantiation going on here. Tree
depends on knowing what a NodeTypeB looks like, because it's using it in its
instantiation of Algebraic. Algebraic!(Foo, Bar) is told to hold either a
Foo or a Bar, which means that it needs to know their definitions - not just
their names. You need to be using pointers if you want to be able to avoid
having to know the actual definition of the type. So, when you tell
NodeTypeB to hold a Tree when a Tree holds a NodeTypeB, it's impossible to
figure out what the actual layout of those types. You have a recursive
expansion.

If you want to have a recursive type definition, you _must_ use pointers so
that the actual definition of the type is not required.

Also, I have no idea what the deal with the This in your code is. IIRC,
there's a feature involving This with template definitions, but you're just
using it outside of a template definition, so I don't know if that's legal.
But I'm also not at all familiar with what This actually does even if it's
used in the right place. So, maybe what you're doing with it is valid. I
question it, but I don't know enough to know for sure one way or the other.

Now, mucking around with your code, trying to see if I could get it to
compile, I suspect that Algebraic has something in its implementation that
doesn't play nicely with what you're trying to do (which may or may not
be a bug), but I don't know exactly what's going on. The error message I'm
getting (which does not match the one that you're getting) involves a
complaint about recursive template expansion, which implies that Algebraic
may need to know the definition of Foo even if you instantiate it with Foo*
- e.g. Algebraic!(Foo*), though I don't know why that would be the case.

The error that you're seeing implies that the code is trying to return a
type by value when it does not know the definition of the type, and that's
not going to work. To return a type by value, the compiler needs to know
what its layout is. So, that probably relates to trying to the recursive
expansion problem where the code ends up trying to use a NodeTypeB!This when
it doesn't know the layout of NodeTypeB!This yet, because it's trying to
define Tree, which requires that it know the definition of NodeTypeB!This...
So, it's likely that with whatever version of the compiler you're using,
you're just triggering a slightly different error than the version I'm using
hits, though the root cause is the same.

So, you need to change your types so that they're not actually recursively
defined (which almost certainly means using pointers), and Algebraic may or
may not actually be able to do what you want due to some aspect of its
current implementation.

- Jonathan M Davis



More information about the Digitalmars-d-learn mailing list