Merge pull request #24 from s-ludwig/taggedalgebraic_self_referential

Make self-referential types work with TaggedAlgebraic, too.
This commit is contained in:
Leonid Kramer 2019-02-22 20:02:29 +01:00 committed by GitHub
commit cb1b000157
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 68 additions and 76 deletions

View file

@ -43,7 +43,7 @@ import std.traits : EnumMembers, FieldNameTuple, Unqual, isInstanceOf;
as the return value.)
)
*/
struct TaggedAlgebraic(U) if (is(U == union) || is(U == struct))
struct TaggedAlgebraic(U) if (is(U == union) || is(U == struct) || is(U == enum))
{
import std.algorithm : among;
import std.string : format;
@ -51,6 +51,8 @@ struct TaggedAlgebraic(U) if (is(U == union) || is(U == struct))
/// Alias of the type used for defining the possible storage types/kinds.
deprecated alias Union = U;
private alias FieldDefinitionType = U;
/// The underlying tagged union type
alias UnionType = TaggedUnion!U;
@ -301,6 +303,26 @@ unittest { // std.conv integration
assert(TA(1, TA.Kind.e) != TA(1, TA.Kind.f));
}
unittest { // self-referential types
struct S {
int num;
TaggedAlgebraic!This[] arr;
TaggedAlgebraic!This[string] obj;
}
alias TA = TaggedAlgebraic!S;
auto ta = TA([
TA(12),
TA(["bar": TA(13)])
]);
assert(ta.kind == TA.Kind.arr);
assert(ta[0].kind == TA.Kind.num);
assert(ta[0] == 12);
assert(ta[1].kind == TA.Kind.obj);
assert(ta[1]["bar"] == 13);
}
unittest {
// test proper type modifier support
static struct S {
@ -746,7 +768,7 @@ private struct DisableOpAttribute {
private template hasOp(TA, OpKind kind, string name, ARGS...)
{
import std.traits : CopyTypeQualifiers;
alias UQ = CopyTypeQualifiers!(TA, TA.Union);
alias UQ = CopyTypeQualifiers!(TA, TA.FieldDefinitionType);
enum hasOp = AliasSeq!(OpInfo!(UQ, kind, name, ARGS).fields).length > 0;
}
@ -808,7 +830,7 @@ private static auto implementOp(OpKind kind, string name, T, ARGS...)(ref T self
import std.array : join;
import std.traits : CopyTypeQualifiers;
import std.variant : Algebraic, Variant;
alias UQ = CopyTypeQualifiers!(T, T.Union);
alias UQ = CopyTypeQualifiers!(T, T.FieldDefinitionType);
alias info = OpInfo!(UQ, kind, name, ARGS);
@ -829,8 +851,8 @@ private static auto implementOp(OpKind kind, string name, T, ARGS...)(ref T self
case __traits(getMember, T.Kind, f):
static if (NoDuplicates!(info.ReturnTypes).length == 1)
return info.perform(self.m_union.trustedGet!FT, args);
else static if (allSatisfy!(isMatchingUniqueType!(T.Union), info.ReturnTypes))
return TaggedAlgebraic!(T.Union)(info.perform(self.m_union.trustedGet!FT, args));
else static if (allSatisfy!(isMatchingUniqueType!T, info.ReturnTypes))
return TaggedAlgebraic!(T.FieldDefinitionType)(info.perform(self.m_union.trustedGet!FT, args));
else static if (allSatisfy!(isNoVariant, info.ReturnTypes)) {
alias Alg = Algebraic!(NoDuplicates!(info.ReturnTypes));
info.ReturnTypes[i] ret = info.perform(self.m_union.trustedGet!FT, args);
@ -955,10 +977,11 @@ unittest {
private template OpInfo(U, OpKind kind, string name, ARGS...)
{
import std.traits : CopyTypeQualifiers, FieldTypeTuple, FieldNameTuple, ReturnType;
import std.traits : CopyTypeQualifiers, ReturnType;
private alias FieldTypes = FieldTypeTuple!U;
private alias fieldNames = FieldNameTuple!U;
private alias FieldKind = UnionFieldEnum!U;
private alias FieldTypes = UnionKindTypes!FieldKind;
private alias fieldNames = UnionKindNames!FieldKind;
private template isOpEnabled(string field)
{
@ -987,7 +1010,7 @@ private template OpInfo(U, OpKind kind, string name, ARGS...)
template ReturnTypesImpl(size_t i) {
static if (i < fields.length) {
alias FT = CopyTypeQualifiers!(U, typeof(__traits(getMember, U, fields[i])));
alias FT = CopyTypeQualifiers!(U, TypeOf!(__traits(getMember, FieldKind, fields[i])));
alias ReturnTypesImpl = AliasSeq!(ReturnType!(performOp!(U, kind, name, FT, ARGS)), ReturnTypesImpl!(i+1));
} else alias ReturnTypesImpl = AliasSeq!();
}
@ -1074,37 +1097,19 @@ private string generateConstructors(U)()
}
private template UniqueTypeFields(U) {
import std.traits : FieldTypeTuple, FieldNameTuple;
alias Types = FieldTypeTuple!U;
template impl(size_t i) {
static if (i < Types.length) {
enum name = FieldNameTuple!U[i];
alias T = Types[i];
static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i+1 .. $]) < 0)
alias impl = AliasSeq!(name, impl!(i+1));
else alias impl = AliasSeq!(impl!(i+1));
} else alias impl = AliasSeq!();
}
alias UniqueTypeFields = impl!0;
alias Enum = UnionFieldEnum!U;
alias Types = UnionKindTypes!Enum;
alias indices = UniqueTypes!Types;
enum toName(int i) = UnionKindNames!Enum[i];
alias UniqueTypeFields = staticMap!(toName, indices);
}
private template AmbiguousTypeFields(U) {
import std.traits : FieldTypeTuple, FieldNameTuple;
alias Types = FieldTypeTuple!U;
template impl(size_t i) {
static if (i < Types.length) {
enum name = FieldNameTuple!U[i];
alias T = Types[i];
static if (staticIndexOf!(T, Types) == i && staticIndexOf!(T, Types[i+1 .. $]) >= 0)
alias impl = AliasSeq!(name, impl!(i+1));
else alias impl = impl!(i+1);
} else alias impl = AliasSeq!();
}
alias AmbiguousTypeFields = impl!0;
alias Enum = UnionFieldEnum!U;
alias Types = UnionKindTypes!Enum;
alias indices = AmbiguousTypes!Types;
enum toName(int i) = UnionKindNames!Enum[i];
alias AmbiguousTypeFields = staticMap!(toName, indices);
}
unittest {
@ -1118,51 +1123,38 @@ unittest {
static assert([AmbiguousTypeFields!U] == ["a"]);
}
private template SameTypeFields(U, string field) {
import std.traits : FieldTypeTuple, FieldNameTuple;
alias Types = FieldTypeTuple!U;
alias T = typeof(__traits(getMember, U, field));
template impl(size_t i) {
static if (i < Types.length) {
enum name = FieldNameTuple!U[i];
static if (is(Types[i] == T))
alias impl = AliasSeq!(name, impl!(i+1));
else alias impl = AliasSeq!(impl!(i+1));
} else alias impl = AliasSeq!();
}
alias SameTypeFields = impl!0;
}
private template MemberType(U) {
template MemberType(string name) {
alias MemberType = typeof(__traits(getMember, U, name));
}
}
private template isMatchingType(U) {
import std.traits : FieldTypeTuple;
enum isMatchingType(T) = staticIndexOf!(T, FieldTypeTuple!U) >= 0;
}
private template isMatchingUniqueType(U) {
private template isMatchingUniqueType(TA) {
import std.traits : staticMap;
alias UniqueTypes = staticMap!(FieldTypeOf!U, UniqueTypeFields!U);
alias FieldTypes = UnionKindTypes!(UnionFieldEnum!(TA.FieldDefinitionType));
alias F(size_t i) = FieldTypes[i];
alias UniqueTypes = staticMap!(F, .UniqueTypes!FieldTypes);
template isMatchingUniqueType(T) {
static if (is(T : TaggedAlgebraic!U)) enum isMatchingUniqueType = true;
static if (is(T : TA)) enum isMatchingUniqueType = true;
else enum isMatchingUniqueType = staticIndexOfImplicit!(T, UniqueTypes) >= 0;
}
}
unittest {
union U {
int i;
TaggedAlgebraic!This[] array;
}
alias TA = TaggedAlgebraic!U;
alias pass(alias templ, T) = templ!T;
static assert(pass!(isMatchingUniqueType!TA, TaggedAlgebraic!U));
static assert(!pass!(isMatchingUniqueType!TA, string));
static assert(pass!(isMatchingUniqueType!TA, int));
static assert(pass!(isMatchingUniqueType!TA, (TaggedAlgebraic!U[])));
}
private template fieldMatchesType(U, T)
{
enum fieldMatchesType(string field) = is(typeof(__traits(getMember, U, field)) == T);
enum fieldMatchesType(string field) = is(TypeOf!(__traits(getMember, UnionFieldEnum!U, field)) == T);
}
private template FieldTypeOf(U) {
template FieldTypeOf(string name) {
alias FieldTypeOf = typeof(__traits(getMember, U, name));
alias FieldTypeOf = TypeOf!(__traits(getMember, UnionFieldEnum!U, name));
}
}

View file

@ -653,7 +653,7 @@ unittest {
assert(n.listValue == [Node(12), Node("foo")]);
}
private template UnionFieldEnum(U)
package template UnionFieldEnum(U)
{
static if (is(U == enum)) alias UnionFieldEnum = U;
else {
@ -665,10 +665,10 @@ private template UnionFieldEnum(U)
deprecated alias TypeEnum(U) = UnionFieldEnum!U;
private alias UnionKindTypes(FieldEnum) = staticMap!(TypeOf, EnumMembers!FieldEnum);
private alias UnionKindNames(FieldEnum) = AliasSeq!(__traits(allMembers, FieldEnum));
package alias UnionKindTypes(FieldEnum) = staticMap!(TypeOf, EnumMembers!FieldEnum);
package alias UnionKindNames(FieldEnum) = AliasSeq!(__traits(allMembers, FieldEnum));
private template UniqueTypes(Types...) {
package template UniqueTypes(Types...) {
template impl(size_t i) {
static if (i < Types.length) {
alias T = Types[i];
@ -680,7 +680,7 @@ private template UniqueTypes(Types...) {
alias UniqueTypes = impl!0;
}
private template AmbiguousTypes(Types...) {
package template AmbiguousTypes(Types...) {
template impl(size_t i) {
static if (i < Types.length) {
alias T = Types[i];
@ -689,7 +689,7 @@ private template AmbiguousTypes(Types...) {
else alias impl = impl!(i+1);
} else alias impl = AliasSeq!();
}
alias AmbiguousTypeFields = impl!0;
alias AmbiguousTypes = impl!0;
}