diff options
Diffstat (limited to 'libphobos/src/std/variant.d')
-rw-r--r-- | libphobos/src/std/variant.d | 973 |
1 files changed, 704 insertions, 269 deletions
diff --git a/libphobos/src/std/variant.d b/libphobos/src/std/variant.d index 574e2c5a375..953d6eafaee 100644 --- a/libphobos/src/std/variant.d +++ b/libphobos/src/std/variant.d @@ -2,7 +2,7 @@ /** This module implements a -$(HTTP erdani.org/publications/cuj-04-2002.html,discriminated union) +$(HTTP erdani.org/publications/cuj-04-2002.php.html,discriminated union) type (a.k.a. $(HTTP en.wikipedia.org/wiki/Tagged_union,tagged union), $(HTTP en.wikipedia.org/wiki/Algebraic_data_type,algebraic type)). @@ -13,8 +13,8 @@ languages, and comfortable exploratory programming. A $(LREF Variant) object can hold a value of any type, with very few restrictions (such as `shared` types and noncopyable types). Setting the value is as immediate as assigning to the `Variant` object. To read back the value of -the appropriate type `T`, use the $(LREF get!T) call. To query whether a -`Variant` currently holds a value of type `T`, use $(LREF peek!T). To fetch the +the appropriate type `T`, use the $(LREF get) method. To query whether a +`Variant` currently holds a value of type `T`, use $(LREF peek). To fetch the exact type currently held, call $(LREF type), which returns the `TypeInfo` of the current value. @@ -23,13 +23,16 @@ type constructor. Unlike `Variant`, `Algebraic` only allows a finite set of types, which are specified in the instantiation (e.g. $(D Algebraic!(int, string)) may only hold an `int` or a `string`). +$(RED Warning: $(LREF Algebraic) is outdated and not recommended for use in new +code. Instead, use $(REF SumType, std,sumtype).) + Credits: Reviewed by Brad Roberts. Daniel Keep provided a detailed code review prompting the following improvements: (1) better support for arrays; (2) support for associative arrays; (3) friendlier behavior towards the garbage collector. Copyright: Copyright Andrei Alexandrescu 2007 - 2015. License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0). Authors: $(HTTP erdani.org, Andrei Alexandrescu) -Source: $(PHOBOSSRC std/_variant.d) +Source: $(PHOBOSSRC std/variant.d) */ module std.variant; @@ -73,59 +76,98 @@ import std.meta, std.traits, std.typecons; } /++ - Gives the $(D sizeof) the largest type given. + Gives the `sizeof` the largest type given. + + See_Also: https://forum.dlang.org/thread/wbpnncxepehgcswhuazl@forum.dlang.org?page=1 +/ -template maxSize(T...) +template maxSize(Ts...) { - static if (T.length == 1) - { - enum size_t maxSize = T[0].sizeof; - } - else + align(1) union Impl { - import std.algorithm.comparison : max; - enum size_t maxSize = max(T[0].sizeof, maxSize!(T[1 .. $])); + static foreach (i, T; Ts) + { + static if (!is(T == void)) + mixin("T _field_", i, ";"); + } } + enum maxSize = Impl.sizeof; } /// @safe unittest { + struct Cat { int a, b, c; } + + align(1) struct S + { + long l; + ubyte b; + } + + align(1) struct T + { + ubyte b; + long l; + } + static assert(maxSize!(int, long) == 8); static assert(maxSize!(bool, byte) == 1); - - struct Cat { int a, b, c; } static assert(maxSize!(bool, Cat) == 12); + static assert(maxSize!(char) == 1); + static assert(maxSize!(char, short, ubyte) == 2); + static assert(maxSize!(char, long, ubyte) == 8); + import std.algorithm.comparison : max; + static assert(maxSize!(long, S) == max(long.sizeof, S.sizeof)); + static assert(maxSize!(S, T) == max(S.sizeof, T.sizeof)); + static assert(maxSize!(int, ubyte[7]) == 7); + static assert(maxSize!(int, ubyte[3]) == 4); + static assert(maxSize!(int, int, ubyte[3]) == 4); + static assert(maxSize!(void, int, ubyte[3]) == 4); + static assert(maxSize!(void) == 1); } struct This; -private alias This2Variant(V, T...) = AliasSeq!(ReplaceType!(This, V, T)); +private alias This2Variant(V, T...) = AliasSeq!(ReplaceTypeUnless!(isAlgebraic, This, V, T)); + +// We can't just use maxAlignment because no types might be specified +// to VariantN, so handle that here and then pass along the rest. +private template maxVariantAlignment(U...) +if (isTypeTuple!U) +{ + static if (U.length == 0) + { + import std.algorithm.comparison : max; + enum maxVariantAlignment = max(real.alignof, size_t.alignof); + } + else + enum maxVariantAlignment = maxAlignment!(U); +} /** * Back-end type seldom used directly by user - * code. Two commonly-used types using $(D VariantN) are: + * code. Two commonly-used types using `VariantN` are: * * $(OL $(LI $(LREF Algebraic): A closed discriminated union with a * limited type universe (e.g., $(D Algebraic!(int, double, * string)) only accepts these three types and rejects anything * else).) $(LI $(LREF Variant): An open discriminated union allowing an - * unbounded set of types. If any of the types in the $(D Variant) + * unbounded set of types. If any of the types in the `Variant` * are larger than the largest built-in type, they will automatically * be boxed. This means that even large types will only be the size - * of a pointer within the $(D Variant), but this also implies some - * overhead. $(D Variant) can accommodate all primitive types and + * of a pointer within the `Variant`, but this also implies some + * overhead. `Variant` can accommodate all primitive types and * all user-defined types.)) * - * Both $(D Algebraic) and $(D Variant) share $(D + * Both `Algebraic` and `Variant` share $(D * VariantN)'s interface. (See their respective documentations below.) * - * $(D VariantN) is a discriminated union type parameterized - * with the largest size of the types stored ($(D maxDataSize)) - * and with the list of allowed types ($(D AllowedTypes)). If + * `VariantN` is a discriminated union type parameterized + * with the largest size of the types stored (`maxDataSize`) + * and with the list of allowed types (`AllowedTypes`). If * the list is empty, then any type up of size up to $(D * maxDataSize) (rounded up for alignment) can be stored in a - * $(D VariantN) object without being boxed (types larger + * `VariantN` object without being boxed (types larger * than this will be boxed). * */ @@ -145,9 +187,9 @@ private: } enum size = SizeChecker.sizeof - (int function()).sizeof; - /** Tells whether a type $(D T) is statically _allowed for - * storage inside a $(D VariantN) object by looking - * $(D T) up in $(D AllowedTypes). + /** Tells whether a type `T` is statically _allowed for + * storage inside a `VariantN` object by looking + * `T` up in `AllowedTypes`. */ public template allowed(T) { @@ -165,15 +207,15 @@ private: apply, postblit, destruct } // state - ptrdiff_t function(OpID selector, ubyte[size]* store, void* data) fptr - = &handler!(void); union { - ubyte[size] store; + align(maxVariantAlignment!(AllowedTypes)) ubyte[size] store; // conservatively mark the region as pointers static if (size >= (void*).sizeof) void*[size / (void*).sizeof] p; } + ptrdiff_t function(OpID selector, ubyte[size]* store, void* data) fptr + = &handler!(void); // internals // Handler for an uninitialized value @@ -235,25 +277,35 @@ private: { static if (is(typeof(*rhsPA == *zis))) { - if (*rhsPA == *zis) + enum isEmptyStructWithoutOpEquals = is(A == struct) && A.tupleof.length == 0 && + !__traits(hasMember, A, "opEquals"); + static if (isEmptyStructWithoutOpEquals) { + // The check above will always succeed if A is an empty struct. + // Don't generate unreachable code as seen in + // https://issues.dlang.org/show_bug.cgi?id=21231 return 0; } - static if (is(typeof(*zis < *rhsPA))) + else { - // Many types (such as any using the default Object opCmp) - // will throw on an invalid opCmp, so do it only - // if the caller requests it. - if (selector == OpID.compare) - return *zis < *rhsPA ? -1 : 1; + if (*rhsPA == *zis) + return 0; + static if (is(typeof(*zis < *rhsPA))) + { + // Many types (such as any using the default Object opCmp) + // will throw on an invalid opCmp, so do it only + // if the caller requests it. + if (selector == OpID.compare) + return *zis < *rhsPA ? -1 : 1; + else + return ptrdiff_t.min; + } else + { + // Not equal, and type does not support ordering + // comparisons. return ptrdiff_t.min; - } - else - { - // Not equal, and type does not support ordering - // comparisons. - return ptrdiff_t.min; + } } } else @@ -271,7 +323,14 @@ private: static bool tryPutting(A* src, TypeInfo targetType, void* target) { alias UA = Unqual!A; - alias MutaTypes = AliasSeq!(UA, ImplicitConversionTargets!UA); + static if (isStaticArray!A && is(typeof(UA.init[0]))) + { + alias MutaTypes = AliasSeq!(UA, typeof(UA.init[0])[], AllImplicitConversionTargets!UA); + } + else + { + alias MutaTypes = AliasSeq!(UA, AllImplicitConversionTargets!UA); + } alias ConstTypes = staticMap!(ConstOf, MutaTypes); alias SharedTypes = staticMap!(SharedOf, MutaTypes); alias SharedConstTypes = staticMap!(SharedConstOf, MutaTypes); @@ -299,13 +358,20 @@ private: if (targetType != typeid(T)) continue; - static if (is(typeof(*cast(T*) target = *src)) || + // SPECIAL NOTE: variant only will ever create a new value with + // tryPutting (effectively), and T is ALWAYS the same type of + // A, but with different modifiers (and a limited set of + // implicit targets). So this checks to see if we can construct + // a T from A, knowing that prerequisite. This handles issues + // where the type contains some constant data aside from the + // modifiers on the type itself. + static if (is(typeof(delegate T() {return *src;})) || is(T == const(U), U) || is(T == shared(U), U) || is(T == shared const(U), U) || is(T == immutable(U), U)) { - import std.conv : emplaceRef; + import core.internal.lifetime : emplaceRef; auto zat = cast(T*) target; if (src) @@ -313,7 +379,15 @@ private: static if (T.sizeof > 0) assert(target, "target must be non-null"); - emplaceRef(*cast(Unqual!T*) zat, *cast(UA*) src); + static if (isStaticArray!A && isDynamicArray!T) + { + auto this_ = (*src)[]; + emplaceRef(*cast(Unqual!T*) zat, cast(Unqual!T) this_); + } + else + { + emplaceRef(*cast(Unqual!T*) zat, *cast(UA*) src); + } } } else @@ -339,7 +413,17 @@ private: static if (target.size < A.sizeof) { if (target.type.tsize < A.sizeof) - *cast(A**)&target.store = new A; + { + static if (is(A == U[n], U, size_t n)) + { + A* p = cast(A*)(new U[n]).ptr; + } + else + { + A* p = new A; + } + *cast(A**)&target.store = p; + } } tryPutting(zis, typeid(A), cast(void*) getPtr(&target.store)) || assert(false); @@ -411,7 +495,7 @@ private: case OpID.index: auto result = cast(Variant*) parm; - static if (isArray!(A) && !is(Unqual!(typeof(A.init[0])) == void)) + static if (isArray!(A) && !is(immutable typeof(A.init[0]) == immutable void)) { // array type; input and output are the same VariantN size_t index = result.convertsTo!(int) @@ -439,7 +523,7 @@ private: (*zis)[index] = args[0].get!(typeof((*zis)[0])); break; } - else static if (isAssociativeArray!(A)) + else static if (isAssociativeArray!(A) && is(typeof((*zis)[A.init.keys[0]] = A.init.values[0]))) { (*zis)[args[1].get!(typeof(A.init.keys[0]))] = args[0].get!(typeof(A.init.values[0])); @@ -451,7 +535,8 @@ private: } case OpID.catAssign: - static if (!is(Unqual!(typeof((*zis)[0])) == void) && is(typeof((*zis)[0])) && is(typeof((*zis) ~= *zis))) + static if (!is(immutable typeof((*zis)[0]) == immutable void) && + is(typeof((*zis)[0])) && is(typeof(*zis ~= *zis))) { // array type; parm is the element to append auto arg = cast(Variant*) parm; @@ -529,14 +614,14 @@ private: case OpID.postblit: static if (hasElaborateCopyConstructor!A) { - typeid(A).postblit(zis); + zis.__xpostblit(); } break; case OpID.destruct: static if (hasElaborateDestructor!A) { - typeid(A).destroy(zis); + zis.__xdtor(); } break; @@ -545,10 +630,8 @@ private: return 0; } - enum doUnittest = is(VariantN == Variant); - public: - /** Constructs a $(D VariantN) value given an argument of a + /** Constructs a `VariantN` value given an argument of a * generic type. Statically rejects disallowed types. */ @@ -578,16 +661,23 @@ public: { ~this() { - fptr(OpID.destruct, &store, null); + // Infer the safety of the provided types + static if (AllowedTypes.length) + { + if (0) + { + AllowedTypes var; + } + } + (() @trusted => fptr(OpID.destruct, &store, null))(); } } - /** Assigns a $(D VariantN) from a generic + /** Assigns a `VariantN` from a generic * argument. Statically rejects disallowed types. */ VariantN opAssign(T)(T rhs) { - //writeln(typeid(rhs)); static assert(allowed!(T), "Cannot store a " ~ T.stringof ~ " in a " ~ VariantN.stringof ~ ". Valid types are " ~ AllowedTypes.stringof); @@ -604,6 +694,8 @@ public: } else { + import core.lifetime : copyEmplace; + static if (!AllowedTypes.length || anySatisfy!(hasElaborateDestructor, AllowedTypes)) { // Assignment should destruct previous value @@ -611,37 +703,17 @@ public: } static if (T.sizeof <= size) - { - import core.stdc.string : memcpy; - // If T is a class we're only copying the reference, so it - // should be safe to cast away shared so the memcpy will work. - // - // TODO: If a shared class has an atomic reference then using - // an atomic load may be more correct. Just make sure - // to use the fastest approach for the load op. - static if (is(T == class) && is(T == shared)) - memcpy(&store, cast(const(void*)) &rhs, rhs.sizeof); - else - memcpy(&store, &rhs, rhs.sizeof); - static if (hasElaborateCopyConstructor!T) - { - typeid(T).postblit(&store); - } - } + copyEmplace(rhs, *cast(T*) &store); else { - import core.stdc.string : memcpy; - static if (__traits(compiles, {new T(T.init);})) - { - auto p = new T(rhs); - } + static if (is(T == U[n], U, size_t n)) + auto p = cast(T*) (new U[n]).ptr; else - { auto p = new T; - *p = rhs; - } - memcpy(&store, &p, p.sizeof); + copyEmplace(rhs, *p); + *(cast(T**) &store) = p; } + fptr = &handler!(T); } return this; @@ -671,7 +743,7 @@ public: return pack[0]; } - /** Returns true if and only if the $(D VariantN) object + /** Returns true if and only if the `VariantN` object * holds a valid value (has been initialized with, or assigned * from, a valid value). */ @@ -682,7 +754,7 @@ public: } /// - static if (doUnittest) + version (StdDdoc) @system unittest { Variant a; @@ -695,10 +767,10 @@ public: } /** - * If the $(D VariantN) object holds a value of the - * $(I exact) type $(D T), returns a pointer to that - * value. Otherwise, returns $(D null). In cases - * where $(D T) is statically disallowed, $(D + * If the `VariantN` object holds a value of the + * $(I exact) type `T`, returns a pointer to that + * value. Otherwise, returns `null`. In cases + * where `T` is statically disallowed, $(D * peek) will not compile. */ @property inout(T)* peek(T)() inout @@ -715,7 +787,7 @@ public: } /// - static if (doUnittest) + version (StdDdoc) @system unittest { Variant a = 5; @@ -726,7 +798,7 @@ public: } /** - * Returns the $(D typeid) of the currently held value. + * Returns the `typeid` of the currently held value. */ @property TypeInfo type() const nothrow @trusted @@ -739,10 +811,10 @@ public: } /** - * Returns $(D true) if and only if the $(D VariantN) + * Returns `true` if and only if the `VariantN` * object holds an object implicitly convertible to type `T`. * Implicit convertibility is defined as per - * $(REF_ALTTEXT ImplicitConversionTargets, ImplicitConversionTargets, std,traits). + * $(REF_ALTTEXT AllImplicitConversionTargets, AllImplicitConversionTargets, std,traits). */ @property bool convertsTo(T)() const @@ -790,11 +862,11 @@ public: } /** - * Returns the value stored in the $(D VariantN) object, + * Returns the value stored in the `VariantN` object, * explicitly converted (coerced) to the requested type $(D - * T). If $(D T) is a string type, the value is formatted as - * a string. If the $(D VariantN) object is a string, a - * parse of the string to type $(D T) is attempted. If a + * T). If `T` is a string type, the value is formatted as + * a string. If the `VariantN` object is a string, a + * parse of the string to type `T` is attempted. If a * conversion is not possible, throws a $(D * VariantException). */ @@ -862,9 +934,9 @@ public: // returns 1 if the two are equal bool opEquals(T)(auto ref T rhs) const - if (allowed!T || is(Unqual!T == VariantN)) + if (allowed!T || is(immutable T == immutable VariantN)) { - static if (is(Unqual!T == VariantN)) + static if (is(immutable T == immutable VariantN)) alias temp = rhs; else auto temp = VariantN(rhs); @@ -881,7 +953,7 @@ public: /** * Ordering comparison used by the "<", "<=", ">", and ">=" * operators. In case comparison is not sensible between the held - * value and $(D rhs), an exception is thrown. + * value and `rhs`, an exception is thrown. */ int opCmp(T)(T rhs) @@ -985,79 +1057,42 @@ public: } /** - * Arithmetic between $(D VariantN) objects and numeric - * values. All arithmetic operations return a $(D VariantN) + * Arithmetic between `VariantN` objects and numeric + * values. All arithmetic operations return a `VariantN` * object typed depending on the types of both values * involved. The conversion rules mimic D's built-in rules for * arithmetic conversions. */ - - // Adapted from http://www.prowiki.org/wiki4d/wiki.cgi?DanielKeep/Variant - // arithmetic - VariantN opAdd(T)(T rhs) { return opArithmetic!(T, "+")(rhs); } - ///ditto - VariantN opSub(T)(T rhs) { return opArithmetic!(T, "-")(rhs); } - - // Commenteed all _r versions for now because of ambiguities - // arising when two Variants are used - - // ///ditto - // VariantN opSub_r(T)(T lhs) - // { - // return VariantN(lhs).opArithmetic!(VariantN, "-")(this); - // } - ///ditto - VariantN opMul(T)(T rhs) { return opArithmetic!(T, "*")(rhs); } - ///ditto - VariantN opDiv(T)(T rhs) { return opArithmetic!(T, "/")(rhs); } - // ///ditto - // VariantN opDiv_r(T)(T lhs) - // { - // return VariantN(lhs).opArithmetic!(VariantN, "/")(this); - // } - ///ditto - VariantN opMod(T)(T rhs) { return opArithmetic!(T, "%")(rhs); } - // ///ditto - // VariantN opMod_r(T)(T lhs) - // { - // return VariantN(lhs).opArithmetic!(VariantN, "%")(this); - // } - ///ditto - VariantN opAnd(T)(T rhs) { return opLogic!(T, "&")(rhs); } + VariantN opBinary(string op, T)(T rhs) + if ((op == "+" || op == "-" || op == "*" || op == "/" || op == "^^" || op == "%") && + is(typeof(opArithmetic!(T, op)(rhs)))) + { return opArithmetic!(T, op)(rhs); } ///ditto - VariantN opOr(T)(T rhs) { return opLogic!(T, "|")(rhs); } + VariantN opBinary(string op, T)(T rhs) + if ((op == "&" || op == "|" || op == "^" || op == ">>" || op == "<<" || op == ">>>") && + is(typeof(opLogic!(T, op)(rhs)))) + { return opLogic!(T, op)(rhs); } ///ditto - VariantN opXor(T)(T rhs) { return opLogic!(T, "^")(rhs); } + VariantN opBinaryRight(string op, T)(T lhs) + if ((op == "+" || op == "*") && + is(typeof(opArithmetic!(T, op)(lhs)))) + { return opArithmetic!(T, op)(lhs); } ///ditto - VariantN opShl(T)(T rhs) { return opLogic!(T, "<<")(rhs); } - // ///ditto - // VariantN opShl_r(T)(T lhs) - // { - // return VariantN(lhs).opLogic!(VariantN, "<<")(this); - // } - ///ditto - VariantN opShr(T)(T rhs) { return opLogic!(T, ">>")(rhs); } - // ///ditto - // VariantN opShr_r(T)(T lhs) - // { - // return VariantN(lhs).opLogic!(VariantN, ">>")(this); - // } - ///ditto - VariantN opUShr(T)(T rhs) { return opLogic!(T, ">>>")(rhs); } - // ///ditto - // VariantN opUShr_r(T)(T lhs) - // { - // return VariantN(lhs).opLogic!(VariantN, ">>>")(this); - // } + VariantN opBinaryRight(string op, T)(T lhs) + if ((op == "&" || op == "|" || op == "^") && + is(typeof(opLogic!(T, op)(lhs)))) + { return opLogic!(T, op)(lhs); } ///ditto - VariantN opCat(T)(T rhs) + VariantN opBinary(string op, T)(T rhs) + if (op == "~") { auto temp = this; temp ~= rhs; return temp; } // ///ditto - // VariantN opCat_r(T)(T rhs) + // VariantN opBinaryRight(string op, T)(T rhs) + // if (op == "~") // { // VariantN temp = rhs; // temp ~= this; @@ -1065,33 +1100,18 @@ public: // } ///ditto - VariantN opAddAssign(T)(T rhs) { return this = this + rhs; } - ///ditto - VariantN opSubAssign(T)(T rhs) { return this = this - rhs; } - ///ditto - VariantN opMulAssign(T)(T rhs) { return this = this * rhs; } - ///ditto - VariantN opDivAssign(T)(T rhs) { return this = this / rhs; } - ///ditto - VariantN opModAssign(T)(T rhs) { return this = this % rhs; } - ///ditto - VariantN opAndAssign(T)(T rhs) { return this = this & rhs; } - ///ditto - VariantN opOrAssign(T)(T rhs) { return this = this | rhs; } - ///ditto - VariantN opXorAssign(T)(T rhs) { return this = this ^ rhs; } - ///ditto - VariantN opShlAssign(T)(T rhs) { return this = this << rhs; } - ///ditto - VariantN opShrAssign(T)(T rhs) { return this = this >> rhs; } - ///ditto - VariantN opUShrAssign(T)(T rhs) { return this = this >>> rhs; } - ///ditto - VariantN opCatAssign(T)(T rhs) + VariantN opOpAssign(string op, T)(T rhs) { - auto toAppend = Variant(rhs); - fptr(OpID.catAssign, &store, &toAppend) == 0 || assert(false); - return this; + static if (op != "~") + { + mixin("return this = this" ~ op ~ "rhs;"); + } + else + { + auto toAppend = Variant(rhs); + fptr(OpID.catAssign, &store, &toAppend) == 0 || assert(false); + return this; + } } /** @@ -1107,7 +1127,7 @@ public: } /// - static if (doUnittest) + version (StdDdoc) @system unittest { Variant a = new int[10]; @@ -1144,7 +1164,7 @@ public: return opIndexAssign(mixin(`opIndex(i)` ~ op ~ `value`), i); } - /** If the $(D VariantN) contains an (associative) array, + /** If the `VariantN` contains an (associative) array, * returns the _length of that array. Otherwise, throws an * exception. */ @@ -1154,7 +1174,7 @@ public: } /** - If the $(D VariantN) contains an array, applies $(D dg) to each + If the `VariantN` contains an array, applies `dg` to each element of the array in turn. Otherwise, throws an exception. */ int opApply(Delegate)(scope Delegate dg) if (is(Delegate == delegate)) @@ -1192,6 +1212,63 @@ public: } } +/// +@system unittest +{ + alias Var = VariantN!(maxSize!(int, double, string)); + + Var a; // Must assign before use, otherwise exception ensues + // Initialize with an integer; make the type int + Var b = 42; + assert(b.type == typeid(int)); + // Peek at the value + assert(b.peek!(int) !is null && *b.peek!(int) == 42); + // Automatically convert per language rules + auto x = b.get!(real); + + // Assign any other type, including other variants + a = b; + a = 3.14; + assert(a.type == typeid(double)); + // Implicit conversions work just as with built-in types + assert(a < b); + // Check for convertibility + assert(!a.convertsTo!(int)); // double not convertible to int + // Strings and all other arrays are supported + a = "now I'm a string"; + assert(a == "now I'm a string"); +} + +/// can also assign arrays +@system unittest +{ + alias Var = VariantN!(maxSize!(int[])); + + Var a = new int[42]; + assert(a.length == 42); + a[5] = 7; + assert(a[5] == 7); +} + +@safe unittest +{ + alias V = VariantN!24; + const alignMask = V.alignof - 1; + assert(V.sizeof == ((24 + (void*).sizeof + alignMask) & ~alignMask)); +} + +/// Can also assign class values +@system unittest +{ + alias Var = VariantN!(maxSize!(int*)); // classes are pointers + Var a; + + class Foo {} + auto foo = new Foo; + a = foo; + assert(*a.peek!(Foo) == foo); // and full type information is preserved +} + @system unittest { import std.conv : to; @@ -1214,7 +1291,7 @@ public: assert(v[42] == 5); } -// opIndex with static arrays, issue 12771 +// opIndex with static arrays, https://issues.dlang.org/show_bug.cgi?id=12771 @system unittest { int[4] elements = [0, 1, 2, 3]; @@ -1245,7 +1322,33 @@ public: assertThrown!VariantException(v[1] = Variant(null)); } -//Issue# 8195 +// https://issues.dlang.org/show_bug.cgi?id=10879 +@system unittest +{ + int[10] arr = [1,2,3,4,5,6,7,8,9,10]; + Variant v1 = arr; + Variant v2; + v2 = arr; + assert(v1 == arr); + assert(v2 == arr); + foreach (i, e; arr) + { + assert(v1[i] == e); + assert(v2[i] == e); + } + static struct LargeStruct + { + int[100] data; + } + LargeStruct ls; + ls.data[] = 4; + v1 = ls; + Variant v3 = ls; + assert(v1 == ls); + assert(v3 == ls); +} + +// https://issues.dlang.org/show_bug.cgi?id=8195 @system unittest { struct S @@ -1265,7 +1368,7 @@ public: assert(v == S.init); } -// Issue #10961 +// https://issues.dlang.org/show_bug.cgi?id=10961 @system unittest { // Primarily test that we can assign a void[] to a Variant. @@ -1275,7 +1378,7 @@ public: assert(returned == elements); } -// Issue #13352 +// https://issues.dlang.org/show_bug.cgi?id=13352 @system unittest { alias TP = Algebraic!(long); @@ -1291,7 +1394,7 @@ public: assert(a + c == 4L); } -// Issue #13354 +// https://issues.dlang.org/show_bug.cgi?id=13354 @system unittest { alias A = Algebraic!(string[]); @@ -1309,14 +1412,14 @@ public: assert(aa["b"] == 3); } -// Issue #14198 +// https://issues.dlang.org/show_bug.cgi?id=14198 @system unittest { Variant a = true; assert(a.type == typeid(bool)); } -// Issue #14233 +// https://issues.dlang.org/show_bug.cgi?id=14233 @system unittest { alias Atom = Algebraic!(string, This[]); @@ -1333,7 +1436,7 @@ pure nothrow @nogc a = 1.0; } -// Issue 14457 +// https://issues.dlang.org/show_bug.cgi?id=14457 @system unittest { alias A = Algebraic!(int, float, double); @@ -1347,7 +1450,7 @@ pure nothrow @nogc assert(a.get!float == 6f); } -// Issue 14585 +// https://issues.dlang.org/show_bug.cgi?id=14585 @system unittest { static struct S @@ -1358,7 +1461,7 @@ pure nothrow @nogc Variant(S()).get!S; } -// Issue 14586 +// https://issues.dlang.org/show_bug.cgi?id=14586 @system unittest { const Variant v = new immutable Object; @@ -1375,6 +1478,135 @@ pure nothrow @nogc v.get!S; } +// https://issues.dlang.org/show_bug.cgi?id=13262 +@system unittest +{ + static void fun(T)(Variant v){ + T x; + v = x; + auto r = v.get!(T); + } + Variant v; + fun!(shared(int))(v); + fun!(shared(int)[])(v); + + static struct S1 + { + int c; + string a; + } + + static struct S2 + { + string a; + shared int[] b; + } + + static struct S3 + { + string a; + shared int[] b; + int c; + } + + fun!(S1)(v); + fun!(shared(S1))(v); + fun!(S2)(v); + fun!(shared(S2))(v); + fun!(S3)(v); + fun!(shared(S3))(v); + + // ensure structs that are shared, but don't have shared postblits + // can't be used. + static struct S4 + { + int x; + this(this) {x = 0;} + } + + fun!(S4)(v); + static assert(!is(typeof(fun!(shared(S4))(v)))); +} + +@safe unittest +{ + Algebraic!(int) x; + + static struct SafeS + { + @safe ~this() {} + } + + Algebraic!(SafeS) y; +} + +// https://issues.dlang.org/show_bug.cgi?id=19986 +@system unittest +{ + VariantN!32 v; + v = const(ubyte[33]).init; + + struct S + { + ubyte[33] s; + } + + VariantN!32 v2; + v2 = const(S).init; +} + +// https://issues.dlang.org/show_bug.cgi?id=21021 +@system unittest +{ + static struct S + { + int h; + int[5] array; + alias h this; + } + + S msg; + msg.array[] = 3; + Variant a = msg; + auto other = a.get!S; + assert(msg.array[0] == 3); + assert(other.array[0] == 3); +} + +// https://issues.dlang.org/show_bug.cgi?id=21231 +// Compatibility with -preview=fieldwise +@system unittest +{ + static struct Empty + { + bool opCmp(const scope ref Empty) const + { return false; } + } + + Empty a, b; + assert(a == b); + assert(!(a < b)); + + VariantN!(4, Empty) v = a; + assert(v == b); + assert(!(v < b)); +} + +// Compatibility with -preview=fieldwise +@system unittest +{ + static struct Empty + { + bool opEquals(const scope ref Empty) const + { return false; } + } + + Empty a, b; + assert(a != b); + + VariantN!(4, Empty) v = a; + assert(v != b); +} /** _Algebraic data type restricted to a closed set of possible @@ -1384,6 +1616,8 @@ useful when it is desirable to restrict what a discriminated type could hold to the end of defining simpler and more efficient manipulation. +$(RED Warning: $(LREF Algebraic) is outdated and not recommended for use in new +code. Instead, use $(REF SumType, std,sumtype).) */ template Algebraic(T...) { @@ -1435,20 +1669,70 @@ be arbitrarily complex. assert(obj.get!2["customer"] == "John"); } +private struct FakeComplexReal +{ + real re, im; +} + /** Alias for $(LREF VariantN) instantiated with the largest size of `creal`, `char[]`, and `void delegate()`. This ensures that `Variant` is large enough to hold all of D's predefined types unboxed, including all numeric types, pointers, delegates, and class references. You may want to use -$(D VariantN) directly with a different maximum size either for +`VariantN` directly with a different maximum size either for storing larger types unboxed, or for saving memory. */ -alias Variant = VariantN!(maxSize!(creal, char[], void delegate())); +alias Variant = VariantN!(maxSize!(FakeComplexReal, char[], void delegate())); + +/// +@system unittest +{ + Variant a; // Must assign before use, otherwise exception ensues + // Initialize with an integer; make the type int + Variant b = 42; + assert(b.type == typeid(int)); + // Peek at the value + assert(b.peek!(int) !is null && *b.peek!(int) == 42); + // Automatically convert per language rules + auto x = b.get!(real); + + // Assign any other type, including other variants + a = b; + a = 3.14; + assert(a.type == typeid(double)); + // Implicit conversions work just as with built-in types + assert(a < b); + // Check for convertibility + assert(!a.convertsTo!(int)); // double not convertible to int + // Strings and all other arrays are supported + a = "now I'm a string"; + assert(a == "now I'm a string"); +} + +/// can also assign arrays +@system unittest +{ + Variant a = new int[42]; + assert(a.length == 42); + a[5] = 7; + assert(a[5] == 7); +} + +/// Can also assign class values +@system unittest +{ + Variant a; + + class Foo {} + auto foo = new Foo; + a = foo; + assert(*a.peek!(Foo) == foo); // and full type information is preserved +} /** - * Returns an array of variants constructed from $(D args). + * Returns an array of variants constructed from `args`. * - * This is by design. During construction the $(D Variant) needs + * This is by design. During construction the `Variant` needs * static type information about the type being held, so as to store a * pointer to function for fast retrieval. */ @@ -1475,9 +1759,9 @@ Variant[] variantArray(T...)(T args) * Thrown in three cases: * * $(OL $(LI An uninitialized `Variant` is used in any way except - * assignment and $(D hasValue);) $(LI A $(D get) or - * $(D coerce) is attempted with an incompatible target type;) - * $(LI A comparison between $(D Variant) objects of + * assignment and `hasValue`;) $(LI A `get` or + * `coerce` is attempted with an incompatible target type;) + * $(LI A comparison between `Variant` objects of * incompatible types is attempted.)) * */ @@ -1503,6 +1787,24 @@ static class VariantException : Exception } } +/// +@system unittest +{ + import std.exception : assertThrown; + + Variant v; + + // uninitialized use + assertThrown!VariantException(v + 1); + assertThrown!VariantException(v.length); + + // .get with an incompatible target type + assertThrown!VariantException(Variant("a").get!int); + + // comparison between incompatible types + assertThrown!VariantException(Variant(3) < Variant("a")); +} + @system unittest { alias W1 = This2Variant!(char, int, This[int]); @@ -1660,7 +1962,7 @@ static class VariantException : Exception assert( v.peek!(double) ); assert( v.convertsTo!(real) ); //@@@ BUG IN COMPILER: DOUBLE SHOULD NOT IMPLICITLY CONVERT TO FLOAT - assert( !v.convertsTo!(float) ); + assert( v.convertsTo!(float) ); assert( *v.peek!(double) == 3.1413 ); auto u = Variant(v); @@ -1721,16 +2023,13 @@ static class VariantException : Exception { auto v1 = Variant(42); auto v2 = Variant("foo"); - auto v3 = Variant(1+2.0i); int[Variant] hash; hash[v1] = 0; hash[v2] = 1; - hash[v3] = 2; assert( hash[v1] == 0 ); assert( hash[v2] == 1 ); - assert( hash[v3] == 2 ); } { @@ -1759,9 +2058,9 @@ static class VariantException : Exception static assert(!__traits(compiles, {v > null;})); } +// https://issues.dlang.org/show_bug.cgi?id=1558 @system unittest { - // bug 1558 Variant va=1; Variant vb=-2; assert((va+vb).get!(int) == -1); @@ -1826,7 +2125,7 @@ static class VariantException : Exception assert(v.convertsTo!(char[])); } -// http://d.puremagic.com/issues/show_bug.cgi?id=5424 +// https://issues.dlang.org/show_bug.cgi?id=5424 @system unittest { interface A { @@ -1842,14 +2141,14 @@ static class VariantException : Exception Variant b = Variant(a); } +// https://issues.dlang.org/show_bug.cgi?id=7070 @system unittest { - // bug 7070 Variant v; v = null; } -// Class and interface opEquals, issue 12157 +// Class and interface opEquals, https://issues.dlang.org/show_bug.cgi?id=12157 @system unittest { class Foo { } @@ -1867,7 +2166,7 @@ static class VariantException : Exception assert(v2 == f2); } -// Const parameters with opCall, issue 11361. +// Const parameters with opCall, https://issues.dlang.org/show_bug.cgi?id=11361 @system unittest { static string t1(string c) { @@ -1898,7 +2197,7 @@ static class VariantException : Exception assert(v3(4).type == typeid(char[])); } -// issue 12071 +// https://issues.dlang.org/show_bug.cgi?id=12071 @system unittest { static struct Structure { int data; } @@ -1915,7 +2214,8 @@ static class VariantException : Exception assert(called); } -// Ordering comparisons of incompatible types, e.g. issue 7990. +// Ordering comparisons of incompatible types +// e.g. https://issues.dlang.org/show_bug.cgi?id=7990 @system unittest { import std.exception : assertThrown; @@ -1927,7 +2227,8 @@ static class VariantException : Exception assertThrown!VariantException(Variant(3) < Variant.init); } -// Handling of unordered types, e.g. issue 9043. +// Handling of unordered types +// https://issues.dlang.org/show_bug.cgi?id=9043 @system unittest { import std.exception : assertThrown; @@ -1940,7 +2241,8 @@ static class VariantException : Exception assertThrown!VariantException(Variant(A(3)) < Variant(A(4))); } -// Handling of empty types and arrays, e.g. issue 10958 +// Handling of empty types and arrays +// https://issues.dlang.org/show_bug.cgi?id=10958 @system unittest { class EmptyClass { } @@ -1971,7 +2273,8 @@ static class VariantException : Exception assert(a.get!EmptyArray == arr); } -// Handling of void function pointers / delegates, e.g. issue 11360 +// Handling of void function pointers / delegates +// https://issues.dlang.org/show_bug.cgi?id=11360 @system unittest { static void t1() { } @@ -1983,7 +2286,8 @@ static class VariantException : Exception assert(v2() == 3); } -// Using peek for large structs, issue 8580 +// Using peek for large structs +// https://issues.dlang.org/show_bug.cgi?id=8580 @system unittest { struct TestStruct(bool pad) @@ -2014,16 +2318,25 @@ static class VariantException : Exception testPeekWith!(TestStruct!true)(); } +// https://issues.dlang.org/show_bug.cgi?id=18780 +@system unittest +{ + int x = 7; + Variant a = x; + assert(a.convertsTo!ulong); + assert(a.convertsTo!uint); +} + /** * Applies a delegate or function to the given $(LREF Algebraic) depending on the held type, * ensuring that all types are handled by the visiting functions. * * The delegate or function having the currently held value as parameter is called - * with $(D variant)'s current value. Visiting handlers are passed + * with `variant`'s current value. Visiting handlers are passed * in the template parameter list. * It is statically ensured that all held types of - * $(D variant) are handled across all handlers. - * $(D visit) allows delegates and static functions to be passed + * `variant` are handled across all handlers. + * `visit` allows delegates and static functions to be passed * as parameters. * * If a function with an untyped parameter is specified, this function is called @@ -2164,11 +2477,11 @@ if (Handlers.length > 0) Algebraic!(int, string) maybenumber = 2; // ok, x ~ "a" valid for string, x + 1 valid for int, only 1 generic - static assert( __traits(compiles, number.visit!((string x) => x ~ "a", x => x + 1))); + static assert( __traits(compiles, maybenumber.visit!((string x) => x ~ "a", x => "foobar"[0 .. x + 1]))); // bad, x ~ "a" valid for string but not int - static assert(!__traits(compiles, number.visit!(x => x ~ "a"))); + static assert(!__traits(compiles, maybenumber.visit!(x => x ~ "a"))); // bad, two generics, each only applies in one case - static assert(!__traits(compiles, number.visit!(x => x + 1, x => x ~ "a"))); + static assert(!__traits(compiles, maybenumber.visit!(x => x + 1, x => x ~ "a"))); } /** @@ -2176,7 +2489,7 @@ if (Handlers.length > 0) * by the visiting functions. * * If a parameter-less function is specified it is called when - * either $(D variant) doesn't hold a value or holds a type + * either `variant` doesn't hold a value or holds a type * which isn't handled by the visiting functions. * * Returns: The return type of tryVisit is deduced from the visiting functions and must be @@ -2260,11 +2573,11 @@ if (isAlgebraic!VariantType && Handler.length > 0) /** - * Returns: Struct where $(D indices) is an array which + * Returns: Struct where `indices` is an array which * contains at the n-th position the index in Handler which takes the * n-th type of AllowedTypes. If an Handler doesn't match an * AllowedType, -1 is set. If a function in the delegates doesn't - * have parameters, the field $(D exceptionFuncIdx) is set; + * have parameters, the field `exceptionFuncIdx` is set; * otherwise it's -1. */ auto visitGetOverloadMap() @@ -2277,6 +2590,24 @@ if (isAlgebraic!VariantType && Handler.length > 0) Result result; + enum int nonmatch = () + { + foreach (int dgidx, dg; Handler) + { + bool found = false; + foreach (T; AllowedTypes) + { + found |= __traits(compiles, { static assert(isSomeFunction!(dg!T)); }); + found |= __traits(compiles, (T t) { dg(t); }); + found |= __traits(compiles, dg()); + } + if (!found) return dgidx; + } + return -1; + }(); + static assert(nonmatch == -1, "No match for visit handler #"~ + nonmatch.stringof~" ("~Handler[nonmatch].stringof~")"); + foreach (tidx, T; AllowedTypes) { bool added = false; @@ -2308,7 +2639,7 @@ if (isAlgebraic!VariantType && Handler.length > 0) result.indices[tidx] = dgidx; } } - else static if (isSomeFunction!(dg!T)) + else static if (__traits(compiles, { static assert(isSomeFunction!(dg!T)); })) { assert(result.generalFuncIdx == -1 || result.generalFuncIdx == dgidx, @@ -2316,10 +2647,6 @@ if (isAlgebraic!VariantType && Handler.length > 0) result.generalFuncIdx = dgidx; } // Handle composite visitors with opCall overloads - else - { - static assert(false, dg.stringof ~ " is not a function or delegate"); - } } if (!added) @@ -2373,6 +2700,19 @@ if (isAlgebraic!VariantType && Handler.length > 0) assert(false); } +// https://issues.dlang.org/show_bug.cgi?id=21253 +@system unittest +{ + static struct A { int n; } + static struct B { } + + auto a = Algebraic!(A, B)(B()); + assert(a.visit!( + (B _) => 42, + (a ) => a.n + ) == 42); +} + @system unittest { // validate that visit can be called with a const type @@ -2389,9 +2729,9 @@ if (isAlgebraic!VariantType && Handler.length > 0) assert(depth(fb) == 3); } +// https://issues.dlang.org/show_bug.cgi?id=16383 @system unittest { - // https://issues.dlang.org/show_bug.cgi?id=16383 class Foo {this() immutable {}} alias V = Algebraic!(immutable Foo); @@ -2401,9 +2741,9 @@ if (isAlgebraic!VariantType && Handler.length > 0) assert(x == 3); } +// https://issues.dlang.org/show_bug.cgi?id=5310 @system unittest { - // http://d.puremagic.com/issues/show_bug.cgi?id=5310 const Variant a; assert(a == a); Variant b; @@ -2417,9 +2757,9 @@ if (isAlgebraic!VariantType && Handler.length > 0) assert(a[0] == 2); } +// https://issues.dlang.org/show_bug.cgi?id=10017 @system unittest { - // http://d.puremagic.com/issues/show_bug.cgi?id=10017 static struct S { ubyte[Variant.size + 1] s; @@ -2430,32 +2770,33 @@ if (isAlgebraic!VariantType && Handler.length > 0) v2 = v1; // AssertError: target must be non-null assert(v1 == v2); } + +// https://issues.dlang.org/show_bug.cgi?id=7069 @system unittest { import std.exception : assertThrown; - // http://d.puremagic.com/issues/show_bug.cgi?id=7069 Variant v; int i = 10; v = i; - foreach (qual; AliasSeq!(MutableOf, ConstOf)) + static foreach (qual; AliasSeq!(Alias, ConstOf)) { assert(v.get!(qual!int) == 10); assert(v.get!(qual!float) == 10.0f); } - foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) + static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) { assertThrown!VariantException(v.get!(qual!int)); } const(int) ci = 20; v = ci; - foreach (qual; AliasSeq!(ConstOf)) + static foreach (qual; AliasSeq!(ConstOf)) { assert(v.get!(qual!int) == 20); assert(v.get!(qual!float) == 20.0f); } - foreach (qual; AliasSeq!(MutableOf, ImmutableOf, SharedOf, SharedConstOf)) + static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf)) { assertThrown!VariantException(v.get!(qual!int)); assertThrown!VariantException(v.get!(qual!float)); @@ -2463,12 +2804,12 @@ if (isAlgebraic!VariantType && Handler.length > 0) immutable(int) ii = ci; v = ii; - foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf)) + static foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf)) { assert(v.get!(qual!int) == 20); assert(v.get!(qual!float) == 20.0f); } - foreach (qual; AliasSeq!(MutableOf, SharedOf)) + static foreach (qual; AliasSeq!(Alias, SharedOf)) { assertThrown!VariantException(v.get!(qual!int)); assertThrown!VariantException(v.get!(qual!float)); @@ -2476,12 +2817,12 @@ if (isAlgebraic!VariantType && Handler.length > 0) int[] ai = [1,2,3]; v = ai; - foreach (qual; AliasSeq!(MutableOf, ConstOf)) + static foreach (qual; AliasSeq!(Alias, ConstOf)) { assert(v.get!(qual!(int[])) == [1,2,3]); assert(v.get!(qual!(int)[]) == [1,2,3]); } - foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) + static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) { assertThrown!VariantException(v.get!(qual!(int[]))); assertThrown!VariantException(v.get!(qual!(int)[])); @@ -2489,12 +2830,12 @@ if (isAlgebraic!VariantType && Handler.length > 0) const(int[]) cai = [4,5,6]; v = cai; - foreach (qual; AliasSeq!(ConstOf)) + static foreach (qual; AliasSeq!(ConstOf)) { assert(v.get!(qual!(int[])) == [4,5,6]); assert(v.get!(qual!(int)[]) == [4,5,6]); } - foreach (qual; AliasSeq!(MutableOf, ImmutableOf, SharedOf, SharedConstOf)) + static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf)) { assertThrown!VariantException(v.get!(qual!(int[]))); assertThrown!VariantException(v.get!(qual!(int)[])); @@ -2508,7 +2849,7 @@ if (isAlgebraic!VariantType && Handler.length > 0) assert(v.get!(const(int)[]) == [7,8,9]); //assert(v.get!(shared(const(int[]))) == cast(shared const)[7,8,9]); // Bug ??? runtime error //assert(v.get!(shared(const(int))[]) == cast(shared const)[7,8,9]); // Bug ??? runtime error - foreach (qual; AliasSeq!(MutableOf)) + static foreach (qual; AliasSeq!(Alias)) { assertThrown!VariantException(v.get!(qual!(int[]))); assertThrown!VariantException(v.get!(qual!(int)[])); @@ -2518,13 +2859,13 @@ if (isAlgebraic!VariantType && Handler.length > 0) class B : A {} B b = new B(); v = b; - foreach (qual; AliasSeq!(MutableOf, ConstOf)) + static foreach (qual; AliasSeq!(Alias, ConstOf)) { assert(v.get!(qual!B) is b); assert(v.get!(qual!A) is b); assert(v.get!(qual!Object) is b); } - foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) + static foreach (qual; AliasSeq!(ImmutableOf, SharedOf, SharedConstOf)) { assertThrown!VariantException(v.get!(qual!B)); assertThrown!VariantException(v.get!(qual!A)); @@ -2533,13 +2874,13 @@ if (isAlgebraic!VariantType && Handler.length > 0) const(B) cb = new B(); v = cb; - foreach (qual; AliasSeq!(ConstOf)) + static foreach (qual; AliasSeq!(ConstOf)) { assert(v.get!(qual!B) is cb); assert(v.get!(qual!A) is cb); assert(v.get!(qual!Object) is cb); } - foreach (qual; AliasSeq!(MutableOf, ImmutableOf, SharedOf, SharedConstOf)) + static foreach (qual; AliasSeq!(Alias, ImmutableOf, SharedOf, SharedConstOf)) { assertThrown!VariantException(v.get!(qual!B)); assertThrown!VariantException(v.get!(qual!A)); @@ -2548,13 +2889,13 @@ if (isAlgebraic!VariantType && Handler.length > 0) immutable(B) ib = new immutable(B)(); v = ib; - foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf)) + static foreach (qual; AliasSeq!(ImmutableOf, ConstOf, SharedConstOf)) { assert(v.get!(qual!B) is ib); assert(v.get!(qual!A) is ib); assert(v.get!(qual!Object) is ib); } - foreach (qual; AliasSeq!(MutableOf, SharedOf)) + static foreach (qual; AliasSeq!(Alias, SharedOf)) { assertThrown!VariantException(v.get!(qual!B)); assertThrown!VariantException(v.get!(qual!A)); @@ -2563,13 +2904,13 @@ if (isAlgebraic!VariantType && Handler.length > 0) shared(B) sb = new shared B(); v = sb; - foreach (qual; AliasSeq!(SharedOf, SharedConstOf)) + static foreach (qual; AliasSeq!(SharedOf, SharedConstOf)) { assert(v.get!(qual!B) is sb); assert(v.get!(qual!A) is sb); assert(v.get!(qual!Object) is sb); } - foreach (qual; AliasSeq!(MutableOf, ImmutableOf, ConstOf)) + static foreach (qual; AliasSeq!(Alias, ImmutableOf, ConstOf)) { assertThrown!VariantException(v.get!(qual!B)); assertThrown!VariantException(v.get!(qual!A)); @@ -2578,13 +2919,13 @@ if (isAlgebraic!VariantType && Handler.length > 0) shared(const(B)) scb = new shared const B(); v = scb; - foreach (qual; AliasSeq!(SharedConstOf)) + static foreach (qual; AliasSeq!(SharedConstOf)) { assert(v.get!(qual!B) is scb); assert(v.get!(qual!A) is scb); assert(v.get!(qual!Object) is scb); } - foreach (qual; AliasSeq!(MutableOf, ConstOf, ImmutableOf, SharedOf)) + static foreach (qual; AliasSeq!(Alias, ConstOf, ImmutableOf, SharedOf)) { assertThrown!VariantException(v.get!(qual!B)); assertThrown!VariantException(v.get!(qual!A)); @@ -2592,11 +2933,11 @@ if (isAlgebraic!VariantType && Handler.length > 0) } } +// https://issues.dlang.org/show_bug.cgi?id=12540 @system unittest { static struct DummyScope { - // https://d.puremagic.com/issues/show_bug.cgi?id=12540 alias Alias12540 = Algebraic!Class12540; static class Class12540 @@ -2654,7 +2995,7 @@ if (isAlgebraic!VariantType && Handler.length > 0) @system unittest { - // Bugzilla 13300 + // https://issues.dlang.org/show_bug.cgi?id=13300 static struct S { this(this) {} @@ -2682,9 +3023,9 @@ if (isAlgebraic!VariantType && Handler.length > 0) auto a = appender!(T[]); } +// https://issues.dlang.org/show_bug.cgi?id=13871 @system unittest { - // Bugzilla 13871 alias A = Algebraic!(int, typeof(null)); static struct B { A value; } alias C = std.variant.Algebraic!B; @@ -2721,9 +3062,9 @@ if (isAlgebraic!VariantType && Handler.length > 0) assertThrown!VariantException(v.length); } +// https://issues.dlang.org/show_bug.cgi?id=13534 @system unittest { - // Bugzilla 13534 static assert(!__traits(compiles, () @safe { auto foo() @system { return 3; } auto v = Variant(&foo); @@ -2731,9 +3072,9 @@ if (isAlgebraic!VariantType && Handler.length > 0) })); } +// https://issues.dlang.org/show_bug.cgi?id=15039 @system unittest { - // Bugzilla 15039 import std.typecons; import std.variant; @@ -2749,9 +3090,9 @@ if (isAlgebraic!VariantType && Handler.length > 0) ); } +// https://issues.dlang.org/show_bug.cgi?id=15791 @system unittest { - // Bugzilla 15791 int n = 3; struct NS1 { int foo() { return n + 10; } } struct NS2 { int foo() { return n * 10; } } @@ -2763,9 +3104,103 @@ if (isAlgebraic!VariantType && Handler.length > 0) assert(v.get!NS2.foo() == 30); } +// https://issues.dlang.org/show_bug.cgi?id=15827 @system unittest { - // Bugzilla 15827 static struct Foo15827 { Variant v; this(Foo15827 v) {} } Variant v = Foo15827.init; } + +// https://issues.dlang.org/show_bug.cgi?id=18934 +@system unittest +{ + static struct S + { + const int x; + } + + auto s = S(42); + Variant v = s; + auto s2 = v.get!S; + assert(s2.x == 42); + Variant v2 = v; // support copying from one variant to the other + v2 = S(2); + v = v2; + assert(v.get!S.x == 2); +} + +// https://issues.dlang.org/show_bug.cgi?id=19200 +@system unittest +{ + static struct S + { + static int opBinaryRight(string op : "|", T)(T rhs) + { + return 3; + } + } + + S s; + Variant v; + auto b = v | s; + assert(b == 3); +} + +// https://issues.dlang.org/show_bug.cgi?id=11061 +@system unittest +{ + int[4] el = [0, 1, 2, 3]; + int[3] nl = [0, 1, 2]; + Variant v1 = el; + assert(v1 == el); // Compare Var(static) to static + assert(v1 != nl); // Compare static arrays of different length + assert(v1 == [0, 1, 2, 3]); // Compare Var(static) to dynamic. + assert(v1 != [0, 1, 2]); + int[] dyn = [0, 1, 2, 3]; + v1 = dyn; + assert(v1 == el); // Compare Var(dynamic) to static. + assert(v1 == [0, 1] ~ [2, 3]); // Compare Var(dynamic) to dynamic +} + +// https://issues.dlang.org/show_bug.cgi?id=15940 +@system unittest +{ + class C { } + struct S + { + C a; + alias a this; + } + S s = S(new C()); + auto v = Variant(s); // compile error +} + +@system unittest +{ + // Test if we don't have scoping issues. + Variant createVariant(int[] input) + { + int[2] el = [input[0], input[1]]; + Variant v = el; + return v; + } + Variant v = createVariant([0, 1]); + createVariant([2, 3]); + assert(v == [0,1]); +} + +// https://issues.dlang.org/show_bug.cgi?id=19994 +@safe unittest +{ + alias Inner = Algebraic!(This*); + alias Outer = Algebraic!(Inner, This*); + + static assert(is(Outer.AllowedTypes == AliasSeq!(Inner, Outer*))); +} + +// https://issues.dlang.org/show_bug.cgi?id=21296 +@system unittest +{ + immutable aa = ["0": 0]; + auto v = Variant(aa); // compile error +} |