Skip to content
This repository has been archived by the owner on Oct 21, 2022. It is now read-only.

Commit

Permalink
Massively simplify opEquals in taggedalgebraic to sidestep https://is…
Browse files Browse the repository at this point in the history
…sues.dlang.org/show_bug.cgi?id=21235 .

Remove all unittests that are incompatible with the new definition.
  • Loading branch information
FeepingCreature committed Sep 24, 2020
1 parent e9004fc commit 85f125b
Show file tree
Hide file tree
Showing 5 changed files with 18 additions and 201 deletions.
8 changes: 7 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
3 changes: 2 additions & 1 deletion src/funkwerk/stdx/data/json/parser.d
Original file line number Diff line number Diff line change
Expand Up @@ -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"}`);
Expand All @@ -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;
Expand Down
161 changes: 6 additions & 155 deletions src/funkwerk/stdx/data/json/taggedalgebraic/taggedalgebraic.d
Original file line number Diff line number Diff line change
Expand Up @@ -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!"); }
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -876,65 +779,13 @@ 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;
alias UQ = CopyTypeQualifiers!(TA, TA.FieldDefinitionType);
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 {
Expand Down
31 changes: 3 additions & 28 deletions src/funkwerk/stdx/data/json/taggedalgebraic/taggedunion.d
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -379,7 +379,7 @@ align(commonAlignment!(UnionKindTypes!(UnionFieldEnum!U))) struct TaggedUnion
}

///
@safe nothrow unittest {
@safe unittest {
union Kinds {
int count;
string text;
Expand Down Expand Up @@ -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;
Expand Down
16 changes: 0 additions & 16 deletions src/funkwerk/stdx/data/json/taggedalgebraic/visit.d
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 85f125b

Please sign in to comment.