aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/ext_depends/D-YAML/source/dyaml/stdsumtype.d
diff options
context:
space:
mode:
Diffstat (limited to 'src/ext_depends/D-YAML/source/dyaml/stdsumtype.d')
-rw-r--r--src/ext_depends/D-YAML/source/dyaml/stdsumtype.d2627
1 files changed, 2627 insertions, 0 deletions
diff --git a/src/ext_depends/D-YAML/source/dyaml/stdsumtype.d b/src/ext_depends/D-YAML/source/dyaml/stdsumtype.d
new file mode 100644
index 0000000..3dac4dd
--- /dev/null
+++ b/src/ext_depends/D-YAML/source/dyaml/stdsumtype.d
@@ -0,0 +1,2627 @@
+/++
+ This module was copied from Phobos at commit 87c6e7e35 (2022-07-06).
+ This is necessary to include https://github.com/dlang/phobos/pull/8501
+ which is a fix needed for DIP1000 compatibility. A couple minor changes
+ where also required to deal with `package(std)` imports.
+
+[SumType] is a generic discriminated union implementation that uses
+design-by-introspection to generate safe and efficient code. Its features
+include:
+
+* [Pattern matching.][match]
+* Support for self-referential types.
+* Full attribute correctness (`pure`, `@safe`, `@nogc`, and `nothrow` are
+ inferred whenever possible).
+* A type-safe and memory-safe API compatible with DIP 1000 (`scope`).
+* No dependency on runtime type information (`TypeInfo`).
+* Compatibility with BetterC.
+
+License: Boost License 1.0
+Authors: Paul Backus
+Source: $(PHOBOSSRC std/sumtype.d)
++/
+module dyaml.stdsumtype;
+
+/// $(DIVID basic-usage,$(H3 Basic usage))
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.math.operations : isClose;
+
+ struct Fahrenheit { double degrees; }
+ struct Celsius { double degrees; }
+ struct Kelvin { double degrees; }
+
+ alias Temperature = SumType!(Fahrenheit, Celsius, Kelvin);
+
+ // Construct from any of the member types.
+ Temperature t1 = Fahrenheit(98.6);
+ Temperature t2 = Celsius(100);
+ Temperature t3 = Kelvin(273);
+
+ // Use pattern matching to access the value.
+ Fahrenheit toFahrenheit(Temperature t)
+ {
+ return Fahrenheit(
+ t.match!(
+ (Fahrenheit f) => f.degrees,
+ (Celsius c) => c.degrees * 9.0/5 + 32,
+ (Kelvin k) => k.degrees * 9.0/5 - 459.4
+ )
+ );
+ }
+
+ assert(toFahrenheit(t1).degrees.isClose(98.6));
+ assert(toFahrenheit(t2).degrees.isClose(212));
+ assert(toFahrenheit(t3).degrees.isClose(32));
+
+ // Use ref to modify the value in place.
+ void freeze(ref Temperature t)
+ {
+ t.match!(
+ (ref Fahrenheit f) => f.degrees = 32,
+ (ref Celsius c) => c.degrees = 0,
+ (ref Kelvin k) => k.degrees = 273
+ );
+ }
+
+ freeze(t1);
+ assert(toFahrenheit(t1).degrees.isClose(32));
+
+ // Use a catch-all handler to give a default result.
+ bool isFahrenheit(Temperature t)
+ {
+ return t.match!(
+ (Fahrenheit f) => true,
+ _ => false
+ );
+ }
+
+ assert(isFahrenheit(t1));
+ assert(!isFahrenheit(t2));
+ assert(!isFahrenheit(t3));
+}
+
+/** $(DIVID introspection-based-matching, $(H3 Introspection-based matching))
+ *
+ * In the `length` and `horiz` functions below, the handlers for `match` do not
+ * specify the types of their arguments. Instead, matching is done based on how
+ * the argument is used in the body of the handler: any type with `x` and `y`
+ * properties will be matched by the `rect` handlers, and any type with `r` and
+ * `theta` properties will be matched by the `polar` handlers.
+ */
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.math.operations : isClose;
+ import std.math.trigonometry : cos;
+ import std.math.constants : PI;
+ import std.math.algebraic : sqrt;
+
+ struct Rectangular { double x, y; }
+ struct Polar { double r, theta; }
+ alias Vector = SumType!(Rectangular, Polar);
+
+ double length(Vector v)
+ {
+ return v.match!(
+ rect => sqrt(rect.x^^2 + rect.y^^2),
+ polar => polar.r
+ );
+ }
+
+ double horiz(Vector v)
+ {
+ return v.match!(
+ rect => rect.x,
+ polar => polar.r * cos(polar.theta)
+ );
+ }
+
+ Vector u = Rectangular(1, 1);
+ Vector v = Polar(1, PI/4);
+
+ assert(length(u).isClose(sqrt(2.0)));
+ assert(length(v).isClose(1));
+ assert(horiz(u).isClose(1));
+ assert(horiz(v).isClose(sqrt(0.5)));
+}
+
+/** $(DIVID arithmetic-expression-evaluator, $(H3 Arithmetic expression evaluator))
+ *
+ * This example makes use of the special placeholder type `This` to define a
+ * [recursive data type](https://en.wikipedia.org/wiki/Recursive_data_type): an
+ * [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) for
+ * representing simple arithmetic expressions.
+ */
+version (D_BetterC) {} else
+@system unittest
+{
+ import std.functional : partial;
+ import std.traits : EnumMembers;
+ import std.typecons : Tuple;
+
+ enum Op : string
+ {
+ Plus = "+",
+ Minus = "-",
+ Times = "*",
+ Div = "/"
+ }
+
+ // An expression is either
+ // - a number,
+ // - a variable, or
+ // - a binary operation combining two sub-expressions.
+ alias Expr = SumType!(
+ double,
+ string,
+ Tuple!(Op, "op", This*, "lhs", This*, "rhs")
+ );
+
+ // Shorthand for Tuple!(Op, "op", Expr*, "lhs", Expr*, "rhs"),
+ // the Tuple type above with Expr substituted for This.
+ alias BinOp = Expr.Types[2];
+
+ // Factory function for number expressions
+ Expr* num(double value)
+ {
+ return new Expr(value);
+ }
+
+ // Factory function for variable expressions
+ Expr* var(string name)
+ {
+ return new Expr(name);
+ }
+
+ // Factory function for binary operation expressions
+ Expr* binOp(Op op, Expr* lhs, Expr* rhs)
+ {
+ return new Expr(BinOp(op, lhs, rhs));
+ }
+
+ // Convenience wrappers for creating BinOp expressions
+ alias sum = partial!(binOp, Op.Plus);
+ alias diff = partial!(binOp, Op.Minus);
+ alias prod = partial!(binOp, Op.Times);
+ alias quot = partial!(binOp, Op.Div);
+
+ // Evaluate expr, looking up variables in env
+ double eval(Expr expr, double[string] env)
+ {
+ return expr.match!(
+ (double num) => num,
+ (string var) => env[var],
+ (BinOp bop)
+ {
+ double lhs = eval(*bop.lhs, env);
+ double rhs = eval(*bop.rhs, env);
+ final switch (bop.op)
+ {
+ static foreach (op; EnumMembers!Op)
+ {
+ case op:
+ return mixin("lhs" ~ op ~ "rhs");
+ }
+ }
+ }
+ );
+ }
+
+ // Return a "pretty-printed" representation of expr
+ string pprint(Expr expr)
+ {
+ import std.format : format;
+
+ return expr.match!(
+ (double num) => "%g".format(num),
+ (string var) => var,
+ (BinOp bop) => "(%s %s %s)".format(
+ pprint(*bop.lhs),
+ cast(string) bop.op,
+ pprint(*bop.rhs)
+ )
+ );
+ }
+
+ Expr* myExpr = sum(var("a"), prod(num(2), var("b")));
+ double[string] myEnv = ["a":3, "b":4, "c":7];
+
+ assert(eval(*myExpr, myEnv) == 11);
+ assert(pprint(*myExpr) == "(a + (2 * b))");
+}
+
+import std.format.spec : FormatSpec, singleSpec;
+import std.meta : AliasSeq, Filter, IndexOf = staticIndexOf, Map = staticMap;
+import std.meta : NoDuplicates;
+import std.meta : anySatisfy, allSatisfy;
+import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor;
+import std.traits : isAssignable, isCopyable, isStaticArray, isRvalueAssignable;
+import std.traits : ConstOf, ImmutableOf, InoutOf, TemplateArgsOf;
+
+// FIXME: std.sumtype : `std.traits : DeducedParameterType` and `std.conv : toCtString`
+// are `package(std)` but trivial, hence copied below
+import std.traits : CommonType, /*DeducatedParameterType*/ Unqual;
+private template DeducedParameterType(T)
+{
+ static if (is(T == U*, U) || is(T == U[], U))
+ alias DeducedParameterType = Unqual!T;
+ else
+ alias DeducedParameterType = T;
+}
+
+import std.typecons : ReplaceTypeUnless;
+import std.typecons : Flag;
+//import std.conv : toCtString;
+private enum toCtString(ulong n) = n.stringof[0 .. $ - "LU".length];
+
+/// Placeholder used to refer to the enclosing [SumType].
+struct This {}
+
+// True if a variable of type T can appear on the lhs of an assignment
+private enum isAssignableTo(T) =
+ isAssignable!T || (!isCopyable!T && isRvalueAssignable!T);
+
+// toHash is required by the language spec to be nothrow and @safe
+private enum isHashable(T) = __traits(compiles,
+ () nothrow @safe { hashOf(T.init); }
+);
+
+private enum hasPostblit(T) = __traits(hasPostblit, T);
+
+private enum isInout(T) = is(T == inout);
+
+/**
+ * A [tagged union](https://en.wikipedia.org/wiki/Tagged_union) that can hold a
+ * single value from any of a specified set of types.
+ *
+ * The value in a `SumType` can be operated on using [pattern matching][match].
+ *
+ * To avoid ambiguity, duplicate types are not allowed (but see the
+ * ["basic usage" example](#basic-usage) for a workaround).
+ *
+ * The special type `This` can be used as a placeholder to create
+ * self-referential types, just like with `Algebraic`. See the
+ * ["Arithmetic expression evaluator" example](#arithmetic-expression-evaluator) for
+ * usage.
+ *
+ * A `SumType` is initialized by default to hold the `.init` value of its
+ * first member type, just like a regular union. The version identifier
+ * `SumTypeNoDefaultCtor` can be used to disable this behavior.
+ *
+ * See_Also: $(REF Algebraic, std,variant)
+ */
+struct SumType(Types...)
+if (is(NoDuplicates!Types == Types) && Types.length > 0)
+{
+ /// The types a `SumType` can hold.
+ alias Types = AliasSeq!(
+ ReplaceTypeUnless!(isSumTypeInstance, This, typeof(this), TemplateArgsOf!SumType)
+ );
+
+private:
+
+ enum bool canHoldTag(T) = Types.length <= T.max;
+ alias unsignedInts = AliasSeq!(ubyte, ushort, uint, ulong);
+
+ alias Tag = Filter!(canHoldTag, unsignedInts)[0];
+
+ union Storage
+ {
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=20068
+ template memberName(T)
+ if (IndexOf!(T, Types) >= 0)
+ {
+ enum tid = IndexOf!(T, Types);
+ mixin("enum memberName = `values_", toCtString!tid, "`;");
+ }
+
+ static foreach (T; Types)
+ {
+ mixin("T ", memberName!T, ";");
+ }
+ }
+
+ Storage storage;
+ Tag tag;
+
+ /* Accesses the value stored in a SumType.
+ *
+ * This method is memory-safe, provided that:
+ *
+ * 1. A SumType's tag is always accurate.
+ * 2. A SumType cannot be assigned to in @safe code if that assignment
+ * could cause unsafe aliasing.
+ *
+ * All code that accesses a SumType's tag or storage directly, including
+ * @safe code in this module, must be manually checked to ensure that it
+ * does not violate either of the above requirements.
+ */
+ @trusted
+ ref inout(T) get(T)() inout
+ if (IndexOf!(T, Types) >= 0)
+ {
+ enum tid = IndexOf!(T, Types);
+ assert(tag == tid,
+ "This `" ~ SumType.stringof ~
+ "` does not contain a(n) `" ~ T.stringof ~ "`"
+ );
+ return __traits(getMember, storage, Storage.memberName!T);
+ }
+
+public:
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21399
+ version (StdDdoc)
+ {
+ // Dummy type to stand in for loop variable
+ private struct T;
+
+ /// Constructs a `SumType` holding a specific value.
+ this(T value);
+
+ /// ditto
+ this(const(T) value) const;
+
+ /// ditto
+ this(immutable(T) value) immutable;
+
+ /// ditto
+ this(Value)(Value value) inout
+ if (is(Value == DeducedParameterType!(inout(T))));
+ }
+
+ static foreach (tid, T; Types)
+ {
+ /// Constructs a `SumType` holding a specific value.
+ this(T value)
+ {
+ import core.lifetime : forward;
+
+ static if (isCopyable!T)
+ {
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
+ __traits(getMember, storage, Storage.memberName!T) = __ctfe ? value : forward!value;
+ }
+ else
+ {
+ __traits(getMember, storage, Storage.memberName!T) = forward!value;
+ }
+
+ tag = tid;
+ }
+
+ static if (isCopyable!(const(T)))
+ {
+ static if (IndexOf!(const(T), Map!(ConstOf, Types)) == tid)
+ {
+ /// ditto
+ this(const(T) value) const
+ {
+ __traits(getMember, storage, Storage.memberName!T) = value;
+ tag = tid;
+ }
+ }
+ }
+ else
+ {
+ @disable this(const(T) value) const;
+ }
+
+ static if (isCopyable!(immutable(T)))
+ {
+ static if (IndexOf!(immutable(T), Map!(ImmutableOf, Types)) == tid)
+ {
+ /// ditto
+ this(immutable(T) value) immutable
+ {
+ __traits(getMember, storage, Storage.memberName!T) = value;
+ tag = tid;
+ }
+ }
+ }
+ else
+ {
+ @disable this(immutable(T) value) immutable;
+ }
+
+ static if (isCopyable!(inout(T)))
+ {
+ static if (IndexOf!(inout(T), Map!(InoutOf, Types)) == tid)
+ {
+ /// ditto
+ this(Value)(Value value) inout
+ if (is(Value == DeducedParameterType!(inout(T))))
+ {
+ __traits(getMember, storage, Storage.memberName!T) = value;
+ tag = tid;
+ }
+ }
+ }
+ else
+ {
+ @disable this(Value)(Value value) inout
+ if (is(Value == DeducedParameterType!(inout(T))));
+ }
+ }
+
+ static if (anySatisfy!(hasElaborateCopyConstructor, Types))
+ {
+ static if
+ (
+ allSatisfy!(isCopyable, Map!(InoutOf, Types))
+ && !anySatisfy!(hasPostblit, Map!(InoutOf, Types))
+ && allSatisfy!(isInout, Map!(InoutOf, Types))
+ )
+ {
+ /// Constructs a `SumType` that's a copy of another `SumType`.
+ this(ref inout(SumType) other) inout
+ {
+ storage = other.match!((ref value) {
+ alias OtherTypes = Map!(InoutOf, Types);
+ enum tid = IndexOf!(typeof(value), OtherTypes);
+ alias T = Types[tid];
+
+ mixin("inout(Storage) newStorage = { ",
+ Storage.memberName!T, ": value",
+ " };");
+
+ return newStorage;
+ });
+
+ tag = other.tag;
+ }
+ }
+ else
+ {
+ static if (allSatisfy!(isCopyable, Types))
+ {
+ /// ditto
+ this(ref SumType other)
+ {
+ storage = other.match!((ref value) {
+ alias T = typeof(value);
+
+ mixin("Storage newStorage = { ",
+ Storage.memberName!T, ": value",
+ " };");
+
+ return newStorage;
+ });
+
+ tag = other.tag;
+ }
+ }
+ else
+ {
+ @disable this(ref SumType other);
+ }
+
+ static if (allSatisfy!(isCopyable, Map!(ConstOf, Types)))
+ {
+ /// ditto
+ this(ref const(SumType) other) const
+ {
+ storage = other.match!((ref value) {
+ alias OtherTypes = Map!(ConstOf, Types);
+ enum tid = IndexOf!(typeof(value), OtherTypes);
+ alias T = Types[tid];
+
+ mixin("const(Storage) newStorage = { ",
+ Storage.memberName!T, ": value",
+ " };");
+
+ return newStorage;
+ });
+
+ tag = other.tag;
+ }
+ }
+ else
+ {
+ @disable this(ref const(SumType) other) const;
+ }
+
+ static if (allSatisfy!(isCopyable, Map!(ImmutableOf, Types)))
+ {
+ /// ditto
+ this(ref immutable(SumType) other) immutable
+ {
+ storage = other.match!((ref value) {
+ alias OtherTypes = Map!(ImmutableOf, Types);
+ enum tid = IndexOf!(typeof(value), OtherTypes);
+ alias T = Types[tid];
+
+ mixin("immutable(Storage) newStorage = { ",
+ Storage.memberName!T, ": value",
+ " };");
+
+ return newStorage;
+ });
+
+ tag = other.tag;
+ }
+ }
+ else
+ {
+ @disable this(ref immutable(SumType) other) immutable;
+ }
+ }
+ }
+
+ version (SumTypeNoDefaultCtor)
+ {
+ @disable this();
+ }
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21399
+ version (StdDdoc)
+ {
+ // Dummy type to stand in for loop variable
+ private struct T;
+
+ /**
+ * Assigns a value to a `SumType`.
+ *
+ * If any of the `SumType`'s members other than the one being assigned
+ * to contain pointers or references, it is possible for the assignment
+ * to cause memory corruption (see the
+ * ["Memory corruption" example](#memory-corruption) below for an
+ * illustration of how). Therefore, such assignments are considered
+ * `@system`.
+ *
+ * An individual assignment can be `@trusted` if the caller can
+ * guarantee that there are no outstanding references to any `SumType`
+ * members that contain pointers or references at the time the
+ * assignment occurs.
+ *
+ * Examples:
+ *
+ * $(DIVID memory-corruption, $(H3 Memory corruption))
+ *
+ * This example shows how assignment to a `SumType` can be used to
+ * cause memory corruption in `@system` code. In `@safe` code, the
+ * assignment `s = 123` would not be allowed.
+ *
+ * ---
+ * SumType!(int*, int) s = new int;
+ * s.tryMatch!(
+ * (ref int* p) {
+ * s = 123; // overwrites `p`
+ * return *p; // undefined behavior
+ * }
+ * );
+ * ---
+ */
+ ref SumType opAssign(T rhs);
+ }
+
+ static foreach (tid, T; Types)
+ {
+ static if (isAssignableTo!T)
+ {
+ /**
+ * Assigns a value to a `SumType`.
+ *
+ * If any of the `SumType`'s members other than the one being assigned
+ * to contain pointers or references, it is possible for the assignment
+ * to cause memory corruption (see the
+ * ["Memory corruption" example](#memory-corruption) below for an
+ * illustration of how). Therefore, such assignments are considered
+ * `@system`.
+ *
+ * An individual assignment can be `@trusted` if the caller can
+ * guarantee that there are no outstanding references to any `SumType`
+ * members that contain pointers or references at the time the
+ * assignment occurs.
+ *
+ * Examples:
+ *
+ * $(DIVID memory-corruption, $(H3 Memory corruption))
+ *
+ * This example shows how assignment to a `SumType` can be used to
+ * cause memory corruption in `@system` code. In `@safe` code, the
+ * assignment `s = 123` would not be allowed.
+ *
+ * ---
+ * SumType!(int*, int) s = new int;
+ * s.tryMatch!(
+ * (ref int* p) {
+ * s = 123; // overwrites `p`
+ * return *p; // undefined behavior
+ * }
+ * );
+ * ---
+ */
+ ref SumType opAssign(T rhs)
+ {
+ import core.lifetime : forward;
+ import std.traits : hasIndirections, hasNested;
+ import std.meta : AliasSeq, Or = templateOr;
+
+ alias OtherTypes =
+ AliasSeq!(Types[0 .. tid], Types[tid + 1 .. $]);
+ enum unsafeToOverwrite =
+ anySatisfy!(Or!(hasIndirections, hasNested), OtherTypes);
+
+ static if (unsafeToOverwrite)
+ {
+ cast(void) () @system {}();
+ }
+
+ this.match!destroyIfOwner;
+
+ static if (isCopyable!T)
+ {
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
+ mixin("Storage newStorage = { ",
+ Storage.memberName!T, ": __ctfe ? rhs : forward!rhs",
+ " };");
+ }
+ else
+ {
+ mixin("Storage newStorage = { ",
+ Storage.memberName!T, ": forward!rhs",
+ " };");
+ }
+
+ storage = newStorage;
+ tag = tid;
+
+ return this;
+ }
+ }
+ }
+
+ static if (allSatisfy!(isAssignableTo, Types))
+ {
+ static if (allSatisfy!(isCopyable, Types))
+ {
+ /**
+ * Copies the value from another `SumType` into this one.
+ *
+ * See the value-assignment overload for details on `@safe`ty.
+ *
+ * Copy assignment is `@disable`d if any of `Types` is non-copyable.
+ */
+ ref SumType opAssign(ref SumType rhs)
+ {
+ rhs.match!((ref value) { this = value; });
+ return this;
+ }
+ }
+ else
+ {
+ @disable ref SumType opAssign(ref SumType rhs);
+ }
+
+ /**
+ * Moves the value from another `SumType` into this one.
+ *
+ * See the value-assignment overload for details on `@safe`ty.
+ */
+ ref SumType opAssign(SumType rhs)
+ {
+ import core.lifetime : move;
+
+ rhs.match!((ref value) {
+ static if (isCopyable!(typeof(value)))
+ {
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
+ this = __ctfe ? value : move(value);
+ }
+ else
+ {
+ this = move(value);
+ }
+ });
+ return this;
+ }
+ }
+
+ /**
+ * Compares two `SumType`s for equality.
+ *
+ * Two `SumType`s are equal if they are the same kind of `SumType`, they
+ * contain values of the same type, and those values are equal.
+ */
+ bool opEquals(this This, Rhs)(auto ref Rhs rhs)
+ if (!is(CommonType!(This, Rhs) == void))
+ {
+ static if (is(This == Rhs))
+ {
+ return AliasSeq!(this, rhs).match!((ref value, ref rhsValue) {
+ static if (is(typeof(value) == typeof(rhsValue)))
+ {
+ return value == rhsValue;
+ }
+ else
+ {
+ return false;
+ }
+ });
+ }
+ else
+ {
+ alias CommonSumType = CommonType!(This, Rhs);
+ return cast(CommonSumType) this == cast(CommonSumType) rhs;
+ }
+ }
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=19407
+ static if (__traits(compiles, anySatisfy!(hasElaborateDestructor, Types)))
+ {
+ // If possible, include the destructor only when it's needed
+ private enum includeDtor = anySatisfy!(hasElaborateDestructor, Types);
+ }
+ else
+ {
+ // If we can't tell, always include it, even when it does nothing
+ private enum includeDtor = true;
+ }
+
+ static if (includeDtor)
+ {
+ /// Calls the destructor of the `SumType`'s current value.
+ ~this()
+ {
+ this.match!destroyIfOwner;
+ }
+ }
+
+ invariant
+ {
+ this.match!((ref value) {
+ static if (is(typeof(value) == class))
+ {
+ if (value !is null)
+ {
+ assert(value);
+ }
+ }
+ else static if (is(typeof(value) == struct))
+ {
+ assert(&value);
+ }
+ });
+ }
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21400
+ version (StdDdoc)
+ {
+ /**
+ * Returns a string representation of the `SumType`'s current value.
+ *
+ * Not available when compiled with `-betterC`.
+ */
+ string toString(this This)();
+
+ /**
+ * Handles formatted writing of the `SumType`'s current value.
+ *
+ * Not available when compiled with `-betterC`.
+ *
+ * Params:
+ * sink = Output range to write to.
+ * fmt = Format specifier to use.
+ *
+ * See_Also: $(REF formatValue, std,format)
+ */
+ void toString(this This, Sink, Char)(ref Sink sink, const ref FormatSpec!Char fmt);
+ }
+
+ version (D_BetterC) {} else
+ /**
+ * Returns a string representation of the `SumType`'s current value.
+ *
+ * Not available when compiled with `-betterC`.
+ */
+ string toString(this This)()
+ {
+ import std.conv : to;
+
+ return this.match!(to!string);
+ }
+
+ version (D_BetterC) {} else
+ /**
+ * Handles formatted writing of the `SumType`'s current value.
+ *
+ * Not available when compiled with `-betterC`.
+ *
+ * Params:
+ * sink = Output range to write to.
+ * fmt = Format specifier to use.
+ *
+ * See_Also: $(REF formatValue, std,format)
+ */
+ void toString(this This, Sink, Char)(ref Sink sink, const ref FormatSpec!Char fmt)
+ {
+ import std.format.write : formatValue;
+
+ this.match!((ref value) {
+ formatValue(sink, value, fmt);
+ });
+ }
+
+ static if (allSatisfy!(isHashable, Map!(ConstOf, Types)))
+ {
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=21400
+ version (StdDdoc)
+ {
+ /**
+ * Returns the hash of the `SumType`'s current value.
+ *
+ * Not available when compiled with `-betterC`.
+ */
+ size_t toHash() const;
+ }
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=20095
+ version (D_BetterC) {} else
+ /**
+ * Returns the hash of the `SumType`'s current value.
+ *
+ * Not available when compiled with `-betterC`.
+ */
+ size_t toHash() const
+ {
+ return this.match!hashOf;
+ }
+ }
+}
+
+// Construction
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+}
+
+// Assignment
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ x = 3.14;
+}
+
+// Self assignment
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+ y = x;
+}
+
+// Equality
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ assert(MySum(123) == MySum(123));
+ assert(MySum(123) != MySum(456));
+ assert(MySum(123) != MySum(123.0));
+ assert(MySum(123) != MySum(456.0));
+
+}
+
+// Equality of differently-qualified SumTypes
+// Disabled in BetterC due to use of dynamic arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ alias SumA = SumType!(int, float);
+ alias SumB = SumType!(const(int[]), int[]);
+ alias SumC = SumType!(int[], const(int[]));
+
+ int[] ma = [1, 2, 3];
+ const(int[]) ca = [1, 2, 3];
+
+ assert(const(SumA)(123) == SumA(123));
+ assert(const(SumB)(ma[]) == SumB(ca[]));
+ assert(const(SumC)(ma[]) == SumC(ca[]));
+}
+
+// Imported types
+@safe unittest
+{
+ import std.typecons : Tuple;
+
+ alias MySum = SumType!(Tuple!(int, int));
+}
+
+// const and immutable types
+@safe unittest
+{
+ alias MySum = SumType!(const(int[]), immutable(float[]));
+}
+
+// Recursive types
+@safe unittest
+{
+ alias MySum = SumType!(This*);
+ assert(is(MySum.Types[0] == MySum*));
+}
+
+// Allowed types
+@safe unittest
+{
+ import std.meta : AliasSeq;
+
+ alias MySum = SumType!(int, float, This*);
+
+ assert(is(MySum.Types == AliasSeq!(int, float, MySum*)));
+}
+
+// Types with destructors and postblits
+@system unittest
+{
+ int copies;
+
+ static struct Test
+ {
+ bool initialized = false;
+ int* copiesPtr;
+
+ this(this) { (*copiesPtr)++; }
+ ~this() { if (initialized) (*copiesPtr)--; }
+ }
+
+ alias MySum = SumType!(int, Test);
+
+ Test t = Test(true, &copies);
+
+ {
+ MySum x = t;
+ assert(copies == 1);
+ }
+ assert(copies == 0);
+
+ {
+ MySum x = 456;
+ assert(copies == 0);
+ }
+ assert(copies == 0);
+
+ {
+ MySum x = t;
+ assert(copies == 1);
+ x = 456;
+ assert(copies == 0);
+ }
+
+ {
+ MySum x = 456;
+ assert(copies == 0);
+ x = t;
+ assert(copies == 1);
+ }
+
+ {
+ MySum x = t;
+ MySum y = x;
+ assert(copies == 2);
+ }
+
+ {
+ MySum x = t;
+ MySum y;
+ y = x;
+ assert(copies == 2);
+ }
+}
+
+// Doesn't destroy reference types
+// Disabled in BetterC due to use of classes
+version (D_BetterC) {} else
+@system unittest
+{
+ bool destroyed;
+
+ class C
+ {
+ ~this()
+ {
+ destroyed = true;
+ }
+ }
+
+ struct S
+ {
+ ~this() {}
+ }
+
+ alias MySum = SumType!(S, C);
+
+ C c = new C();
+ {
+ MySum x = c;
+ destroyed = false;
+ }
+ assert(!destroyed);
+
+ {
+ MySum x = c;
+ destroyed = false;
+ x = S();
+ assert(!destroyed);
+ }
+}
+
+// Types with @disable this()
+@safe unittest
+{
+ static struct NoInit
+ {
+ @disable this();
+ }
+
+ alias MySum = SumType!(NoInit, int);
+
+ assert(!__traits(compiles, MySum()));
+ auto _ = MySum(42);
+}
+
+// const SumTypes
+version (D_BetterC) {} else // not @nogc, https://issues.dlang.org/show_bug.cgi?id=22117
+@safe unittest
+{
+ auto _ = const(SumType!(int[]))([1, 2, 3]);
+}
+
+// Equality of const SumTypes
+@safe unittest
+{
+ alias MySum = SumType!int;
+
+ auto _ = const(MySum)(123) == const(MySum)(456);
+}
+
+// Compares reference types using value equality
+@safe unittest
+{
+ import std.array : staticArray;
+
+ static struct Field {}
+ static struct Struct { Field[] fields; }
+ alias MySum = SumType!Struct;
+
+ static arr1 = staticArray([Field()]);
+ static arr2 = staticArray([Field()]);
+
+ auto a = MySum(Struct(arr1[]));
+ auto b = MySum(Struct(arr2[]));
+
+ assert(a == b);
+}
+
+// toString
+// Disabled in BetterC due to use of std.conv.text
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.conv : text;
+
+ static struct Int { int i; }
+ static struct Double { double d; }
+ alias Sum = SumType!(Int, Double);
+
+ assert(Sum(Int(42)).text == Int(42).text, Sum(Int(42)).text);
+ assert(Sum(Double(33.3)).text == Double(33.3).text, Sum(Double(33.3)).text);
+ assert((const(Sum)(Int(42))).text == (const(Int)(42)).text, (const(Sum)(Int(42))).text);
+}
+
+// string formatting
+// Disabled in BetterC due to use of std.format.format
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.format : format;
+
+ SumType!int x = 123;
+
+ assert(format!"%s"(x) == format!"%s"(123));
+ assert(format!"%x"(x) == format!"%x"(123));
+}
+
+// string formatting of qualified SumTypes
+// Disabled in BetterC due to use of std.format.format and dynamic arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.format : format;
+
+ int[] a = [1, 2, 3];
+ const(SumType!(int[])) x = a;
+
+ assert(format!"%(%d, %)"(x) == format!"%(%s, %)"(a));
+}
+
+// Github issue #16
+// Disabled in BetterC due to use of dynamic arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ alias Node = SumType!(This[], string);
+
+ // override inference of @system attribute for cyclic functions
+ assert((() @trusted =>
+ Node([Node([Node("x")])])
+ ==
+ Node([Node([Node("x")])])
+ )());
+}
+
+// Github issue #16 with const
+// Disabled in BetterC due to use of dynamic arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ alias Node = SumType!(const(This)[], string);
+
+ // override inference of @system attribute for cyclic functions
+ assert((() @trusted =>
+ Node([Node([Node("x")])])
+ ==
+ Node([Node([Node("x")])])
+ )());
+}
+
+// Stale pointers
+// Disabled in BetterC due to use of dynamic arrays
+version (D_BetterC) {} else
+@system unittest
+{
+ alias MySum = SumType!(ubyte, void*[2]);
+
+ MySum x = [null, cast(void*) 0x12345678];
+ void** p = &x.get!(void*[2])[1];
+ x = ubyte(123);
+
+ assert(*p != cast(void*) 0x12345678);
+}
+
+// Exception-safe assignment
+// Disabled in BetterC due to use of exceptions
+version (D_BetterC) {} else
+@safe unittest
+{
+ static struct A
+ {
+ int value = 123;
+ }
+
+ static struct B
+ {
+ int value = 456;
+ this(this) { throw new Exception("oops"); }
+ }
+
+ alias MySum = SumType!(A, B);
+
+ MySum x;
+ try
+ {
+ x = B();
+ }
+ catch (Exception e) {}
+
+ assert(
+ (x.tag == 0 && x.get!A.value == 123) ||
+ (x.tag == 1 && x.get!B.value == 456)
+ );
+}
+
+// Types with @disable this(this)
+@safe unittest
+{
+ import core.lifetime : move;
+
+ static struct NoCopy
+ {
+ @disable this(this);
+ }
+
+ alias MySum = SumType!NoCopy;
+
+ NoCopy lval = NoCopy();
+
+ MySum x = NoCopy();
+ MySum y = NoCopy();
+
+
+ assert(!__traits(compiles, SumType!NoCopy(lval)));
+
+ y = NoCopy();
+ y = move(x);
+ assert(!__traits(compiles, y = lval));
+ assert(!__traits(compiles, y = x));
+
+ bool b = x == y;
+}
+
+// Github issue #22
+// Disabled in BetterC due to use of std.typecons.Nullable
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.typecons;
+
+ static struct A
+ {
+ SumType!(Nullable!int) a = Nullable!int.init;
+ }
+}
+
+// Static arrays of structs with postblits
+// Disabled in BetterC due to use of dynamic arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ static struct S
+ {
+ int n;
+ this(this) { n++; }
+ }
+
+ SumType!(S[1]) x = [S(0)];
+ SumType!(S[1]) y = x;
+
+ auto xval = x.get!(S[1])[0].n;
+ auto yval = y.get!(S[1])[0].n;
+
+ assert(xval != yval);
+}
+
+// Replacement does not happen inside SumType
+// Disabled in BetterC due to use of associative arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.typecons : Tuple, ReplaceTypeUnless;
+ alias A = Tuple!(This*,SumType!(This*))[SumType!(This*,string)[This]];
+ alias TR = ReplaceTypeUnless!(isSumTypeInstance, This, int, A);
+ static assert(is(TR == Tuple!(int*,SumType!(This*))[SumType!(This*, string)[int]]));
+}
+
+// Supports nested self-referential SumTypes
+@safe unittest
+{
+ import std.typecons : Tuple, Flag;
+ alias Nat = SumType!(Flag!"0", Tuple!(This*));
+ alias Inner = SumType!Nat;
+ alias Outer = SumType!(Nat*, Tuple!(This*, This*));
+}
+
+// Self-referential SumTypes inside Algebraic
+// Disabled in BetterC due to use of std.variant.Algebraic
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.variant : Algebraic;
+
+ alias T = Algebraic!(SumType!(This*));
+
+ assert(is(T.AllowedTypes[0].Types[0] == T.AllowedTypes[0]*));
+}
+
+// Doesn't call @system postblits in @safe code
+@safe unittest
+{
+ static struct SystemCopy { @system this(this) {} }
+ SystemCopy original;
+
+ assert(!__traits(compiles, () @safe
+ {
+ SumType!SystemCopy copy = original;
+ }));
+
+ assert(!__traits(compiles, () @safe
+ {
+ SumType!SystemCopy copy; copy = original;
+ }));
+}
+
+// Doesn't overwrite pointers in @safe code
+@safe unittest
+{
+ alias MySum = SumType!(int*, int);
+
+ MySum x;
+
+ assert(!__traits(compiles, () @safe
+ {
+ x = 123;
+ }));
+
+ assert(!__traits(compiles, () @safe
+ {
+ x = MySum(123);
+ }));
+}
+
+// Types with invariants
+// Disabled in BetterC due to use of exceptions
+version (D_BetterC) {} else
+version (D_Invariants)
+@system unittest
+{
+ import std.exception : assertThrown;
+ import core.exception : AssertError;
+
+ struct S
+ {
+ int i;
+ invariant { assert(i >= 0); }
+ }
+
+ class C
+ {
+ int i;
+ invariant { assert(i >= 0); }
+ }
+
+ SumType!S x;
+ x.match!((ref v) { v.i = -1; });
+ assertThrown!AssertError(assert(&x));
+
+ SumType!C y = new C();
+ y.match!((ref v) { v.i = -1; });
+ assertThrown!AssertError(assert(&y));
+}
+
+// Calls value postblit on self-assignment
+@safe unittest
+{
+ static struct S
+ {
+ int n;
+ this(this) { n++; }
+ }
+
+ SumType!S x = S();
+ SumType!S y;
+ y = x;
+
+ auto xval = x.get!S.n;
+ auto yval = y.get!S.n;
+
+ assert(xval != yval);
+}
+
+// Github issue #29
+@safe unittest
+{
+ alias A = SumType!string;
+
+ @safe A createA(string arg)
+ {
+ return A(arg);
+ }
+
+ @safe void test()
+ {
+ A a = createA("");
+ }
+}
+
+// SumTypes as associative array keys
+// Disabled in BetterC due to use of associative arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ int[SumType!(int, string)] aa;
+}
+
+// toString with non-copyable types
+// Disabled in BetterC due to use of std.conv.to (in toString)
+version (D_BetterC) {} else
+@safe unittest
+{
+ struct NoCopy
+ {
+ @disable this(this);
+ }
+
+ SumType!NoCopy x;
+
+ auto _ = x.toString();
+}
+
+// Can use the result of assignment
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum a = MySum(123);
+ MySum b = MySum(3.14);
+
+ assert((a = b) == b);
+ assert((a = MySum(123)) == MySum(123));
+ assert((a = 3.14) == MySum(3.14));
+ assert(((a = b) = MySum(123)) == MySum(123));
+}
+
+// Types with copy constructors
+@safe unittest
+{
+ static struct S
+ {
+ int n;
+
+ this(ref return scope inout S other) inout
+ {
+ n = other.n + 1;
+ }
+ }
+
+ SumType!S x = S();
+ SumType!S y = x;
+
+ auto xval = x.get!S.n;
+ auto yval = y.get!S.n;
+
+ assert(xval != yval);
+}
+
+// Copyable by generated copy constructors
+@safe unittest
+{
+ static struct Inner
+ {
+ ref this(ref inout Inner other) {}
+ }
+
+ static struct Outer
+ {
+ SumType!Inner inner;
+ }
+
+ Outer x;
+ Outer y = x;
+}
+
+// Types with qualified copy constructors
+@safe unittest
+{
+ static struct ConstCopy
+ {
+ int n;
+ this(inout int n) inout { this.n = n; }
+ this(ref const typeof(this) other) const { this.n = other.n; }
+ }
+
+ static struct ImmutableCopy
+ {
+ int n;
+ this(inout int n) inout { this.n = n; }
+ this(ref immutable typeof(this) other) immutable { this.n = other.n; }
+ }
+
+ const SumType!ConstCopy x = const(ConstCopy)(1);
+ immutable SumType!ImmutableCopy y = immutable(ImmutableCopy)(1);
+}
+
+// Types with disabled opEquals
+@safe unittest
+{
+ static struct S
+ {
+ @disable bool opEquals(const S rhs) const;
+ }
+
+ auto _ = SumType!S(S());
+}
+
+// Types with non-const opEquals
+@safe unittest
+{
+ static struct S
+ {
+ int i;
+ bool opEquals(S rhs) { return i == rhs.i; }
+ }
+
+ auto _ = SumType!S(S(123));
+}
+
+// Incomparability of different SumTypes
+@safe unittest
+{
+ SumType!(int, string) x = 123;
+ SumType!(string, int) y = 123;
+
+ assert(!__traits(compiles, x != y));
+}
+
+// Self-reference in return/parameter type of function pointer member
+// Disabled in BetterC due to use of delegates
+version (D_BetterC) {} else
+@safe unittest
+{
+ alias T = SumType!(int, This delegate(This));
+}
+
+// Construction and assignment from implicitly-convertible lvalue
+@safe unittest
+{
+ alias MySum = SumType!bool;
+
+ const(bool) b = true;
+
+ MySum x = b;
+ MySum y; y = b;
+}
+
+// @safe assignment to the only pointer type in a SumType
+@safe unittest
+{
+ SumType!(string, int) sm = 123;
+ sm = "this should be @safe";
+}
+
+// Immutable member type with copy constructor
+// https://issues.dlang.org/show_bug.cgi?id=22572
+@safe unittest
+{
+ static struct CopyConstruct
+ {
+ this(ref inout CopyConstruct other) inout {}
+ }
+
+ static immutable struct Value
+ {
+ CopyConstruct c;
+ }
+
+ SumType!Value s;
+}
+
+// Construction of inout-qualified SumTypes
+// https://issues.dlang.org/show_bug.cgi?id=22901
+@safe unittest
+{
+ static inout(SumType!(int[])) example(inout(int[]) arr)
+ {
+ return inout(SumType!(int[]))(arr);
+ }
+}
+
+// Assignment of struct with overloaded opAssign in CTFE
+// https://issues.dlang.org/show_bug.cgi?id=23182
+@safe unittest
+{
+ static struct HasOpAssign
+ {
+ void opAssign(HasOpAssign rhs) {}
+ }
+
+ static SumType!HasOpAssign test()
+ {
+ SumType!HasOpAssign s;
+ // Test both overloads
+ s = HasOpAssign();
+ s = SumType!HasOpAssign();
+ return s;
+ }
+
+ // Force CTFE
+ enum result = test();
+}
+
+/// True if `T` is an instance of the `SumType` template, otherwise false.
+private enum bool isSumTypeInstance(T) = is(T == SumType!Args, Args...);
+
+@safe unittest
+{
+ static struct Wrapper
+ {
+ SumType!int s;
+ alias s this;
+ }
+
+ assert(isSumTypeInstance!(SumType!int));
+ assert(!isSumTypeInstance!Wrapper);
+}
+
+/// True if `T` is a [SumType] or implicitly converts to one, otherwise false.
+enum bool isSumType(T) = is(T : SumType!Args, Args...);
+
+///
+@safe unittest
+{
+ static struct ConvertsToSumType
+ {
+ SumType!int payload;
+ alias payload this;
+ }
+
+ static struct ContainsSumType
+ {
+ SumType!int payload;
+ }
+
+ assert(isSumType!(SumType!int));
+ assert(isSumType!ConvertsToSumType);
+ assert(!isSumType!ContainsSumType);
+}
+
+/**
+ * Calls a type-appropriate function with the value held in a [SumType].
+ *
+ * For each possible type the [SumType] can hold, the given handlers are
+ * checked, in order, to see whether they accept a single argument of that type.
+ * The first one that does is chosen as the match for that type. (Note that the
+ * first match may not always be the most exact match.
+ * See ["Avoiding unintentional matches"](#avoiding-unintentional-matches) for
+ * one common pitfall.)
+ *
+ * Every type must have a matching handler, and every handler must match at
+ * least one type. This is enforced at compile time.
+ *
+ * Handlers may be functions, delegates, or objects with `opCall` overloads. If
+ * a function with more than one overload is given as a handler, all of the
+ * overloads are considered as potential matches.
+ *
+ * Templated handlers are also accepted, and will match any type for which they
+ * can be [implicitly instantiated](https://dlang.org/glossary.html#ifti). See
+ * ["Introspection-based matching"](#introspection-based-matching) for an
+ * example of templated handler usage.
+ *
+ * If multiple [SumType]s are passed to match, their values are passed to the
+ * handlers as separate arguments, and matching is done for each possible
+ * combination of value types. See ["Multiple dispatch"](#multiple-dispatch) for
+ * an example.
+ *
+ * Returns:
+ * The value returned from the handler that matches the currently-held type.
+ *
+ * See_Also: $(REF visit, std,variant)
+ */
+template match(handlers...)
+{
+ import std.typecons : Yes;
+
+ /**
+ * The actual `match` function.
+ *
+ * Params:
+ * args = One or more [SumType] objects.
+ */
+ auto ref match(SumTypes...)(auto ref SumTypes args)
+ if (allSatisfy!(isSumType, SumTypes) && args.length > 0)
+ {
+ return matchImpl!(Yes.exhaustive, handlers)(args);
+ }
+}
+
+/** $(DIVID avoiding-unintentional-matches, $(H3 Avoiding unintentional matches))
+ *
+ * Sometimes, implicit conversions may cause a handler to match more types than
+ * intended. The example below shows two solutions to this problem.
+ */
+@safe unittest
+{
+ alias Number = SumType!(double, int);
+
+ Number x;
+
+ // Problem: because int implicitly converts to double, the double
+ // handler is used for both types, and the int handler never matches.
+ assert(!__traits(compiles,
+ x.match!(
+ (double d) => "got double",
+ (int n) => "got int"
+ )
+ ));
+
+ // Solution 1: put the handler for the "more specialized" type (in this
+ // case, int) before the handler for the type it converts to.
+ assert(__traits(compiles,
+ x.match!(
+ (int n) => "got int",
+ (double d) => "got double"
+ )
+ ));
+
+ // Solution 2: use a template that only accepts the exact type it's
+ // supposed to match, instead of any type that implicitly converts to it.
+ alias exactly(T, alias fun) = function (arg)
+ {
+ static assert(is(typeof(arg) == T));
+ return fun(arg);
+ };
+
+ // Now, even if we put the double handler first, it will only be used for
+ // doubles, not ints.
+ assert(__traits(compiles,
+ x.match!(
+ exactly!(double, d => "got double"),
+ exactly!(int, n => "got int")
+ )
+ ));
+}
+
+/** $(DIVID multiple-dispatch, $(H3 Multiple dispatch))
+ *
+ * Pattern matching can be performed on multiple `SumType`s at once by passing
+ * handlers with multiple arguments. This usually leads to more concise code
+ * than using nested calls to `match`, as show below.
+ */
+@safe unittest
+{
+ struct Point2D { double x, y; }
+ struct Point3D { double x, y, z; }
+
+ alias Point = SumType!(Point2D, Point3D);
+
+ version (none)
+ {
+ // This function works, but the code is ugly and repetitive.
+ // It uses three separate calls to match!
+ @safe pure nothrow @nogc
+ bool sameDimensions(Point p1, Point p2)
+ {
+ return p1.match!(
+ (Point2D _) => p2.match!(
+ (Point2D _) => true,
+ _ => false
+ ),
+ (Point3D _) => p2.match!(
+ (Point3D _) => true,
+ _ => false
+ )
+ );
+ }
+ }
+
+ // This version is much nicer.
+ @safe pure nothrow @nogc
+ bool sameDimensions(Point p1, Point p2)
+ {
+ alias doMatch = match!(
+ (Point2D _1, Point2D _2) => true,
+ (Point3D _1, Point3D _2) => true,
+ (_1, _2) => false
+ );
+
+ return doMatch(p1, p2);
+ }
+
+ Point a = Point2D(1, 2);
+ Point b = Point2D(3, 4);
+ Point c = Point3D(5, 6, 7);
+ Point d = Point3D(8, 9, 0);
+
+ assert( sameDimensions(a, b));
+ assert( sameDimensions(c, d));
+ assert(!sameDimensions(a, c));
+ assert(!sameDimensions(d, b));
+}
+
+/**
+ * Attempts to call a type-appropriate function with the value held in a
+ * [SumType], and throws on failure.
+ *
+ * Matches are chosen using the same rules as [match], but are not required to
+ * be exhaustive—in other words, a type (or combination of types) is allowed to
+ * have no matching handler. If a type without a handler is encountered at
+ * runtime, a [MatchException] is thrown.
+ *
+ * Not available when compiled with `-betterC`.
+ *
+ * Returns:
+ * The value returned from the handler that matches the currently-held type,
+ * if a handler was given for that type.
+ *
+ * Throws:
+ * [MatchException], if the currently-held type has no matching handler.
+ *
+ * See_Also: $(REF tryVisit, std,variant)
+ */
+version (D_Exceptions)
+template tryMatch(handlers...)
+{
+ import std.typecons : No;
+
+ /**
+ * The actual `tryMatch` function.
+ *
+ * Params:
+ * args = One or more [SumType] objects.
+ */
+ auto ref tryMatch(SumTypes...)(auto ref SumTypes args)
+ if (allSatisfy!(isSumType, SumTypes) && args.length > 0)
+ {
+ return matchImpl!(No.exhaustive, handlers)(args);
+ }
+}
+
+/**
+ * Thrown by [tryMatch] when an unhandled type is encountered.
+ *
+ * Not available when compiled with `-betterC`.
+ */
+version (D_Exceptions)
+class MatchException : Exception
+{
+ ///
+ pure @safe @nogc nothrow
+ this(string msg, string file = __FILE__, size_t line = __LINE__)
+ {
+ super(msg, file, line);
+ }
+}
+
+/**
+ * True if `handler` is a potential match for `Ts`, otherwise false.
+ *
+ * See the documentation for [match] for a full explanation of how matches are
+ * chosen.
+ */
+template canMatch(alias handler, Ts...)
+if (Ts.length > 0)
+{
+ enum canMatch = is(typeof((ref Ts args) => handler(args)));
+}
+
+///
+@safe unittest
+{
+ alias handleInt = (int i) => "got an int";
+
+ assert( canMatch!(handleInt, int));
+ assert(!canMatch!(handleInt, string));
+}
+
+// Includes all overloads of the given handler
+@safe unittest
+{
+ static struct OverloadSet
+ {
+ static void fun(int n) {}
+ static void fun(double d) {}
+ }
+
+ assert(canMatch!(OverloadSet.fun, int));
+ assert(canMatch!(OverloadSet.fun, double));
+}
+
+// Like aliasSeqOf!(iota(n)), but works in BetterC
+private template Iota(size_t n)
+{
+ static if (n == 0)
+ {
+ alias Iota = AliasSeq!();
+ }
+ else
+ {
+ alias Iota = AliasSeq!(Iota!(n - 1), n - 1);
+ }
+}
+
+@safe unittest
+{
+ assert(is(Iota!0 == AliasSeq!()));
+ assert(Iota!1 == AliasSeq!(0));
+ assert(Iota!3 == AliasSeq!(0, 1, 2));
+}
+
+/* The number that the dim-th argument's tag is multiplied by when
+ * converting TagTuples to and from case indices ("caseIds").
+ *
+ * Named by analogy to the stride that the dim-th index into a
+ * multidimensional static array is multiplied by to calculate the
+ * offset of a specific element.
+ */
+private size_t stride(size_t dim, lengths...)()
+{
+ import core.checkedint : mulu;
+
+ size_t result = 1;
+ bool overflow = false;
+
+ static foreach (i; 0 .. dim)
+ {
+ result = mulu(result, lengths[i], overflow);
+ }
+
+ /* The largest number matchImpl uses, numCases, is calculated with
+ * stride!(SumTypes.length), so as long as this overflow check
+ * passes, we don't need to check for overflow anywhere else.
+ */
+ assert(!overflow, "Integer overflow");
+ return result;
+}
+
+private template matchImpl(Flag!"exhaustive" exhaustive, handlers...)
+{
+ auto ref matchImpl(SumTypes...)(auto ref SumTypes args)
+ if (allSatisfy!(isSumType, SumTypes) && args.length > 0)
+ {
+ alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes));
+ alias TagTuple = .TagTuple!(SumTypes);
+
+ /*
+ * A list of arguments to be passed to a handler needed for the case
+ * labeled with `caseId`.
+ */
+ template handlerArgs(size_t caseId)
+ {
+ enum tags = TagTuple.fromCaseId(caseId);
+ enum argsFrom(size_t i : tags.length) = "";
+ enum argsFrom(size_t i) = "args[" ~ toCtString!i ~ "].get!(SumTypes[" ~ toCtString!i ~ "]" ~
+ ".Types[" ~ toCtString!(tags[i]) ~ "])(), " ~ argsFrom!(i + 1);
+ enum handlerArgs = argsFrom!0;
+ }
+
+ /* An AliasSeq of the types of the member values in the argument list
+ * returned by `handlerArgs!caseId`.
+ *
+ * Note that these are the actual (that is, qualified) types of the
+ * member values, which may not be the same as the types listed in
+ * the arguments' `.Types` properties.
+ */
+ template valueTypes(size_t caseId)
+ {
+ enum tags = TagTuple.fromCaseId(caseId);
+
+ template getType(size_t i)
+ {
+ enum tid = tags[i];
+ alias T = SumTypes[i].Types[tid];
+ alias getType = typeof(args[i].get!T());
+ }
+
+ alias valueTypes = Map!(getType, Iota!(tags.length));
+ }
+
+ /* The total number of cases is
+ *
+ * Π SumTypes[i].Types.length for 0 ≤ i < SumTypes.length
+ *
+ * Or, equivalently,
+ *
+ * ubyte[SumTypes[0].Types.length]...[SumTypes[$-1].Types.length].sizeof
+ *
+ * Conveniently, this is equal to stride!(SumTypes.length), so we can
+ * use that function to compute it.
+ */
+ enum numCases = stride!(SumTypes.length);
+
+ /* Guaranteed to never be a valid handler index, since
+ * handlers.length <= size_t.max.
+ */
+ enum noMatch = size_t.max;
+
+ // An array that maps caseIds to handler indices ("hids").
+ enum matches = ()
+ {
+ size_t[numCases] matches;
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=19561
+ foreach (ref match; matches)
+ {
+ match = noMatch;
+ }
+
+ static foreach (caseId; 0 .. numCases)
+ {
+ static foreach (hid, handler; handlers)
+ {
+ static if (canMatch!(handler, valueTypes!caseId))
+ {
+ if (matches[caseId] == noMatch)
+ {
+ matches[caseId] = hid;
+ }
+ }
+ }
+ }
+
+ return matches;
+ }();
+
+ import std.algorithm.searching : canFind;
+
+ // Check for unreachable handlers
+ static foreach (hid, handler; handlers)
+ {
+ static assert(matches[].canFind(hid),
+ "`handlers[" ~ toCtString!hid ~ "]` " ~
+ "of type `" ~ ( __traits(isTemplate, handler)
+ ? "template"
+ : typeof(handler).stringof
+ ) ~ "` " ~
+ "never matches"
+ );
+ }
+
+ // Workaround for https://issues.dlang.org/show_bug.cgi?id=19993
+ enum handlerName(size_t hid) = "handler" ~ toCtString!hid;
+
+ static foreach (size_t hid, handler; handlers)
+ {
+ mixin("alias ", handlerName!hid, " = handler;");
+ }
+
+ immutable argsId = TagTuple(args).toCaseId;
+
+ final switch (argsId)
+ {
+ static foreach (caseId; 0 .. numCases)
+ {
+ case caseId:
+ static if (matches[caseId] != noMatch)
+ {
+ return mixin(handlerName!(matches[caseId]), "(", handlerArgs!caseId, ")");
+ }
+ else
+ {
+ static if (exhaustive)
+ {
+ static assert(false,
+ "No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`");
+ }
+ else
+ {
+ throw new MatchException(
+ "No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`");
+ }
+ }
+ }
+ }
+
+ assert(false, "unreachable");
+ }
+}
+
+private enum typeCount(SumType) = SumType.Types.length;
+
+/* A TagTuple represents a single possible set of tags that `args`
+ * could have at runtime.
+ *
+ * Because D does not allow a struct to be the controlling expression
+ * of a switch statement, we cannot dispatch on the TagTuple directly.
+ * Instead, we must map each TagTuple to a unique integer and generate
+ * a case label for each of those integers.
+ *
+ * This mapping is implemented in `fromCaseId` and `toCaseId`. It uses
+ * the same technique that's used to map index tuples to memory offsets
+ * in a multidimensional static array.
+ *
+ * For example, when `args` consists of two SumTypes with two member
+ * types each, the TagTuples corresponding to each case label are:
+ *
+ * case 0: TagTuple([0, 0])
+ * case 1: TagTuple([1, 0])
+ * case 2: TagTuple([0, 1])
+ * case 3: TagTuple([1, 1])
+ *
+ * When there is only one argument, the caseId is equal to that
+ * argument's tag.
+ */
+private struct TagTuple(SumTypes...)
+{
+ size_t[SumTypes.length] tags;
+ alias tags this;
+
+ alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes));
+
+ invariant
+ {
+ static foreach (i; 0 .. tags.length)
+ {
+ assert(tags[i] < SumTypes[i].Types.length, "Invalid tag");
+ }
+ }
+
+ this(ref const(SumTypes) args)
+ {
+ static foreach (i; 0 .. tags.length)
+ {
+ tags[i] = args[i].tag;
+ }
+ }
+
+ static TagTuple fromCaseId(size_t caseId)
+ {
+ TagTuple result;
+
+ // Most-significant to least-significant
+ static foreach_reverse (i; 0 .. result.length)
+ {
+ result[i] = caseId / stride!i;
+ caseId %= stride!i;
+ }
+
+ return result;
+ }
+
+ size_t toCaseId()
+ {
+ size_t result;
+
+ static foreach (i; 0 .. tags.length)
+ {
+ result += tags[i] * stride!i;
+ }
+
+ return result;
+ }
+}
+
+// Matching
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+
+ assert(x.match!((int v) => true, (float v) => false));
+ assert(y.match!((int v) => false, (float v) => true));
+}
+
+// Missing handlers
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+
+ assert(!__traits(compiles, x.match!((int x) => true)));
+ assert(!__traits(compiles, x.match!()));
+}
+
+// Handlers with qualified parameters
+// Disabled in BetterC due to use of dynamic arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ alias MySum = SumType!(int[], float[]);
+
+ MySum x = MySum([1, 2, 3]);
+ MySum y = MySum([1.0, 2.0, 3.0]);
+
+ assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false));
+ assert(y.match!((const(int[]) v) => false, (const(float[]) v) => true));
+}
+
+// Handlers for qualified types
+// Disabled in BetterC due to use of dynamic arrays
+version (D_BetterC) {} else
+@safe unittest
+{
+ alias MySum = SumType!(immutable(int[]), immutable(float[]));
+
+ MySum x = MySum([1, 2, 3]);
+
+ assert(x.match!((immutable(int[]) v) => true, (immutable(float[]) v) => false));
+ assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false));
+ // Tail-qualified parameters
+ assert(x.match!((immutable(int)[] v) => true, (immutable(float)[] v) => false));
+ assert(x.match!((const(int)[] v) => true, (const(float)[] v) => false));
+ // Generic parameters
+ assert(x.match!((immutable v) => true));
+ assert(x.match!((const v) => true));
+ // Unqualified parameters
+ assert(!__traits(compiles,
+ x.match!((int[] v) => true, (float[] v) => false)
+ ));
+}
+
+// Delegate handlers
+// Disabled in BetterC due to use of closures
+version (D_BetterC) {} else
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ int answer = 42;
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+
+ assert(x.match!((int v) => v == answer, (float v) => v == answer));
+ assert(!y.match!((int v) => v == answer, (float v) => v == answer));
+}
+
+version (unittest)
+{
+ version (D_BetterC)
+ {
+ // std.math.isClose depends on core.runtime.math, so use a
+ // libc-based version for testing with -betterC
+ @safe pure @nogc nothrow
+ private bool isClose(double lhs, double rhs)
+ {
+ import core.stdc.math : fabs;
+
+ return fabs(lhs - rhs) < 1e-5;
+ }
+ }
+ else
+ {
+ import std.math.operations : isClose;
+ }
+}
+
+// Generic handler
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+
+ assert(x.match!(v => v*2) == 84);
+ assert(y.match!(v => v*2).isClose(6.28));
+}
+
+// Fallback to generic handler
+// Disabled in BetterC due to use of std.conv.to
+version (D_BetterC) {} else
+@safe unittest
+{
+ import std.conv : to;
+
+ alias MySum = SumType!(int, float, string);
+
+ MySum x = MySum(42);
+ MySum y = MySum("42");
+
+ assert(x.match!((string v) => v.to!int, v => v*2) == 84);
+ assert(y.match!((string v) => v.to!int, v => v*2) == 42);
+}
+
+// Multiple non-overlapping generic handlers
+@safe unittest
+{
+ import std.array : staticArray;
+
+ alias MySum = SumType!(int, float, int[], char[]);
+
+ static ints = staticArray([1, 2, 3]);
+ static chars = staticArray(['a', 'b', 'c']);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+ MySum z = MySum(ints[]);
+ MySum w = MySum(chars[]);
+
+ assert(x.match!(v => v*2, v => v.length) == 84);
+ assert(y.match!(v => v*2, v => v.length).isClose(6.28));
+ assert(w.match!(v => v*2, v => v.length) == 3);
+ assert(z.match!(v => v*2, v => v.length) == 3);
+}
+
+// Structural matching
+@safe unittest
+{
+ static struct S1 { int x; }
+ static struct S2 { int y; }
+ alias MySum = SumType!(S1, S2);
+
+ MySum a = MySum(S1(0));
+ MySum b = MySum(S2(0));
+
+ assert(a.match!(s1 => s1.x + 1, s2 => s2.y - 1) == 1);
+ assert(b.match!(s1 => s1.x + 1, s2 => s2.y - 1) == -1);
+}
+
+// Separate opCall handlers
+@safe unittest
+{
+ static struct IntHandler
+ {
+ bool opCall(int arg)
+ {
+ return true;
+ }
+ }
+
+ static struct FloatHandler
+ {
+ bool opCall(float arg)
+ {
+ return false;
+ }
+ }
+
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+
+ assert(x.match!(IntHandler.init, FloatHandler.init));
+ assert(!y.match!(IntHandler.init, FloatHandler.init));
+}
+
+// Compound opCall handler
+@safe unittest
+{
+ static struct CompoundHandler
+ {
+ bool opCall(int arg)
+ {
+ return true;
+ }
+
+ bool opCall(float arg)
+ {
+ return false;
+ }
+ }
+
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+
+ assert(x.match!(CompoundHandler.init));
+ assert(!y.match!(CompoundHandler.init));
+}
+
+// Ordered matching
+@safe unittest
+{
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+
+ assert(x.match!((int v) => true, v => false));
+}
+
+// Non-exhaustive matching
+version (D_Exceptions)
+@system unittest
+{
+ import std.exception : assertThrown, assertNotThrown;
+
+ alias MySum = SumType!(int, float);
+
+ MySum x = MySum(42);
+ MySum y = MySum(3.14);
+
+ assertNotThrown!MatchException(x.tryMatch!((int n) => true));
+ assertThrown!MatchException(y.tryMatch!((int n) => true));
+}
+
+// Non-exhaustive matching in @safe code
+version (D_Exceptions)
+@safe unittest
+{
+ SumType!(int, float) x;
+
+ auto _ = x.tryMatch!(
+ (int n) => n + 1,
+ );
+}
+
+// Handlers with ref parameters
+@safe unittest
+{
+ alias Value = SumType!(long, double);
+
+ auto value = Value(3.14);
+
+ value.match!(
+ (long) {},
+ (ref double d) { d *= 2; }
+ );
+
+ assert(value.get!double.isClose(6.28));
+}
+
+// Unreachable handlers
+@safe unittest
+{
+ alias MySum = SumType!(int, string);
+
+ MySum s;
+
+ assert(!__traits(compiles,
+ s.match!(
+ (int _) => 0,
+ (string _) => 1,
+ (double _) => 2
+ )
+ ));
+
+ assert(!__traits(compiles,
+ s.match!(
+ _ => 0,
+ (int _) => 1
+ )
+ ));
+}
+
+// Unsafe handlers
+@system unittest
+{
+ SumType!int x;
+ alias unsafeHandler = (int x) @system { return; };
+
+ assert(!__traits(compiles, () @safe
+ {
+ x.match!unsafeHandler;
+ }));
+
+ auto test() @system
+ {
+ return x.match!unsafeHandler;
+ }
+}
+
+// Overloaded handlers
+@safe unittest
+{
+ static struct OverloadSet
+ {
+ static string fun(int i) { return "int"; }
+ static string fun(double d) { return "double"; }
+ }
+
+ alias MySum = SumType!(int, double);
+
+ MySum a = 42;
+ MySum b = 3.14;
+
+ assert(a.match!(OverloadSet.fun) == "int");
+ assert(b.match!(OverloadSet.fun) == "double");
+}
+
+// Overload sets that include SumType arguments
+@safe unittest
+{
+ alias Inner = SumType!(int, double);
+ alias Outer = SumType!(Inner, string);
+
+ static struct OverloadSet
+ {
+ @safe:
+ static string fun(int i) { return "int"; }
+ static string fun(double d) { return "double"; }
+ static string fun(string s) { return "string"; }
+ static string fun(Inner i) { return i.match!fun; }
+ static string fun(Outer o) { return o.match!fun; }
+ }
+
+ Outer a = Inner(42);
+ Outer b = Inner(3.14);
+ Outer c = "foo";
+
+ assert(OverloadSet.fun(a) == "int");
+ assert(OverloadSet.fun(b) == "double");
+ assert(OverloadSet.fun(c) == "string");
+}
+
+// Overload sets with ref arguments
+@safe unittest
+{
+ static struct OverloadSet
+ {
+ static void fun(ref int i) { i = 42; }
+ static void fun(ref double d) { d = 3.14; }
+ }
+
+ alias MySum = SumType!(int, double);
+
+ MySum x = 0;
+ MySum y = 0.0;
+
+ x.match!(OverloadSet.fun);
+ y.match!(OverloadSet.fun);
+
+ assert(x.match!((value) => is(typeof(value) == int) && value == 42));
+ assert(y.match!((value) => is(typeof(value) == double) && value == 3.14));
+}
+
+// Overload sets with templates
+@safe unittest
+{
+ import std.traits : isNumeric;
+
+ static struct OverloadSet
+ {
+ static string fun(string arg)
+ {
+ return "string";
+ }
+
+ static string fun(T)(T arg)
+ if (isNumeric!T)
+ {
+ return "numeric";
+ }
+ }
+
+ alias MySum = SumType!(int, string);
+
+ MySum x = 123;
+ MySum y = "hello";
+
+ assert(x.match!(OverloadSet.fun) == "numeric");
+ assert(y.match!(OverloadSet.fun) == "string");
+}
+
+// Github issue #24
+@safe unittest
+{
+ void test() @nogc
+ {
+ int acc = 0;
+ SumType!int(1).match!((int x) => acc += x);
+ }
+}
+
+// Github issue #31
+@safe unittest
+{
+ void test() @nogc
+ {
+ int acc = 0;
+
+ SumType!(int, string)(1).match!(
+ (int x) => acc += x,
+ (string _) => 0,
+ );
+ }
+}
+
+// Types that `alias this` a SumType
+@safe unittest
+{
+ static struct A {}
+ static struct B {}
+ static struct D { SumType!(A, B) value; alias value this; }
+
+ auto _ = D().match!(_ => true);
+}
+
+// Multiple dispatch
+@safe unittest
+{
+ alias MySum = SumType!(int, string);
+
+ static int fun(MySum x, MySum y)
+ {
+ import std.meta : Args = AliasSeq;
+
+ return Args!(x, y).match!(
+ (int xv, int yv) => 0,
+ (string xv, int yv) => 1,
+ (int xv, string yv) => 2,
+ (string xv, string yv) => 3
+ );
+ }
+
+ assert(fun(MySum(0), MySum(0)) == 0);
+ assert(fun(MySum(""), MySum(0)) == 1);
+ assert(fun(MySum(0), MySum("")) == 2);
+ assert(fun(MySum(""), MySum("")) == 3);
+}
+
+// inout SumTypes
+@safe unittest
+{
+ inout(int[]) fun(inout(SumType!(int[])) x)
+ {
+ return x.match!((inout(int[]) a) => a);
+ }
+}
+
+private void destroyIfOwner(T)(ref T value)
+{
+ static if (hasElaborateDestructor!T)
+ {
+ destroy(value);
+ }
+}