diff --git a/CHANGELOG.md b/CHANGELOG.md index f28a578..35010ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [1.1.0] - 2020-01-09 +## [1.1.1] - 2020-09-24 +### Fixed +- Include a copy of std_data_json and taggedalgebraic directly. Work around DMD bug 21235 by hacking opEquals + to be non-templated. + This change should be reverted once the upstream issues are fixed. (Hah. Right.) + +## [1.1.0] - 2020-09-01 ### Added - Allow loading any type with a `fromString` static method from a string, such as an XML attribute. diff --git a/src/funkwerk/stdx/data/json/parser.d b/src/funkwerk/stdx/data/json/parser.d index 703ee31..38af9f0 100644 --- a/src/funkwerk/stdx/data/json/parser.d +++ b/src/funkwerk/stdx/data/json/parser.d @@ -18,6 +18,7 @@ module funkwerk.stdx.data.json.parser; unittest { import std.algorithm : equal, map; + import std.format : format; // Parse a JSON string to a single value JSONValue value = toJSONValue(`{"name": "D", "kind": "language"}`); @@ -32,7 +33,7 @@ unittest // Parse a list of tokens instead of a string auto tokens = lexJSON(`{"name": "D", "kind": "language"}`); JSONValue value2 = toJSONValue(tokens); - assert(value == value2); + assert(value == value2, format!"%s != %s"(value, value2)); } import std.array : appender; diff --git a/src/funkwerk/stdx/data/json/taggedalgebraic/taggedalgebraic.d b/src/funkwerk/stdx/data/json/taggedalgebraic/taggedalgebraic.d index 06195c5..342368c 100644 --- a/src/funkwerk/stdx/data/json/taggedalgebraic/taggedalgebraic.d +++ b/src/funkwerk/stdx/data/json/taggedalgebraic/taggedalgebraic.d @@ -116,27 +116,15 @@ struct TaggedAlgebraic(U) if (is(U == union) || is(U == struct) || is(U == enum) } /// Enables equality comparison with the stored value. - // no "this TA" due to https://issues.dlang.org/show_bug.cgi?id=21235 - auto ref opEquals(T)(auto ref T other) - if (is(Unqual!T == TaggedAlgebraic) || hasOp!(typeof(this), OpKind.binary, "==", T)) + auto ref opEquals(T, this TA)(auto ref T other) + if (!is(Unqual!T == TaggedAlgebraic) && hasOp!(TA, OpKind.binary, "==", T)) { - static if (is(Unqual!T == TaggedAlgebraic)) { - return m_union == other.m_union; - } else return implementOp!(OpKind.binary, "==")(this, other); + return implementOp!(OpKind.binary, "==")(this, other); } - auto ref opEquals(T)(auto ref T other) const - if (is(Unqual!T == TaggedAlgebraic) || hasOp!(typeof(this), OpKind.binary, "==", T)) + // minimal fallback for TypeInfo + bool opEquals(inout TaggedAlgebraic other) @safe inout { - static if (is(Unqual!T == TaggedAlgebraic)) { - return m_union == other.m_union; - } else return implementOp!(OpKind.binary, "==")(this, other); - } - auto ref opEquals(T)(auto ref T other) immutable - if (is(Unqual!T == TaggedAlgebraic) || hasOp!(typeof(this), OpKind.binary, "==", T)) - { - static if (is(Unqual!T == TaggedAlgebraic)) { - return m_union == other.m_union; - } else return implementOp!(OpKind.binary, "==")(this, other); + return this.m_union == other.m_union; } /// Enables relational comparisons with the stored value. auto ref opCmp(T, this TA)(auto ref T other) if (hasOp!(TA, OpKind.binary, "<", T)) { assert(false, "TODO!"); } @@ -401,91 +389,6 @@ unittest { static assert( is(typeof(usc.testSC())));*/ } -unittest { - // test attributes on contained values - import std.typecons : Rebindable, rebindable; - - class C { - void test() {} - void testC() const {} - void testI() immutable {} - } - union U { - Rebindable!(immutable(C)) c; - } - - auto ta = TaggedAlgebraic!U(rebindable(new immutable C)); - static assert(!is(typeof(ta.test()))); - static assert( is(typeof(ta.testC()))); - static assert( is(typeof(ta.testI()))); -} - -// test recursive definition using a wrapper dummy struct -// (needed to avoid "no size yet for forward reference" errors) -unittest { - static struct TA { - union U { - TA[] children; - int value; - } - TaggedAlgebraic!U u; - alias u this; - this(ARGS...)(ARGS args) { u = TaggedAlgebraic!U(args); } - } - - auto ta = TA(null); - ta ~= TA(0); - ta ~= TA(1); - ta ~= TA([TA(2)]); - assert(ta[0] == 0); - assert(ta[1] == 1); - assert(ta[2][0] == 2); -} - -unittest { // postblit/destructor test - static struct S { - static int i = 0; - bool initialized = false; - this(bool) { initialized = true; i++; } - this(this) { if (initialized) i++; } - ~this() { if (initialized) i--; } - } - - static struct U { - S s; - int t; - } - alias TA = TaggedAlgebraic!U; - { - assert(S.i == 0); - auto ta = TA(S(true)); - assert(S.i == 1); - { - auto tb = ta; - assert(S.i == 2); - ta = tb; - assert(S.i == 2); - ta = 1; - assert(S.i == 1); - ta = S(true); - assert(S.i == 2); - } - assert(S.i == 1); - } - assert(S.i == 0); - - static struct U2 { - S a; - S b; - } - alias TA2 = TaggedAlgebraic!U2; - { - auto ta2 = TA2(S(true), TA2.Kind.a); - assert(S.i == 1); - } - assert(S.i == 0); -} - unittest { static struct S { union U { @@ -876,26 +779,6 @@ private template hasAnyMember(TA, string name) alias hasAnyMember = impl!0; } -unittest { - import std.range.primitives : isOutputRange; - import std.typecons : Rebindable; - - struct S { int a, b; void foo() {}} - interface I { void bar() immutable; } - static union U { int x; S s; Rebindable!(const(I)) i; int[] a; } - alias TA = TaggedAlgebraic!U; - static assert(hasAnyMember!(TA, "a")); - static assert(hasAnyMember!(TA, "b")); - static assert(hasAnyMember!(TA, "foo")); - static assert(hasAnyMember!(TA, "bar")); - static assert(hasAnyMember!(TA, "length")); - static assert(hasAnyMember!(TA, "ptr")); - static assert(hasAnyMember!(TA, "capacity")); - static assert(hasAnyMember!(TA, "sizeof")); - static assert(!hasAnyMember!(TA, "put")); - static assert(!isOutputRange!(TA, int)); -} - private template hasOp(TA, OpKind kind, string name, ARGS...) { import std.traits : CopyTypeQualifiers; @@ -903,38 +786,6 @@ private template hasOp(TA, OpKind kind, string name, ARGS...) enum hasOp = AliasSeq!(OpInfo!(UQ, kind, name, ARGS).fields).length > 0; } -unittest { - static struct S { - void m(int i) {} - bool opEquals(int i) { return true; } - bool opEquals(S s) { return true; } - } - - static union U { int i; string s; S st; } - alias TA = TaggedAlgebraic!U; - - static assert(hasOp!(TA, OpKind.binary, "+", int)); - static assert(hasOp!(TA, OpKind.binary, "~", string)); - static assert(hasOp!(TA, OpKind.binary, "==", int)); - static assert(hasOp!(TA, OpKind.binary, "==", string)); - static assert(hasOp!(TA, OpKind.binary, "==", int)); - static assert(hasOp!(TA, OpKind.binary, "==", S)); - static assert(hasOp!(TA, OpKind.method, "m", int)); - static assert(hasOp!(TA, OpKind.binary, "+=", int)); - static assert(!hasOp!(TA, OpKind.binary, "~", int)); - static assert(!hasOp!(TA, OpKind.binary, "~", int)); - static assert(!hasOp!(TA, OpKind.method, "m", string)); - static assert(!hasOp!(TA, OpKind.method, "m")); - static assert(!hasOp!(const(TA), OpKind.binary, "+=", int)); - static assert(!hasOp!(const(TA), OpKind.method, "m", int)); - static assert(!hasOp!(TA, OpKind.method, "put", int)); - - static union U2 { int *i; } - alias TA2 = TaggedAlgebraic!U2; - - static assert(hasOp!(TA2, OpKind.unary, "*")); -} - unittest { struct S { union U { diff --git a/src/funkwerk/stdx/data/json/taggedalgebraic/taggedunion.d b/src/funkwerk/stdx/data/json/taggedalgebraic/taggedunion.d index b9c4eae..e1af0ae 100644 --- a/src/funkwerk/stdx/data/json/taggedalgebraic/taggedunion.d +++ b/src/funkwerk/stdx/data/json/taggedalgebraic/taggedunion.d @@ -190,8 +190,8 @@ align(commonAlignment!(UnionKindTypes!(UnionFieldEnum!U))) struct TaggedUnion } /// Enables equality comparison with the stored value. - bool opEquals()(auto ref inout(TaggedUnion) other) - inout { + bool opEquals(inout TaggedUnion other) @safe inout + { if (this.kind != other.kind) return false; final switch (this.kind) { @@ -379,7 +379,7 @@ align(commonAlignment!(UnionKindTypes!(UnionFieldEnum!U))) struct TaggedUnion } /// -@safe nothrow unittest { +@safe unittest { union Kinds { int count; string text; @@ -427,31 +427,6 @@ align(commonAlignment!(UnionKindTypes!(UnionFieldEnum!U))) struct TaggedUnion assert(tu.textValue == "foo"); } -/// -@safe nothrow unittest { - // Enum annotations supported since DMD 2.082.0. The mixin below is - // necessary to keep the parser happy on older versions. - static if (__VERSION__ >= 2082) { - alias myint = int; - // tagged unions can be defined in terms of an annotated enum - mixin(q{enum E { - none, - @string text - }}); - - alias TU = TaggedUnion!E; - static assert(is(TU.Kind == E)); - - TU tu; - assert(tu.isNone); - assert(tu.kind == E.none); - - tu.setText("foo"); - assert(tu.kind == E.text); - assert(tu.textValue == "foo"); - } -} - unittest { // test for name clashes union U { .string string; } alias TU = TaggedUnion!U; diff --git a/src/funkwerk/stdx/data/json/taggedalgebraic/visit.d b/src/funkwerk/stdx/data/json/taggedalgebraic/visit.d index 7adb05e..54d539a 100644 --- a/src/funkwerk/stdx/data/json/taggedalgebraic/visit.d +++ b/src/funkwerk/stdx/data/json/taggedalgebraic/visit.d @@ -128,22 +128,6 @@ unittest { //static assert(!is(typeof(u.visit!((int) {}, (float) {}, () {}, (_) {})))); // superfluous generic handler } -unittest { - // make sure that the generic handler is not instantiated with types for - // which it doesn't compile - class C {} - union U { int i; C c; } - TaggedUnion!U u; - u.visit!( - (C c) => c !is null, - (v) { - static assert(is(typeof(v) == int)); - return v != 0; - } - ); -} - - /** 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