From b766f62d432aa7bbd0566b83e2912900930668fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Sat, 23 Feb 2019 15:55:57 +0100 Subject: [PATCH 1/5] Actually check for superfluous handlers. --- source/taggedalgebraic/taggedunion.d | 34 ++++++++++++++++------------ 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/source/taggedalgebraic/taggedunion.d b/source/taggedalgebraic/taggedunion.d index 5487ada..335bab0 100644 --- a/source/taggedalgebraic/taggedunion.d +++ b/source/taggedalgebraic/taggedunion.d @@ -240,9 +240,9 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum)) if (staticIndexOf!(T, FieldTypes) >= 0) { final switch (this.kind) { - static foreach (n; fieldNames) { + static foreach (i, n; fieldNames) { case __traits(getMember, Kind, n): - static if (is(FieldTypes[__traits(getMember, Kind, n)] == T)) + static if (is(FieldTypes[i] == T)) return trustedGet!T; else assert(false, "Attempting to get type "~T.stringof ~ " from a TaggedUnion with type " @@ -383,6 +383,8 @@ template visit(VISITORS...) auto visit(TU)(auto ref TU tu) if (isInstanceOf!(TaggedUnion, TU)) { + alias val = validateHandlers!(TU, VISITORS); + final switch (tu.kind) { static foreach (k; EnumMembers!(TU.Kind)) { case k: { @@ -431,7 +433,6 @@ unittest { // static assert(is(typeof(u.visit!((int) {}, (float) {}, () {})))); - u.visit!((_) {}, () {}); static assert(is(typeof(u.visit!((_) {}, () {})))); static assert(is(typeof(u.visit!((_) {}, (float) {}, () {})))); static assert(is(typeof(u.visit!((float) {}, (_) {}, () {})))); @@ -512,20 +513,23 @@ private template validateHandlers(TU, VISITORS...) } } -private template matchesType(alias fun, T) -{ - static if (isSomeFunction!fun) { - alias Params = ParameterTypeTuple!fun; - static if (Params.length == 0 && is(T == void)) enum matchesType = true; - else static if (Params.length == 1 && is(T == Params[0])) enum matchesType = true; - else enum matchesType = false; - } else static if (!is(T == void)) { - static if (isSomeFunction!(fun!T)) { - alias Parms = ParameterTypeTuple!fun; - static if (Params.length == 1 && is(T == Params[0])) enum matchesType = true; +private template matchesType(alias fun) { + import std.traits : ParameterTypeTuple, isSomeFunction; + + template matchesType(T) { + static if (isSomeFunction!fun) { + alias Params = ParameterTypeTuple!fun; + static if (Params.length == 0 && isUnitType!T) enum matchesType = true; + else static if (Params.length == 1 && is(T == Params[0])) enum matchesType = true; else enum matchesType = false; + } else static if (!isUnitType!T) { + static if (isSomeFunction!(fun!T)) { + alias Params = ParameterTypeTuple!(fun!T); + static if (Params.length == 1 && is(T == Params[0])) enum matchesType = true; + else enum matchesType = false; + } else enum matchesType = false; } else enum matchesType = false; - } else enum matchesType = false; + } } private template selectHandler(T, VISITORS...) From a52958ef018e9d2a503be868e0dcf828c78b02bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Sat, 23 Feb 2019 15:56:59 +0100 Subject: [PATCH 2/5] Work around ReplaceType compile error issue. ReplaceType doesn't work for "class C : D!C {}" --- source/taggedalgebraic/taggedunion.d | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/taggedalgebraic/taggedunion.d b/source/taggedalgebraic/taggedunion.d index 335bab0..40b37ba 100644 --- a/source/taggedalgebraic/taggedunion.d +++ b/source/taggedalgebraic/taggedunion.d @@ -603,7 +603,12 @@ template TypeOf(alias kind) else alias FT = uda[0]; } - alias TypeOf = ReplaceType!(This, U, FT); + // NOTE: ReplaceType has issues with certain types, such as a class + // declaration like this: class C : D!C {} + // For this reason, we test first if it compiles and only then use it. + static if (is(ReplaceType!(This, U, FT))) + alias TypeOf = ReplaceType!(This, U, FT); + else alias TypeOf = FT; } /// From 6589bac23a5fa04590c006b6e885b69243b78269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Sat, 23 Feb 2019 16:16:20 +0100 Subject: [PATCH 3/5] Add TaggedUnion.hasType!T. --- source/taggedalgebraic/taggedunion.d | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/source/taggedalgebraic/taggedunion.d b/source/taggedalgebraic/taggedunion.d index 40b37ba..a564d6e 100644 --- a/source/taggedalgebraic/taggedunion.d +++ b/source/taggedalgebraic/taggedunion.d @@ -203,7 +203,20 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum)) } else { mixin("static @property TaggedUnion "~name~"() { TaggedUnion tu; tu.set!(Kind."~name~"); return tu; }"); } + } + /** Checks whether the currently stored value has a given type. + */ + @property bool hasType(T)() + const { + static assert(staticIndexOf!(T, FieldTypes) >= 0, "Type "~T.stringof~ " not part of "~FieldTypes.stringof); + + final switch (this.kind) { + static foreach (i, n; fieldNames) { + case __traits(getMember, Kind, n): + return is(FieldTypes[i] == T); + } + } } /** Accesses the contained value by reference. @@ -237,8 +250,9 @@ struct TaggedUnion(U) if (is(U == union) || is(U == struct) || is(U == enum)) See_Also: `set`, `opAssign` */ @property ref inout(T) value(T)() inout - if (staticIndexOf!(T, FieldTypes) >= 0) { + static assert(staticIndexOf!(T, FieldTypes) >= 0, "Type "~T.stringof~ " not part of "~FieldTypes.stringof); + final switch (this.kind) { static foreach (i, n; fieldNames) { case __traits(getMember, Kind, n): From 56d50fd56c86551a3a38b141a75b30eed2c5c3ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Sat, 23 Feb 2019 17:19:00 +0100 Subject: [PATCH 4/5] Work around Phobos issue 19696. --- source/taggedalgebraic/taggedunion.d | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/source/taggedalgebraic/taggedunion.d b/source/taggedalgebraic/taggedunion.d index a564d6e..bf1b080 100644 --- a/source/taggedalgebraic/taggedunion.d +++ b/source/taggedalgebraic/taggedunion.d @@ -378,6 +378,22 @@ unittest { // test for name clashes assert(tu.stringValue == "foo"); } +unittest { // test woraround for Phobos issue 19696 + struct T { + struct F { int num; } + alias Payload = TaggedUnion!F; + Payload payload; + alias payload this; + } + + struct U { + T t; + } + + alias TU = TaggedUnion!U; + static assert(is(TU.FieldTypes[0] == T)); +} + /** Dispatches the value contained on a `TaggedUnion` to a set of visitors. @@ -620,7 +636,12 @@ template TypeOf(alias kind) // NOTE: ReplaceType has issues with certain types, such as a class // declaration like this: class C : D!C {} // For this reason, we test first if it compiles and only then use it. - static if (is(ReplaceType!(This, U, FT))) + // It also replaces a type with the contained "alias this" type under + // certain conditions, so we make a second check to see heuristically + // if This is actually present in FT + // + // Phobos issues: 19696, 19697 + static if (is(ReplaceType!(This, U, FT)) && !is(ReplaceType!(This, void, FT))) alias TypeOf = ReplaceType!(This, U, FT); else alias TypeOf = FT; } From 7145693748de755747bd1195682d69245145cb61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=B6nke=20Ludwig?= Date: Sat, 23 Feb 2019 17:47:24 +0100 Subject: [PATCH 5/5] Work around name lookup issue on DMD < 2.081.0. --- source/taggedalgebraic/taggedunion.d | 44 +++++++++++++--------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/source/taggedalgebraic/taggedunion.d b/source/taggedalgebraic/taggedunion.d index bf1b080..b302e10 100644 --- a/source/taggedalgebraic/taggedunion.d +++ b/source/taggedalgebraic/taggedunion.d @@ -434,23 +434,27 @@ template visit(VISITORS...) /// unittest { - union U { - int number; - string text; + static if (__VERSION__ >= 2081) { + import std.conv : to; + + union U { + int number; + string text; + } + alias TU = TaggedUnion!U; + + auto tu = TU.number(42); + tu.visit!( + (int n) { assert(n == 42); }, + (string s) { assert(false); } + ); + + assert(tu.visit!((v) => to!int(v)) == 42); + + tu.setText("43"); + + assert(tu.visit!((v) => to!int(v)) == 43); } - alias TU = TaggedUnion!U; - - auto tu = TU.number(42); - tu.visit!( - (int n) { assert(n == 42); }, - (string s) { assert(false); } - ); - - assert(tu.visit!((v) => to!int(v)) == 42); - - tu.setText("43"); - - assert(tu.visit!((v) => to!int(v)) == 43); } unittest { @@ -480,14 +484,6 @@ unittest { } -// workaround for "template to is not defined" error in the unit test above -// happens on DMD 2.080 and below -private U to(U, T)(T val) { - static import std.conv; - return std.conv.to!U(val); -} - - /** The same as `visit`, except that failure to handle types is checked at runtime. Instead of failing to compile, `tryVisit` will throw an `Exception` if none