Newer
Older
dub_jkp / source / dyaml / stdsumtype.d
  1. /++
  2. This module was copied from Phobos at commit 87c6e7e35 (2022-07-06).
  3. This is necessary to include https://github.com/dlang/phobos/pull/8501
  4. which is a fix needed for DIP1000 compatibility. A couple minor changes
  5. where also required to deal with `package(std)` imports.
  6.  
  7. [SumType] is a generic discriminated union implementation that uses
  8. design-by-introspection to generate safe and efficient code. Its features
  9. include:
  10.  
  11. * [Pattern matching.][match]
  12. * Support for self-referential types.
  13. * Full attribute correctness (`pure`, `@safe`, `@nogc`, and `nothrow` are
  14. inferred whenever possible).
  15. * A type-safe and memory-safe API compatible with DIP 1000 (`scope`).
  16. * No dependency on runtime type information (`TypeInfo`).
  17. * Compatibility with BetterC.
  18.  
  19. License: Boost License 1.0
  20. Authors: Paul Backus
  21. Source: $(PHOBOSSRC std/sumtype.d)
  22. +/
  23. module dyaml.stdsumtype;
  24.  
  25. /// $(DIVID basic-usage,$(H3 Basic usage))
  26. version (D_BetterC) {} else
  27. @safe unittest
  28. {
  29. import std.math.operations : isClose;
  30.  
  31. struct Fahrenheit { double degrees; }
  32. struct Celsius { double degrees; }
  33. struct Kelvin { double degrees; }
  34.  
  35. alias Temperature = SumType!(Fahrenheit, Celsius, Kelvin);
  36.  
  37. // Construct from any of the member types.
  38. Temperature t1 = Fahrenheit(98.6);
  39. Temperature t2 = Celsius(100);
  40. Temperature t3 = Kelvin(273);
  41.  
  42. // Use pattern matching to access the value.
  43. Fahrenheit toFahrenheit(Temperature t)
  44. {
  45. return Fahrenheit(
  46. t.match!(
  47. (Fahrenheit f) => f.degrees,
  48. (Celsius c) => c.degrees * 9.0/5 + 32,
  49. (Kelvin k) => k.degrees * 9.0/5 - 459.4
  50. )
  51. );
  52. }
  53.  
  54. assert(toFahrenheit(t1).degrees.isClose(98.6));
  55. assert(toFahrenheit(t2).degrees.isClose(212));
  56. assert(toFahrenheit(t3).degrees.isClose(32));
  57.  
  58. // Use ref to modify the value in place.
  59. void freeze(ref Temperature t)
  60. {
  61. t.match!(
  62. (ref Fahrenheit f) => f.degrees = 32,
  63. (ref Celsius c) => c.degrees = 0,
  64. (ref Kelvin k) => k.degrees = 273
  65. );
  66. }
  67.  
  68. freeze(t1);
  69. assert(toFahrenheit(t1).degrees.isClose(32));
  70.  
  71. // Use a catch-all handler to give a default result.
  72. bool isFahrenheit(Temperature t)
  73. {
  74. return t.match!(
  75. (Fahrenheit f) => true,
  76. _ => false
  77. );
  78. }
  79.  
  80. assert(isFahrenheit(t1));
  81. assert(!isFahrenheit(t2));
  82. assert(!isFahrenheit(t3));
  83. }
  84.  
  85. /** $(DIVID introspection-based-matching, $(H3 Introspection-based matching))
  86. *
  87. * In the `length` and `horiz` functions below, the handlers for `match` do not
  88. * specify the types of their arguments. Instead, matching is done based on how
  89. * the argument is used in the body of the handler: any type with `x` and `y`
  90. * properties will be matched by the `rect` handlers, and any type with `r` and
  91. * `theta` properties will be matched by the `polar` handlers.
  92. */
  93. version (D_BetterC) {} else
  94. @safe unittest
  95. {
  96. import std.math.operations : isClose;
  97. import std.math.trigonometry : cos;
  98. import std.math.constants : PI;
  99. import std.math.algebraic : sqrt;
  100.  
  101. struct Rectangular { double x, y; }
  102. struct Polar { double r, theta; }
  103. alias Vector = SumType!(Rectangular, Polar);
  104.  
  105. double length(Vector v)
  106. {
  107. return v.match!(
  108. rect => sqrt(rect.x^^2 + rect.y^^2),
  109. polar => polar.r
  110. );
  111. }
  112.  
  113. double horiz(Vector v)
  114. {
  115. return v.match!(
  116. rect => rect.x,
  117. polar => polar.r * cos(polar.theta)
  118. );
  119. }
  120.  
  121. Vector u = Rectangular(1, 1);
  122. Vector v = Polar(1, PI/4);
  123.  
  124. assert(length(u).isClose(sqrt(2.0)));
  125. assert(length(v).isClose(1));
  126. assert(horiz(u).isClose(1));
  127. assert(horiz(v).isClose(sqrt(0.5)));
  128. }
  129.  
  130. /** $(DIVID arithmetic-expression-evaluator, $(H3 Arithmetic expression evaluator))
  131. *
  132. * This example makes use of the special placeholder type `This` to define a
  133. * [recursive data type](https://en.wikipedia.org/wiki/Recursive_data_type): an
  134. * [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree) for
  135. * representing simple arithmetic expressions.
  136. */
  137. version (D_BetterC) {} else
  138. @system unittest
  139. {
  140. import std.functional : partial;
  141. import std.traits : EnumMembers;
  142. import std.typecons : Tuple;
  143.  
  144. enum Op : string
  145. {
  146. Plus = "+",
  147. Minus = "-",
  148. Times = "*",
  149. Div = "/"
  150. }
  151.  
  152. // An expression is either
  153. // - a number,
  154. // - a variable, or
  155. // - a binary operation combining two sub-expressions.
  156. alias Expr = SumType!(
  157. double,
  158. string,
  159. Tuple!(Op, "op", This*, "lhs", This*, "rhs")
  160. );
  161.  
  162. // Shorthand for Tuple!(Op, "op", Expr*, "lhs", Expr*, "rhs"),
  163. // the Tuple type above with Expr substituted for This.
  164. alias BinOp = Expr.Types[2];
  165.  
  166. // Factory function for number expressions
  167. Expr* num(double value)
  168. {
  169. return new Expr(value);
  170. }
  171.  
  172. // Factory function for variable expressions
  173. Expr* var(string name)
  174. {
  175. return new Expr(name);
  176. }
  177.  
  178. // Factory function for binary operation expressions
  179. Expr* binOp(Op op, Expr* lhs, Expr* rhs)
  180. {
  181. return new Expr(BinOp(op, lhs, rhs));
  182. }
  183.  
  184. // Convenience wrappers for creating BinOp expressions
  185. alias sum = partial!(binOp, Op.Plus);
  186. alias diff = partial!(binOp, Op.Minus);
  187. alias prod = partial!(binOp, Op.Times);
  188. alias quot = partial!(binOp, Op.Div);
  189.  
  190. // Evaluate expr, looking up variables in env
  191. double eval(Expr expr, double[string] env)
  192. {
  193. return expr.match!(
  194. (double num) => num,
  195. (string var) => env[var],
  196. (BinOp bop)
  197. {
  198. double lhs = eval(*bop.lhs, env);
  199. double rhs = eval(*bop.rhs, env);
  200. final switch (bop.op)
  201. {
  202. static foreach (op; EnumMembers!Op)
  203. {
  204. case op:
  205. return mixin("lhs" ~ op ~ "rhs");
  206. }
  207. }
  208. }
  209. );
  210. }
  211.  
  212. // Return a "pretty-printed" representation of expr
  213. string pprint(Expr expr)
  214. {
  215. import std.format : format;
  216.  
  217. return expr.match!(
  218. (double num) => "%g".format(num),
  219. (string var) => var,
  220. (BinOp bop) => "(%s %s %s)".format(
  221. pprint(*bop.lhs),
  222. cast(string) bop.op,
  223. pprint(*bop.rhs)
  224. )
  225. );
  226. }
  227.  
  228. Expr* myExpr = sum(var("a"), prod(num(2), var("b")));
  229. double[string] myEnv = ["a":3, "b":4, "c":7];
  230.  
  231. assert(eval(*myExpr, myEnv) == 11);
  232. assert(pprint(*myExpr) == "(a + (2 * b))");
  233. }
  234.  
  235. import std.format : FormatSpec, singleSpec;
  236. import std.meta : AliasSeq, Filter, IndexOf = staticIndexOf, Map = staticMap;
  237. import std.meta : NoDuplicates;
  238. import std.meta : anySatisfy, allSatisfy;
  239. import std.traits : hasElaborateCopyConstructor, hasElaborateDestructor;
  240. import std.traits : isAssignable, isCopyable, isStaticArray;
  241. import std.traits : ConstOf, ImmutableOf, InoutOf, TemplateArgsOf;
  242.  
  243. // FIXME: std.sumtype : `std.traits : DeducedParameterType` and `std.conv : toCtString`
  244. // are `package(std)` but trivial, hence copied below
  245. import std.traits : CommonType, /*DeducatedParameterType*/ Unqual;
  246. private template DeducedParameterType(T)
  247. {
  248. static if (is(T == U*, U) || is(T == U[], U))
  249. alias DeducedParameterType = Unqual!T;
  250. else
  251. alias DeducedParameterType = T;
  252. }
  253.  
  254. /// Compatibility with < v2.095.0
  255. private struct __InoutWorkaroundStruct{}
  256. private @property T rvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init);
  257. private @property ref T lvalueOf(T)(inout __InoutWorkaroundStruct = __InoutWorkaroundStruct.init);
  258. private enum isRvalueAssignable(Lhs, Rhs = Lhs) = __traits(compiles, { lvalueOf!Lhs = rvalueOf!Rhs; });
  259.  
  260. import std.typecons : ReplaceTypeUnless;
  261. import std.typecons : Flag;
  262. //import std.conv : toCtString;
  263. private enum toCtString(ulong n) = n.stringof[0 .. $ - "LU".length];
  264.  
  265. /// Placeholder used to refer to the enclosing [SumType].
  266. struct This {}
  267.  
  268. // True if a variable of type T can appear on the lhs of an assignment
  269. private enum isAssignableTo(T) =
  270. isAssignable!T || (!isCopyable!T && isRvalueAssignable!T);
  271.  
  272. // toHash is required by the language spec to be nothrow and @safe
  273. private enum isHashable(T) = __traits(compiles,
  274. () nothrow @safe { hashOf(T.init); }
  275. );
  276.  
  277. private enum hasPostblit(T) = __traits(hasPostblit, T);
  278.  
  279. private enum isInout(T) = is(T == inout);
  280.  
  281. /**
  282. * A [tagged union](https://en.wikipedia.org/wiki/Tagged_union) that can hold a
  283. * single value from any of a specified set of types.
  284. *
  285. * The value in a `SumType` can be operated on using [pattern matching][match].
  286. *
  287. * To avoid ambiguity, duplicate types are not allowed (but see the
  288. * ["basic usage" example](#basic-usage) for a workaround).
  289. *
  290. * The special type `This` can be used as a placeholder to create
  291. * self-referential types, just like with `Algebraic`. See the
  292. * ["Arithmetic expression evaluator" example](#arithmetic-expression-evaluator) for
  293. * usage.
  294. *
  295. * A `SumType` is initialized by default to hold the `.init` value of its
  296. * first member type, just like a regular union. The version identifier
  297. * `SumTypeNoDefaultCtor` can be used to disable this behavior.
  298. *
  299. * See_Also: $(REF Algebraic, std,variant)
  300. */
  301. struct SumType(Types...)
  302. if (is(NoDuplicates!Types == Types) && Types.length > 0)
  303. {
  304. /// The types a `SumType` can hold.
  305. alias Types = AliasSeq!(
  306. ReplaceTypeUnless!(isSumTypeInstance, This, typeof(this), TemplateArgsOf!SumType)
  307. );
  308.  
  309. private:
  310.  
  311. enum bool canHoldTag(T) = Types.length <= T.max;
  312. alias unsignedInts = AliasSeq!(ubyte, ushort, uint, ulong);
  313.  
  314. alias Tag = Filter!(canHoldTag, unsignedInts)[0];
  315.  
  316. union Storage
  317. {
  318. // Workaround for https://issues.dlang.org/show_bug.cgi?id=20068
  319. template memberName(T)
  320. if (IndexOf!(T, Types) >= 0)
  321. {
  322. enum tid = IndexOf!(T, Types);
  323. mixin("enum memberName = `values_", toCtString!tid, "`;");
  324. }
  325.  
  326. static foreach (T; Types)
  327. {
  328. mixin("T ", memberName!T, ";");
  329. }
  330. }
  331.  
  332. Storage storage;
  333. Tag tag;
  334.  
  335. /* Accesses the value stored in a SumType.
  336. *
  337. * This method is memory-safe, provided that:
  338. *
  339. * 1. A SumType's tag is always accurate.
  340. * 2. A SumType cannot be assigned to in @safe code if that assignment
  341. * could cause unsafe aliasing.
  342. *
  343. * All code that accesses a SumType's tag or storage directly, including
  344. * @safe code in this module, must be manually checked to ensure that it
  345. * does not violate either of the above requirements.
  346. */
  347. @trusted
  348. ref inout(T) get(T)() inout
  349. if (IndexOf!(T, Types) >= 0)
  350. {
  351. enum tid = IndexOf!(T, Types);
  352. assert(tag == tid,
  353. "This `" ~ SumType.stringof ~
  354. "` does not contain a(n) `" ~ T.stringof ~ "`"
  355. );
  356. return __traits(getMember, storage, Storage.memberName!T);
  357. }
  358.  
  359. public:
  360.  
  361. // Workaround for https://issues.dlang.org/show_bug.cgi?id=21399
  362. version (StdDdoc)
  363. {
  364. // Dummy type to stand in for loop variable
  365. private struct T;
  366.  
  367. /// Constructs a `SumType` holding a specific value.
  368. this(T value);
  369.  
  370. /// ditto
  371. this(const(T) value) const;
  372.  
  373. /// ditto
  374. this(immutable(T) value) immutable;
  375.  
  376. /// ditto
  377. this(Value)(Value value) inout
  378. if (is(Value == DeducedParameterType!(inout(T))));
  379. }
  380.  
  381. static foreach (tid, T; Types)
  382. {
  383. /// Constructs a `SumType` holding a specific value.
  384. this(T value)
  385. {
  386. import core.lifetime : forward;
  387.  
  388. static if (isCopyable!T)
  389. {
  390. // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
  391. if (__ctfe)
  392. __traits(getMember, storage, Storage.memberName!T) = value;
  393. else
  394. __traits(getMember, storage, Storage.memberName!T) = forward!value;
  395. }
  396. else
  397. {
  398. __traits(getMember, storage, Storage.memberName!T) = forward!value;
  399. }
  400.  
  401. tag = tid;
  402. }
  403.  
  404. // DUB: Those traits compile work around bugs in < v2.098
  405. static if (!__traits(compiles, { T c = const(T).init; }))
  406. {
  407. static if (isCopyable!(const(T)))
  408. {
  409. static if (IndexOf!(const(T), Map!(ConstOf, Types)) == tid)
  410. {
  411. /// ditto
  412. this(const(T) value) const
  413. {
  414. __traits(getMember, storage, Storage.memberName!T) = value;
  415. tag = tid;
  416. }
  417. }
  418. }
  419. else
  420. {
  421. @disable this(const(T) value) const;
  422. }
  423. }
  424.  
  425. static if (!__traits(compiles, { T c = immutable(T).init; }))
  426. {
  427. static if (isCopyable!(immutable(T)))
  428. {
  429. static if (IndexOf!(immutable(T), Map!(ImmutableOf, Types)) == tid)
  430. {
  431. /// ditto
  432. this(immutable(T) value) immutable
  433. {
  434. __traits(getMember, storage, Storage.memberName!T) = value;
  435. tag = tid;
  436. }
  437. }
  438. }
  439. else
  440. {
  441. @disable this(immutable(T) value) immutable;
  442. }
  443. }
  444.  
  445. static if (isCopyable!(inout(T)))
  446. {
  447. static if (IndexOf!(inout(T), Map!(InoutOf, Types)) == tid)
  448. {
  449. /// ditto
  450. this(Value)(Value value) inout
  451. if (is(Value == DeducedParameterType!(inout(T))))
  452. {
  453. __traits(getMember, storage, Storage.memberName!T) = value;
  454. tag = tid;
  455. }
  456. }
  457. }
  458. else
  459. {
  460. @disable this(Value)(Value value) inout
  461. if (is(Value == DeducedParameterType!(inout(T))));
  462. }
  463. }
  464.  
  465. static if (anySatisfy!(hasElaborateCopyConstructor, Types))
  466. {
  467. static if
  468. (
  469. allSatisfy!(isCopyable, Map!(InoutOf, Types))
  470. && !anySatisfy!(hasPostblit, Map!(InoutOf, Types))
  471. && allSatisfy!(isInout, Map!(InoutOf, Types))
  472. )
  473. {
  474. /// Constructs a `SumType` that's a copy of another `SumType`.
  475. this(ref inout(SumType) other) inout
  476. {
  477. storage = other.match!((ref value) {
  478. alias OtherTypes = Map!(InoutOf, Types);
  479. enum tid = IndexOf!(typeof(value), OtherTypes);
  480. alias T = Types[tid];
  481.  
  482. mixin("inout(Storage) newStorage = { ",
  483. Storage.memberName!T, ": value",
  484. " };");
  485.  
  486. return newStorage;
  487. });
  488.  
  489. tag = other.tag;
  490. }
  491. }
  492. else
  493. {
  494. static if (allSatisfy!(isCopyable, Types))
  495. {
  496. /// ditto
  497. this(ref SumType other)
  498. {
  499. storage = other.match!((ref value) {
  500. alias T = typeof(value);
  501.  
  502. mixin("Storage newStorage = { ",
  503. Storage.memberName!T, ": value",
  504. " };");
  505.  
  506. return newStorage;
  507. });
  508.  
  509. tag = other.tag;
  510. }
  511. }
  512. else
  513. {
  514. @disable this(ref SumType other);
  515. }
  516.  
  517. static if (allSatisfy!(isCopyable, Map!(ConstOf, Types)))
  518. {
  519. /// ditto
  520. this(ref const(SumType) other) const
  521. {
  522. storage = other.match!((ref value) {
  523. alias OtherTypes = Map!(ConstOf, Types);
  524. enum tid = IndexOf!(typeof(value), OtherTypes);
  525. alias T = Types[tid];
  526.  
  527. mixin("const(Storage) newStorage = { ",
  528. Storage.memberName!T, ": value",
  529. " };");
  530.  
  531. return newStorage;
  532. });
  533.  
  534. tag = other.tag;
  535. }
  536. }
  537. else
  538. {
  539. @disable this(ref const(SumType) other) const;
  540. }
  541.  
  542. static if (allSatisfy!(isCopyable, Map!(ImmutableOf, Types)))
  543. {
  544. /// ditto
  545. this(ref immutable(SumType) other) immutable
  546. {
  547. storage = other.match!((ref value) {
  548. alias OtherTypes = Map!(ImmutableOf, Types);
  549. enum tid = IndexOf!(typeof(value), OtherTypes);
  550. alias T = Types[tid];
  551.  
  552. mixin("immutable(Storage) newStorage = { ",
  553. Storage.memberName!T, ": value",
  554. " };");
  555.  
  556. return newStorage;
  557. });
  558.  
  559. tag = other.tag;
  560. }
  561. }
  562. else
  563. {
  564. @disable this(ref immutable(SumType) other) immutable;
  565. }
  566. }
  567. }
  568.  
  569. version (SumTypeNoDefaultCtor)
  570. {
  571. @disable this();
  572. }
  573.  
  574. // Workaround for https://issues.dlang.org/show_bug.cgi?id=21399
  575. version (StdDdoc)
  576. {
  577. // Dummy type to stand in for loop variable
  578. private struct T;
  579.  
  580. /**
  581. * Assigns a value to a `SumType`.
  582. *
  583. * If any of the `SumType`'s members other than the one being assigned
  584. * to contain pointers or references, it is possible for the assignment
  585. * to cause memory corruption (see the
  586. * ["Memory corruption" example](#memory-corruption) below for an
  587. * illustration of how). Therefore, such assignments are considered
  588. * `@system`.
  589. *
  590. * An individual assignment can be `@trusted` if the caller can
  591. * guarantee that there are no outstanding references to any `SumType`
  592. * members that contain pointers or references at the time the
  593. * assignment occurs.
  594. *
  595. * Examples:
  596. *
  597. * $(DIVID memory-corruption, $(H3 Memory corruption))
  598. *
  599. * This example shows how assignment to a `SumType` can be used to
  600. * cause memory corruption in `@system` code. In `@safe` code, the
  601. * assignment `s = 123` would not be allowed.
  602. *
  603. * ---
  604. * SumType!(int*, int) s = new int;
  605. * s.tryMatch!(
  606. * (ref int* p) {
  607. * s = 123; // overwrites `p`
  608. * return *p; // undefined behavior
  609. * }
  610. * );
  611. * ---
  612. */
  613. ref SumType opAssign(T rhs);
  614. }
  615.  
  616. static foreach (tid, T; Types)
  617. {
  618. static if (isAssignableTo!T)
  619. {
  620. /**
  621. * Assigns a value to a `SumType`.
  622. *
  623. * If any of the `SumType`'s members other than the one being assigned
  624. * to contain pointers or references, it is possible for the assignment
  625. * to cause memory corruption (see the
  626. * ["Memory corruption" example](#memory-corruption) below for an
  627. * illustration of how). Therefore, such assignments are considered
  628. * `@system`.
  629. *
  630. * An individual assignment can be `@trusted` if the caller can
  631. * guarantee that there are no outstanding references to any `SumType`
  632. * members that contain pointers or references at the time the
  633. * assignment occurs.
  634. *
  635. * Examples:
  636. *
  637. * $(DIVID memory-corruption, $(H3 Memory corruption))
  638. *
  639. * This example shows how assignment to a `SumType` can be used to
  640. * cause memory corruption in `@system` code. In `@safe` code, the
  641. * assignment `s = 123` would not be allowed.
  642. *
  643. * ---
  644. * SumType!(int*, int) s = new int;
  645. * s.tryMatch!(
  646. * (ref int* p) {
  647. * s = 123; // overwrites `p`
  648. * return *p; // undefined behavior
  649. * }
  650. * );
  651. * ---
  652. */
  653. ref SumType opAssign(T rhs)
  654. {
  655. import core.lifetime : forward;
  656. import std.traits : hasIndirections, hasNested;
  657. import std.meta : AliasSeq, Or = templateOr;
  658.  
  659. alias OtherTypes =
  660. AliasSeq!(Types[0 .. tid], Types[tid + 1 .. $]);
  661. enum unsafeToOverwrite =
  662. anySatisfy!(Or!(hasIndirections, hasNested), OtherTypes);
  663.  
  664. static if (unsafeToOverwrite)
  665. {
  666. cast(void) () @system {}();
  667. }
  668.  
  669. this.match!destroyIfOwner;
  670.  
  671. static if (isCopyable!T)
  672. {
  673. // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
  674. mixin("Storage newStorage = { ",
  675. Storage.memberName!T, ": __ctfe ? rhs : forward!rhs",
  676. " };");
  677. }
  678. else
  679. {
  680. mixin("Storage newStorage = { ",
  681. Storage.memberName!T, ": forward!rhs",
  682. " };");
  683. }
  684.  
  685. storage = newStorage;
  686. tag = tid;
  687.  
  688. return this;
  689. }
  690. }
  691. }
  692.  
  693. static if (allSatisfy!(isAssignableTo, Types))
  694. {
  695. static if (allSatisfy!(isCopyable, Types))
  696. {
  697. /**
  698. * Copies the value from another `SumType` into this one.
  699. *
  700. * See the value-assignment overload for details on `@safe`ty.
  701. *
  702. * Copy assignment is `@disable`d if any of `Types` is non-copyable.
  703. */
  704. ref SumType opAssign(ref SumType rhs)
  705. {
  706. rhs.match!((ref value) { this = value; });
  707. return this;
  708. }
  709. }
  710. else
  711. {
  712. @disable ref SumType opAssign(ref SumType rhs);
  713. }
  714.  
  715. /**
  716. * Moves the value from another `SumType` into this one.
  717. *
  718. * See the value-assignment overload for details on `@safe`ty.
  719. */
  720. ref SumType opAssign(SumType rhs)
  721. {
  722. import core.lifetime : move;
  723.  
  724. rhs.match!((ref value) {
  725. static if (isCopyable!(typeof(value)))
  726. {
  727. // Workaround for https://issues.dlang.org/show_bug.cgi?id=21542
  728. this = __ctfe ? value : move(value);
  729. }
  730. else
  731. {
  732. this = move(value);
  733. }
  734. });
  735. return this;
  736. }
  737. }
  738.  
  739. /**
  740. * Compares two `SumType`s for equality.
  741. *
  742. * Two `SumType`s are equal if they are the same kind of `SumType`, they
  743. * contain values of the same type, and those values are equal.
  744. */
  745. bool opEquals(this This, Rhs)(auto ref Rhs rhs)
  746. if (!is(CommonType!(This, Rhs) == void))
  747. {
  748. static if (is(This == Rhs))
  749. {
  750. return AliasSeq!(this, rhs).match!((ref value, ref rhsValue) {
  751. static if (is(typeof(value) == typeof(rhsValue)))
  752. {
  753. return value == rhsValue;
  754. }
  755. else
  756. {
  757. return false;
  758. }
  759. });
  760. }
  761. else
  762. {
  763. alias CommonSumType = CommonType!(This, Rhs);
  764. return cast(CommonSumType) this == cast(CommonSumType) rhs;
  765. }
  766. }
  767.  
  768. // Workaround for https://issues.dlang.org/show_bug.cgi?id=19407
  769. static if (__traits(compiles, anySatisfy!(hasElaborateDestructor, Types)))
  770. {
  771. // If possible, include the destructor only when it's needed
  772. private enum includeDtor = anySatisfy!(hasElaborateDestructor, Types);
  773. }
  774. else
  775. {
  776. // If we can't tell, always include it, even when it does nothing
  777. private enum includeDtor = true;
  778. }
  779.  
  780. static if (includeDtor)
  781. {
  782. /// Calls the destructor of the `SumType`'s current value.
  783. ~this()
  784. {
  785. this.match!destroyIfOwner;
  786. }
  787. }
  788.  
  789. invariant
  790. {
  791. this.match!((ref value) {
  792. static if (is(typeof(value) == class))
  793. {
  794. if (value !is null)
  795. {
  796. assert(value);
  797. }
  798. }
  799. else static if (is(typeof(value) == struct))
  800. {
  801. assert(&value);
  802. }
  803. });
  804. }
  805.  
  806. // Workaround for https://issues.dlang.org/show_bug.cgi?id=21400
  807. version (StdDdoc)
  808. {
  809. /**
  810. * Returns a string representation of the `SumType`'s current value.
  811. *
  812. * Not available when compiled with `-betterC`.
  813. */
  814. string toString(this This)();
  815.  
  816. /**
  817. * Handles formatted writing of the `SumType`'s current value.
  818. *
  819. * Not available when compiled with `-betterC`.
  820. *
  821. * Params:
  822. * sink = Output range to write to.
  823. * fmt = Format specifier to use.
  824. *
  825. * See_Also: $(REF formatValue, std,format)
  826. */
  827. void toString(this This, Sink, Char)(ref Sink sink, const ref FormatSpec!Char fmt);
  828. }
  829.  
  830. version (D_BetterC) {} else
  831. /**
  832. * Returns a string representation of the `SumType`'s current value.
  833. *
  834. * Not available when compiled with `-betterC`.
  835. */
  836. string toString(this This)()
  837. {
  838. import std.conv : to;
  839.  
  840. return this.match!(to!string);
  841. }
  842.  
  843. version (D_BetterC) {} else
  844. /**
  845. * Handles formatted writing of the `SumType`'s current value.
  846. *
  847. * Not available when compiled with `-betterC`.
  848. *
  849. * Params:
  850. * sink = Output range to write to.
  851. * fmt = Format specifier to use.
  852. *
  853. * See_Also: $(REF formatValue, std,format)
  854. */
  855. void toString(this This, Sink, Char)(ref Sink sink, const ref FormatSpec!Char fmt)
  856. {
  857. import std.format.write : formatValue;
  858.  
  859. this.match!((ref value) {
  860. formatValue(sink, value, fmt);
  861. });
  862. }
  863.  
  864. static if (allSatisfy!(isHashable, Map!(ConstOf, Types)))
  865. {
  866. // Workaround for https://issues.dlang.org/show_bug.cgi?id=21400
  867. version (StdDdoc)
  868. {
  869. /**
  870. * Returns the hash of the `SumType`'s current value.
  871. *
  872. * Not available when compiled with `-betterC`.
  873. */
  874. size_t toHash() const;
  875. }
  876.  
  877. // Workaround for https://issues.dlang.org/show_bug.cgi?id=20095
  878. version (D_BetterC) {} else
  879. /**
  880. * Returns the hash of the `SumType`'s current value.
  881. *
  882. * Not available when compiled with `-betterC`.
  883. */
  884. size_t toHash() const
  885. {
  886. return this.match!hashOf;
  887. }
  888. }
  889. }
  890.  
  891. // Construction
  892. @safe unittest
  893. {
  894. alias MySum = SumType!(int, float);
  895.  
  896. MySum x = MySum(42);
  897. MySum y = MySum(3.14);
  898. }
  899.  
  900. // Assignment
  901. @safe unittest
  902. {
  903. alias MySum = SumType!(int, float);
  904.  
  905. MySum x = MySum(42);
  906. x = 3.14;
  907. }
  908.  
  909. // Self assignment
  910. @safe unittest
  911. {
  912. alias MySum = SumType!(int, float);
  913.  
  914. MySum x = MySum(42);
  915. MySum y = MySum(3.14);
  916. y = x;
  917. }
  918.  
  919. // Equality
  920. @safe unittest
  921. {
  922. alias MySum = SumType!(int, float);
  923.  
  924. assert(MySum(123) == MySum(123));
  925. assert(MySum(123) != MySum(456));
  926. assert(MySum(123) != MySum(123.0));
  927. assert(MySum(123) != MySum(456.0));
  928.  
  929. }
  930.  
  931. // Equality of differently-qualified SumTypes
  932. // Disabled in BetterC due to use of dynamic arrays
  933. version (D_BetterC) {} else
  934. @safe unittest
  935. {
  936. alias SumA = SumType!(int, float);
  937. alias SumB = SumType!(const(int[]), int[]);
  938. alias SumC = SumType!(int[], const(int[]));
  939.  
  940. int[] ma = [1, 2, 3];
  941. const(int[]) ca = [1, 2, 3];
  942.  
  943. assert(const(SumA)(123) == SumA(123));
  944. assert(const(SumB)(ma[]) == SumB(ca[]));
  945. assert(const(SumC)(ma[]) == SumC(ca[]));
  946. }
  947.  
  948. // Imported types
  949. @safe unittest
  950. {
  951. import std.typecons : Tuple;
  952.  
  953. alias MySum = SumType!(Tuple!(int, int));
  954. }
  955.  
  956. // const and immutable types
  957. @safe unittest
  958. {
  959. alias MySum = SumType!(const(int[]), immutable(float[]));
  960. }
  961.  
  962. // Recursive types
  963. @safe unittest
  964. {
  965. alias MySum = SumType!(This*);
  966. assert(is(MySum.Types[0] == MySum*));
  967. }
  968.  
  969. // Allowed types
  970. @safe unittest
  971. {
  972. import std.meta : AliasSeq;
  973.  
  974. alias MySum = SumType!(int, float, This*);
  975.  
  976. assert(is(MySum.Types == AliasSeq!(int, float, MySum*)));
  977. }
  978.  
  979. // Types with destructors and postblits
  980. @system unittest
  981. {
  982. int copies;
  983.  
  984. static struct Test
  985. {
  986. bool initialized = false;
  987. int* copiesPtr;
  988.  
  989. this(this) { (*copiesPtr)++; }
  990. ~this() { if (initialized) (*copiesPtr)--; }
  991. }
  992.  
  993. alias MySum = SumType!(int, Test);
  994.  
  995. Test t = Test(true, &copies);
  996.  
  997. {
  998. MySum x = t;
  999. assert(copies == 1);
  1000. }
  1001. assert(copies == 0);
  1002.  
  1003. {
  1004. MySum x = 456;
  1005. assert(copies == 0);
  1006. }
  1007. assert(copies == 0);
  1008.  
  1009. {
  1010. MySum x = t;
  1011. assert(copies == 1);
  1012. x = 456;
  1013. assert(copies == 0);
  1014. }
  1015.  
  1016. {
  1017. MySum x = 456;
  1018. assert(copies == 0);
  1019. x = t;
  1020. assert(copies == 1);
  1021. }
  1022.  
  1023. {
  1024. MySum x = t;
  1025. MySum y = x;
  1026. assert(copies == 2);
  1027. }
  1028.  
  1029. {
  1030. MySum x = t;
  1031. MySum y;
  1032. y = x;
  1033. assert(copies == 2);
  1034. }
  1035. }
  1036.  
  1037. // Doesn't destroy reference types
  1038. // Disabled in BetterC due to use of classes
  1039. version (D_BetterC) {} else
  1040. @system unittest
  1041. {
  1042. bool destroyed;
  1043.  
  1044. class C
  1045. {
  1046. ~this()
  1047. {
  1048. destroyed = true;
  1049. }
  1050. }
  1051.  
  1052. struct S
  1053. {
  1054. ~this() {}
  1055. }
  1056.  
  1057. alias MySum = SumType!(S, C);
  1058.  
  1059. C c = new C();
  1060. {
  1061. MySum x = c;
  1062. destroyed = false;
  1063. }
  1064. assert(!destroyed);
  1065.  
  1066. {
  1067. MySum x = c;
  1068. destroyed = false;
  1069. x = S();
  1070. assert(!destroyed);
  1071. }
  1072. }
  1073.  
  1074. // Types with @disable this()
  1075. @safe unittest
  1076. {
  1077. static struct NoInit
  1078. {
  1079. @disable this();
  1080. }
  1081.  
  1082. alias MySum = SumType!(NoInit, int);
  1083.  
  1084. assert(!__traits(compiles, MySum()));
  1085. auto _ = MySum(42);
  1086. }
  1087.  
  1088. // const SumTypes
  1089. version (D_BetterC) {} else // not @nogc, https://issues.dlang.org/show_bug.cgi?id=22117
  1090. @safe unittest
  1091. {
  1092. auto _ = const(SumType!(int[]))([1, 2, 3]);
  1093. }
  1094.  
  1095. // Equality of const SumTypes
  1096. @safe unittest
  1097. {
  1098. alias MySum = SumType!int;
  1099.  
  1100. auto _ = const(MySum)(123) == const(MySum)(456);
  1101. }
  1102.  
  1103. // Compares reference types using value equality
  1104. @safe unittest
  1105. {
  1106. import std.array : staticArray;
  1107.  
  1108. static struct Field {}
  1109. static struct Struct { Field[] fields; }
  1110. alias MySum = SumType!Struct;
  1111.  
  1112. static arr1 = staticArray([Field()]);
  1113. static arr2 = staticArray([Field()]);
  1114.  
  1115. auto a = MySum(Struct(arr1[]));
  1116. auto b = MySum(Struct(arr2[]));
  1117.  
  1118. assert(a == b);
  1119. }
  1120.  
  1121. // toString
  1122. // Disabled in BetterC due to use of std.conv.text
  1123. version (D_BetterC) {} else
  1124. @safe unittest
  1125. {
  1126. import std.conv : text;
  1127.  
  1128. static struct Int { int i; }
  1129. static struct Double { double d; }
  1130. alias Sum = SumType!(Int, Double);
  1131.  
  1132. assert(Sum(Int(42)).text == Int(42).text, Sum(Int(42)).text);
  1133. assert(Sum(Double(33.3)).text == Double(33.3).text, Sum(Double(33.3)).text);
  1134. assert((const(Sum)(Int(42))).text == (const(Int)(42)).text, (const(Sum)(Int(42))).text);
  1135. }
  1136.  
  1137. // string formatting
  1138. // Disabled in BetterC due to use of std.format.format
  1139. version (D_BetterC) {} else
  1140. @safe unittest
  1141. {
  1142. import std.format : format;
  1143.  
  1144. SumType!int x = 123;
  1145.  
  1146. assert(format!"%s"(x) == format!"%s"(123));
  1147. assert(format!"%x"(x) == format!"%x"(123));
  1148. }
  1149.  
  1150. // string formatting of qualified SumTypes
  1151. // Disabled in BetterC due to use of std.format.format and dynamic arrays
  1152. version (D_BetterC) {} else
  1153. @safe unittest
  1154. {
  1155. import std.format : format;
  1156.  
  1157. int[] a = [1, 2, 3];
  1158. const(SumType!(int[])) x = a;
  1159.  
  1160. assert(format!"%(%d, %)"(x) == format!"%(%s, %)"(a));
  1161. }
  1162.  
  1163. // Github issue #16
  1164. // Disabled in BetterC due to use of dynamic arrays
  1165. version (D_BetterC) {} else
  1166. @safe unittest
  1167. {
  1168. alias Node = SumType!(This[], string);
  1169.  
  1170. // override inference of @system attribute for cyclic functions
  1171. assert((() @trusted =>
  1172. Node([Node([Node("x")])])
  1173. ==
  1174. Node([Node([Node("x")])])
  1175. )());
  1176. }
  1177.  
  1178. // Github issue #16 with const
  1179. // Disabled in BetterC due to use of dynamic arrays
  1180. version (D_BetterC) {} else
  1181. @safe unittest
  1182. {
  1183. alias Node = SumType!(const(This)[], string);
  1184.  
  1185. // override inference of @system attribute for cyclic functions
  1186. assert((() @trusted =>
  1187. Node([Node([Node("x")])])
  1188. ==
  1189. Node([Node([Node("x")])])
  1190. )());
  1191. }
  1192.  
  1193. // Stale pointers
  1194. // Disabled in BetterC due to use of dynamic arrays
  1195. version (D_BetterC) {} else
  1196. @system unittest
  1197. {
  1198. alias MySum = SumType!(ubyte, void*[2]);
  1199.  
  1200. MySum x = [null, cast(void*) 0x12345678];
  1201. void** p = &x.get!(void*[2])[1];
  1202. x = ubyte(123);
  1203.  
  1204. assert(*p != cast(void*) 0x12345678);
  1205. }
  1206.  
  1207. // Exception-safe assignment
  1208. // Disabled in BetterC due to use of exceptions
  1209. version (D_BetterC) {} else
  1210. @safe unittest
  1211. {
  1212. static struct A
  1213. {
  1214. int value = 123;
  1215. }
  1216.  
  1217. static struct B
  1218. {
  1219. int value = 456;
  1220. this(this) { throw new Exception("oops"); }
  1221. }
  1222.  
  1223. alias MySum = SumType!(A, B);
  1224.  
  1225. MySum x;
  1226. try
  1227. {
  1228. x = B();
  1229. }
  1230. catch (Exception e) {}
  1231.  
  1232. assert(
  1233. (x.tag == 0 && x.get!A.value == 123) ||
  1234. (x.tag == 1 && x.get!B.value == 456)
  1235. );
  1236. }
  1237.  
  1238. // Types with @disable this(this)
  1239. @safe unittest
  1240. {
  1241. import core.lifetime : move;
  1242.  
  1243. static struct NoCopy
  1244. {
  1245. @disable this(this);
  1246. }
  1247.  
  1248. alias MySum = SumType!NoCopy;
  1249.  
  1250. NoCopy lval = NoCopy();
  1251.  
  1252. MySum x = NoCopy();
  1253. MySum y = NoCopy();
  1254.  
  1255.  
  1256. assert(!__traits(compiles, SumType!NoCopy(lval)));
  1257.  
  1258. y = NoCopy();
  1259. y = move(x);
  1260. assert(!__traits(compiles, y = lval));
  1261. assert(!__traits(compiles, y = x));
  1262.  
  1263. bool b = x == y;
  1264. }
  1265.  
  1266. // Github issue #22
  1267. // Disabled in BetterC due to use of std.typecons.Nullable
  1268. version (D_BetterC) {} else
  1269. @safe unittest
  1270. {
  1271. import std.typecons;
  1272.  
  1273. static struct A
  1274. {
  1275. SumType!(Nullable!int) a = Nullable!int.init;
  1276. }
  1277. }
  1278.  
  1279. // Static arrays of structs with postblits
  1280. // Disabled in BetterC due to use of dynamic arrays
  1281. version (D_BetterC) {} else
  1282. @safe unittest
  1283. {
  1284. static struct S
  1285. {
  1286. int n;
  1287. this(this) { n++; }
  1288. }
  1289.  
  1290. SumType!(S[1]) x = [S(0)];
  1291. SumType!(S[1]) y = x;
  1292.  
  1293. auto xval = x.get!(S[1])[0].n;
  1294. auto yval = y.get!(S[1])[0].n;
  1295.  
  1296. assert(xval != yval);
  1297. }
  1298.  
  1299. // Replacement does not happen inside SumType
  1300. // Disabled in BetterC due to use of associative arrays
  1301. version (D_BetterC) {} else
  1302. @safe unittest
  1303. {
  1304. import std.typecons : Tuple, ReplaceTypeUnless;
  1305. alias A = Tuple!(This*,SumType!(This*))[SumType!(This*,string)[This]];
  1306. alias TR = ReplaceTypeUnless!(isSumTypeInstance, This, int, A);
  1307. static assert(is(TR == Tuple!(int*,SumType!(This*))[SumType!(This*, string)[int]]));
  1308. }
  1309.  
  1310. // Supports nested self-referential SumTypes
  1311. @safe unittest
  1312. {
  1313. import std.typecons : Tuple, Flag;
  1314. alias Nat = SumType!(Flag!"0", Tuple!(This*));
  1315. alias Inner = SumType!Nat;
  1316. alias Outer = SumType!(Nat*, Tuple!(This*, This*));
  1317. }
  1318.  
  1319. // Self-referential SumTypes inside Algebraic
  1320. // Disabled in BetterC due to use of std.variant.Algebraic
  1321. version (D_BetterC) {} else
  1322. @safe unittest
  1323. {
  1324. import std.variant : Algebraic;
  1325.  
  1326. alias T = Algebraic!(SumType!(This*));
  1327.  
  1328. assert(is(T.AllowedTypes[0].Types[0] == T.AllowedTypes[0]*));
  1329. }
  1330.  
  1331. // Doesn't call @system postblits in @safe code
  1332. @safe unittest
  1333. {
  1334. static struct SystemCopy { @system this(this) {} }
  1335. SystemCopy original;
  1336.  
  1337. assert(!__traits(compiles, () @safe
  1338. {
  1339. SumType!SystemCopy copy = original;
  1340. }));
  1341.  
  1342. assert(!__traits(compiles, () @safe
  1343. {
  1344. SumType!SystemCopy copy; copy = original;
  1345. }));
  1346. }
  1347.  
  1348. // Doesn't overwrite pointers in @safe code
  1349. @safe unittest
  1350. {
  1351. alias MySum = SumType!(int*, int);
  1352.  
  1353. MySum x;
  1354.  
  1355. assert(!__traits(compiles, () @safe
  1356. {
  1357. x = 123;
  1358. }));
  1359.  
  1360. assert(!__traits(compiles, () @safe
  1361. {
  1362. x = MySum(123);
  1363. }));
  1364. }
  1365.  
  1366. // Types with invariants
  1367. // Disabled in BetterC due to use of exceptions
  1368. version (D_BetterC) {} else
  1369. version (D_Invariants)
  1370. @system unittest
  1371. {
  1372. import std.exception : assertThrown;
  1373. import core.exception : AssertError;
  1374.  
  1375. struct S
  1376. {
  1377. int i;
  1378. invariant { assert(i >= 0); }
  1379. }
  1380.  
  1381. class C
  1382. {
  1383. int i;
  1384. invariant { assert(i >= 0); }
  1385. }
  1386.  
  1387. SumType!S x;
  1388. x.match!((ref v) { v.i = -1; });
  1389. assertThrown!AssertError(assert(&x));
  1390.  
  1391. SumType!C y = new C();
  1392. y.match!((ref v) { v.i = -1; });
  1393. assertThrown!AssertError(assert(&y));
  1394. }
  1395.  
  1396. // Calls value postblit on self-assignment
  1397. @safe unittest
  1398. {
  1399. static struct S
  1400. {
  1401. int n;
  1402. this(this) { n++; }
  1403. }
  1404.  
  1405. SumType!S x = S();
  1406. SumType!S y;
  1407. y = x;
  1408.  
  1409. auto xval = x.get!S.n;
  1410. auto yval = y.get!S.n;
  1411.  
  1412. assert(xval != yval);
  1413. }
  1414.  
  1415. // Github issue #29
  1416. @safe unittest
  1417. {
  1418. alias A = SumType!string;
  1419.  
  1420. @safe A createA(string arg)
  1421. {
  1422. return A(arg);
  1423. }
  1424.  
  1425. @safe void test()
  1426. {
  1427. A a = createA("");
  1428. }
  1429. }
  1430.  
  1431. // SumTypes as associative array keys
  1432. // Disabled in BetterC due to use of associative arrays
  1433. version (D_BetterC) {} else
  1434. @safe unittest
  1435. {
  1436. int[SumType!(int, string)] aa;
  1437. }
  1438.  
  1439. // toString with non-copyable types
  1440. // Disabled in BetterC due to use of std.conv.to (in toString)
  1441. version (D_BetterC) {} else
  1442. @safe unittest
  1443. {
  1444. struct NoCopy
  1445. {
  1446. @disable this(this);
  1447. }
  1448.  
  1449. SumType!NoCopy x;
  1450.  
  1451. auto _ = x.toString();
  1452. }
  1453.  
  1454. // Can use the result of assignment
  1455. @safe unittest
  1456. {
  1457. alias MySum = SumType!(int, float);
  1458.  
  1459. MySum a = MySum(123);
  1460. MySum b = MySum(3.14);
  1461.  
  1462. assert((a = b) == b);
  1463. assert((a = MySum(123)) == MySum(123));
  1464. assert((a = 3.14) == MySum(3.14));
  1465. assert(((a = b) = MySum(123)) == MySum(123));
  1466. }
  1467.  
  1468. // Types with copy constructors
  1469. @safe unittest
  1470. {
  1471. static struct S
  1472. {
  1473. int n;
  1474.  
  1475. this(ref return scope inout S other) inout
  1476. {
  1477. n = other.n + 1;
  1478. }
  1479. }
  1480.  
  1481. SumType!S x = S();
  1482. SumType!S y = x;
  1483.  
  1484. auto xval = x.get!S.n;
  1485. auto yval = y.get!S.n;
  1486.  
  1487. assert(xval != yval);
  1488. }
  1489.  
  1490. // Copyable by generated copy constructors
  1491. @safe unittest
  1492. {
  1493. static struct Inner
  1494. {
  1495. ref this(ref inout Inner other) {}
  1496. }
  1497.  
  1498. static struct Outer
  1499. {
  1500. SumType!Inner inner;
  1501. }
  1502.  
  1503. Outer x;
  1504. Outer y = x;
  1505. }
  1506.  
  1507. // Types with qualified copy constructors
  1508. @safe unittest
  1509. {
  1510. static struct ConstCopy
  1511. {
  1512. int n;
  1513. this(inout int n) inout { this.n = n; }
  1514. this(ref const typeof(this) other) const { this.n = other.n; }
  1515. }
  1516.  
  1517. static struct ImmutableCopy
  1518. {
  1519. int n;
  1520. this(inout int n) inout { this.n = n; }
  1521. this(ref immutable typeof(this) other) immutable { this.n = other.n; }
  1522. }
  1523.  
  1524. const SumType!ConstCopy x = const(ConstCopy)(1);
  1525. immutable SumType!ImmutableCopy y = immutable(ImmutableCopy)(1);
  1526. }
  1527.  
  1528. // Types with disabled opEquals
  1529. @safe unittest
  1530. {
  1531. static struct S
  1532. {
  1533. @disable bool opEquals(const S rhs) const;
  1534. }
  1535.  
  1536. auto _ = SumType!S(S());
  1537. }
  1538.  
  1539. // Types with non-const opEquals
  1540. @safe unittest
  1541. {
  1542. static struct S
  1543. {
  1544. int i;
  1545. bool opEquals(S rhs) { return i == rhs.i; }
  1546. }
  1547.  
  1548. auto _ = SumType!S(S(123));
  1549. }
  1550.  
  1551. // Incomparability of different SumTypes
  1552. @safe unittest
  1553. {
  1554. SumType!(int, string) x = 123;
  1555. SumType!(string, int) y = 123;
  1556.  
  1557. assert(!__traits(compiles, x != y));
  1558. }
  1559.  
  1560. // Self-reference in return/parameter type of function pointer member
  1561. // Disabled in BetterC due to use of delegates
  1562. version (D_BetterC) {} else
  1563. @safe unittest
  1564. {
  1565. alias T = SumType!(int, This delegate(This));
  1566. }
  1567.  
  1568. // Construction and assignment from implicitly-convertible lvalue
  1569. @safe unittest
  1570. {
  1571. alias MySum = SumType!bool;
  1572.  
  1573. const(bool) b = true;
  1574.  
  1575. MySum x = b;
  1576. MySum y; y = b;
  1577. }
  1578.  
  1579. // @safe assignment to the only pointer type in a SumType
  1580. @safe unittest
  1581. {
  1582. SumType!(string, int) sm = 123;
  1583. sm = "this should be @safe";
  1584. }
  1585.  
  1586. // Immutable member type with copy constructor
  1587. // https://issues.dlang.org/show_bug.cgi?id=22572
  1588. @safe unittest
  1589. {
  1590. static struct CopyConstruct
  1591. {
  1592. this(ref inout CopyConstruct other) inout {}
  1593. }
  1594.  
  1595. static immutable struct Value
  1596. {
  1597. CopyConstruct c;
  1598. }
  1599.  
  1600. SumType!Value s;
  1601. }
  1602.  
  1603. // Construction of inout-qualified SumTypes
  1604. // https://issues.dlang.org/show_bug.cgi?id=22901
  1605. @safe unittest
  1606. {
  1607. static inout(SumType!(int[])) example(inout(int[]) arr)
  1608. {
  1609. return inout(SumType!(int[]))(arr);
  1610. }
  1611. }
  1612.  
  1613. // Assignment of struct with overloaded opAssign in CTFE
  1614. // https://issues.dlang.org/show_bug.cgi?id=23182
  1615. @safe unittest
  1616. {
  1617. static struct HasOpAssign
  1618. {
  1619. void opAssign(HasOpAssign rhs) {}
  1620. }
  1621.  
  1622. static SumType!HasOpAssign test()
  1623. {
  1624. SumType!HasOpAssign s;
  1625. // Test both overloads
  1626. s = HasOpAssign();
  1627. s = SumType!HasOpAssign();
  1628. return s;
  1629. }
  1630.  
  1631. // Force CTFE
  1632. enum result = test();
  1633. }
  1634.  
  1635. /// True if `T` is an instance of the `SumType` template, otherwise false.
  1636. private enum bool isSumTypeInstance(T) = is(T == SumType!Args, Args...);
  1637.  
  1638. @safe unittest
  1639. {
  1640. static struct Wrapper
  1641. {
  1642. SumType!int s;
  1643. alias s this;
  1644. }
  1645.  
  1646. assert(isSumTypeInstance!(SumType!int));
  1647. assert(!isSumTypeInstance!Wrapper);
  1648. }
  1649.  
  1650. /// True if `T` is a [SumType] or implicitly converts to one, otherwise false.
  1651. enum bool isSumType(T) = is(T : SumType!Args, Args...);
  1652.  
  1653. ///
  1654. @safe unittest
  1655. {
  1656. static struct ConvertsToSumType
  1657. {
  1658. SumType!int payload;
  1659. alias payload this;
  1660. }
  1661.  
  1662. static struct ContainsSumType
  1663. {
  1664. SumType!int payload;
  1665. }
  1666.  
  1667. assert(isSumType!(SumType!int));
  1668. assert(isSumType!ConvertsToSumType);
  1669. assert(!isSumType!ContainsSumType);
  1670. }
  1671.  
  1672. /**
  1673. * Calls a type-appropriate function with the value held in a [SumType].
  1674. *
  1675. * For each possible type the [SumType] can hold, the given handlers are
  1676. * checked, in order, to see whether they accept a single argument of that type.
  1677. * The first one that does is chosen as the match for that type. (Note that the
  1678. * first match may not always be the most exact match.
  1679. * See ["Avoiding unintentional matches"](#avoiding-unintentional-matches) for
  1680. * one common pitfall.)
  1681. *
  1682. * Every type must have a matching handler, and every handler must match at
  1683. * least one type. This is enforced at compile time.
  1684. *
  1685. * Handlers may be functions, delegates, or objects with `opCall` overloads. If
  1686. * a function with more than one overload is given as a handler, all of the
  1687. * overloads are considered as potential matches.
  1688. *
  1689. * Templated handlers are also accepted, and will match any type for which they
  1690. * can be [implicitly instantiated](https://dlang.org/glossary.html#ifti). See
  1691. * ["Introspection-based matching"](#introspection-based-matching) for an
  1692. * example of templated handler usage.
  1693. *
  1694. * If multiple [SumType]s are passed to match, their values are passed to the
  1695. * handlers as separate arguments, and matching is done for each possible
  1696. * combination of value types. See ["Multiple dispatch"](#multiple-dispatch) for
  1697. * an example.
  1698. *
  1699. * Returns:
  1700. * The value returned from the handler that matches the currently-held type.
  1701. *
  1702. * See_Also: $(REF visit, std,variant)
  1703. */
  1704. template match(handlers...)
  1705. {
  1706. import std.typecons : Yes;
  1707.  
  1708. /**
  1709. * The actual `match` function.
  1710. *
  1711. * Params:
  1712. * args = One or more [SumType] objects.
  1713. */
  1714. auto ref match(SumTypes...)(auto ref SumTypes args)
  1715. if (allSatisfy!(isSumType, SumTypes) && args.length > 0)
  1716. {
  1717. return matchImpl!(Yes.exhaustive, handlers)(args);
  1718. }
  1719. }
  1720.  
  1721. /** $(DIVID avoiding-unintentional-matches, $(H3 Avoiding unintentional matches))
  1722. *
  1723. * Sometimes, implicit conversions may cause a handler to match more types than
  1724. * intended. The example below shows two solutions to this problem.
  1725. */
  1726. @safe unittest
  1727. {
  1728. alias Number = SumType!(double, int);
  1729.  
  1730. Number x;
  1731.  
  1732. // Problem: because int implicitly converts to double, the double
  1733. // handler is used for both types, and the int handler never matches.
  1734. assert(!__traits(compiles,
  1735. x.match!(
  1736. (double d) => "got double",
  1737. (int n) => "got int"
  1738. )
  1739. ));
  1740.  
  1741. // Solution 1: put the handler for the "more specialized" type (in this
  1742. // case, int) before the handler for the type it converts to.
  1743. assert(__traits(compiles,
  1744. x.match!(
  1745. (int n) => "got int",
  1746. (double d) => "got double"
  1747. )
  1748. ));
  1749.  
  1750. // Solution 2: use a template that only accepts the exact type it's
  1751. // supposed to match, instead of any type that implicitly converts to it.
  1752. alias exactly(T, alias fun) = function (arg)
  1753. {
  1754. static assert(is(typeof(arg) == T));
  1755. return fun(arg);
  1756. };
  1757.  
  1758. // Now, even if we put the double handler first, it will only be used for
  1759. // doubles, not ints.
  1760. assert(__traits(compiles,
  1761. x.match!(
  1762. exactly!(double, d => "got double"),
  1763. exactly!(int, n => "got int")
  1764. )
  1765. ));
  1766. }
  1767.  
  1768. /** $(DIVID multiple-dispatch, $(H3 Multiple dispatch))
  1769. *
  1770. * Pattern matching can be performed on multiple `SumType`s at once by passing
  1771. * handlers with multiple arguments. This usually leads to more concise code
  1772. * than using nested calls to `match`, as show below.
  1773. */
  1774. @safe unittest
  1775. {
  1776. struct Point2D { double x, y; }
  1777. struct Point3D { double x, y, z; }
  1778.  
  1779. alias Point = SumType!(Point2D, Point3D);
  1780.  
  1781. version (none)
  1782. {
  1783. // This function works, but the code is ugly and repetitive.
  1784. // It uses three separate calls to match!
  1785. @safe pure nothrow @nogc
  1786. bool sameDimensions(Point p1, Point p2)
  1787. {
  1788. return p1.match!(
  1789. (Point2D _) => p2.match!(
  1790. (Point2D _) => true,
  1791. _ => false
  1792. ),
  1793. (Point3D _) => p2.match!(
  1794. (Point3D _) => true,
  1795. _ => false
  1796. )
  1797. );
  1798. }
  1799. }
  1800.  
  1801. // This version is much nicer.
  1802. @safe pure nothrow @nogc
  1803. bool sameDimensions(Point p1, Point p2)
  1804. {
  1805. alias doMatch = match!(
  1806. (Point2D _1, Point2D _2) => true,
  1807. (Point3D _1, Point3D _2) => true,
  1808. (_1, _2) => false
  1809. );
  1810.  
  1811. return doMatch(p1, p2);
  1812. }
  1813.  
  1814. Point a = Point2D(1, 2);
  1815. Point b = Point2D(3, 4);
  1816. Point c = Point3D(5, 6, 7);
  1817. Point d = Point3D(8, 9, 0);
  1818.  
  1819. assert( sameDimensions(a, b));
  1820. assert( sameDimensions(c, d));
  1821. assert(!sameDimensions(a, c));
  1822. assert(!sameDimensions(d, b));
  1823. }
  1824.  
  1825. /**
  1826. * Attempts to call a type-appropriate function with the value held in a
  1827. * [SumType], and throws on failure.
  1828. *
  1829. * Matches are chosen using the same rules as [match], but are not required to
  1830. * be exhaustive—in other words, a type (or combination of types) is allowed to
  1831. * have no matching handler. If a type without a handler is encountered at
  1832. * runtime, a [MatchException] is thrown.
  1833. *
  1834. * Not available when compiled with `-betterC`.
  1835. *
  1836. * Returns:
  1837. * The value returned from the handler that matches the currently-held type,
  1838. * if a handler was given for that type.
  1839. *
  1840. * Throws:
  1841. * [MatchException], if the currently-held type has no matching handler.
  1842. *
  1843. * See_Also: $(REF tryVisit, std,variant)
  1844. */
  1845. version (D_Exceptions)
  1846. template tryMatch(handlers...)
  1847. {
  1848. import std.typecons : No;
  1849.  
  1850. /**
  1851. * The actual `tryMatch` function.
  1852. *
  1853. * Params:
  1854. * args = One or more [SumType] objects.
  1855. */
  1856. auto ref tryMatch(SumTypes...)(auto ref SumTypes args)
  1857. if (allSatisfy!(isSumType, SumTypes) && args.length > 0)
  1858. {
  1859. return matchImpl!(No.exhaustive, handlers)(args);
  1860. }
  1861. }
  1862.  
  1863. /**
  1864. * Thrown by [tryMatch] when an unhandled type is encountered.
  1865. *
  1866. * Not available when compiled with `-betterC`.
  1867. */
  1868. version (D_Exceptions)
  1869. class MatchException : Exception
  1870. {
  1871. ///
  1872. pure @safe @nogc nothrow
  1873. this(string msg, string file = __FILE__, size_t line = __LINE__)
  1874. {
  1875. super(msg, file, line);
  1876. }
  1877. }
  1878.  
  1879. /**
  1880. * True if `handler` is a potential match for `Ts`, otherwise false.
  1881. *
  1882. * See the documentation for [match] for a full explanation of how matches are
  1883. * chosen.
  1884. */
  1885. template canMatch(alias handler, Ts...)
  1886. if (Ts.length > 0)
  1887. {
  1888. enum canMatch = is(typeof((ref Ts args) => handler(args)));
  1889. }
  1890.  
  1891. ///
  1892. @safe unittest
  1893. {
  1894. alias handleInt = (int i) => "got an int";
  1895.  
  1896. assert( canMatch!(handleInt, int));
  1897. assert(!canMatch!(handleInt, string));
  1898. }
  1899.  
  1900. // Includes all overloads of the given handler
  1901. @safe unittest
  1902. {
  1903. static struct OverloadSet
  1904. {
  1905. static void fun(int n) {}
  1906. static void fun(double d) {}
  1907. }
  1908.  
  1909. assert(canMatch!(OverloadSet.fun, int));
  1910. assert(canMatch!(OverloadSet.fun, double));
  1911. }
  1912.  
  1913. // Like aliasSeqOf!(iota(n)), but works in BetterC
  1914. private template Iota(size_t n)
  1915. {
  1916. static if (n == 0)
  1917. {
  1918. alias Iota = AliasSeq!();
  1919. }
  1920. else
  1921. {
  1922. alias Iota = AliasSeq!(Iota!(n - 1), n - 1);
  1923. }
  1924. }
  1925.  
  1926. @safe unittest
  1927. {
  1928. assert(is(Iota!0 == AliasSeq!()));
  1929. assert(Iota!1 == AliasSeq!(0));
  1930. assert(Iota!3 == AliasSeq!(0, 1, 2));
  1931. }
  1932.  
  1933. /* The number that the dim-th argument's tag is multiplied by when
  1934. * converting TagTuples to and from case indices ("caseIds").
  1935. *
  1936. * Named by analogy to the stride that the dim-th index into a
  1937. * multidimensional static array is multiplied by to calculate the
  1938. * offset of a specific element.
  1939. */
  1940. private size_t stride(size_t dim, lengths...)()
  1941. {
  1942. import core.checkedint : mulu;
  1943.  
  1944. size_t result = 1;
  1945. bool overflow = false;
  1946.  
  1947. static foreach (i; 0 .. dim)
  1948. {
  1949. result = mulu(result, lengths[i], overflow);
  1950. }
  1951.  
  1952. /* The largest number matchImpl uses, numCases, is calculated with
  1953. * stride!(SumTypes.length), so as long as this overflow check
  1954. * passes, we don't need to check for overflow anywhere else.
  1955. */
  1956. assert(!overflow, "Integer overflow");
  1957. return result;
  1958. }
  1959.  
  1960. private template matchImpl(Flag!"exhaustive" exhaustive, handlers...)
  1961. {
  1962. auto ref matchImpl(SumTypes...)(auto ref SumTypes args)
  1963. if (allSatisfy!(isSumType, SumTypes) && args.length > 0)
  1964. {
  1965. alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes));
  1966. alias TagTuple = .TagTuple!(SumTypes);
  1967.  
  1968. /*
  1969. * A list of arguments to be passed to a handler needed for the case
  1970. * labeled with `caseId`.
  1971. */
  1972. template handlerArgs(size_t caseId)
  1973. {
  1974. enum tags = TagTuple.fromCaseId(caseId);
  1975. enum argsFrom(size_t i : tags.length) = "";
  1976. enum argsFrom(size_t i) = "args[" ~ toCtString!i ~ "].get!(SumTypes[" ~ toCtString!i ~ "]" ~
  1977. ".Types[" ~ toCtString!(tags[i]) ~ "])(), " ~ argsFrom!(i + 1);
  1978. enum handlerArgs = argsFrom!0;
  1979. }
  1980.  
  1981. /* An AliasSeq of the types of the member values in the argument list
  1982. * returned by `handlerArgs!caseId`.
  1983. *
  1984. * Note that these are the actual (that is, qualified) types of the
  1985. * member values, which may not be the same as the types listed in
  1986. * the arguments' `.Types` properties.
  1987. */
  1988. template valueTypes(size_t caseId)
  1989. {
  1990. enum tags = TagTuple.fromCaseId(caseId);
  1991.  
  1992. template getType(size_t i)
  1993. {
  1994. enum tid = tags[i];
  1995. alias T = SumTypes[i].Types[tid];
  1996. alias getType = typeof(args[i].get!T());
  1997. }
  1998.  
  1999. alias valueTypes = Map!(getType, Iota!(tags.length));
  2000. }
  2001.  
  2002. /* The total number of cases is
  2003. *
  2004. * Π SumTypes[i].Types.length for 0 ≤ i < SumTypes.length
  2005. *
  2006. * Or, equivalently,
  2007. *
  2008. * ubyte[SumTypes[0].Types.length]...[SumTypes[$-1].Types.length].sizeof
  2009. *
  2010. * Conveniently, this is equal to stride!(SumTypes.length), so we can
  2011. * use that function to compute it.
  2012. */
  2013. enum numCases = stride!(SumTypes.length);
  2014.  
  2015. /* Guaranteed to never be a valid handler index, since
  2016. * handlers.length <= size_t.max.
  2017. */
  2018. enum noMatch = size_t.max;
  2019.  
  2020. // An array that maps caseIds to handler indices ("hids").
  2021. enum matches = ()
  2022. {
  2023. size_t[numCases] matches;
  2024.  
  2025. // Workaround for https://issues.dlang.org/show_bug.cgi?id=19561
  2026. foreach (ref match; matches)
  2027. {
  2028. match = noMatch;
  2029. }
  2030.  
  2031. static foreach (caseId; 0 .. numCases)
  2032. {
  2033. static foreach (hid, handler; handlers)
  2034. {
  2035. static if (canMatch!(handler, valueTypes!caseId))
  2036. {
  2037. if (matches[caseId] == noMatch)
  2038. {
  2039. matches[caseId] = hid;
  2040. }
  2041. }
  2042. }
  2043. }
  2044.  
  2045. return matches;
  2046. }();
  2047.  
  2048. import std.algorithm.searching : canFind;
  2049.  
  2050. // Check for unreachable handlers
  2051. static foreach (hid, handler; handlers)
  2052. {
  2053. static assert(matches[].canFind(hid),
  2054. "`handlers[" ~ toCtString!hid ~ "]` " ~
  2055. "of type `" ~ ( __traits(isTemplate, handler)
  2056. ? "template"
  2057. : typeof(handler).stringof
  2058. ) ~ "` " ~
  2059. "never matches"
  2060. );
  2061. }
  2062.  
  2063. // Workaround for https://issues.dlang.org/show_bug.cgi?id=19993
  2064. enum handlerName(size_t hid) = "handler" ~ toCtString!hid;
  2065.  
  2066. static foreach (size_t hid, handler; handlers)
  2067. {
  2068. mixin("alias ", handlerName!hid, " = handler;");
  2069. }
  2070.  
  2071. immutable argsId = TagTuple(args).toCaseId;
  2072.  
  2073. final switch (argsId)
  2074. {
  2075. static foreach (caseId; 0 .. numCases)
  2076. {
  2077. case caseId:
  2078. static if (matches[caseId] != noMatch)
  2079. {
  2080. return mixin(handlerName!(matches[caseId]), "(", handlerArgs!caseId, ")");
  2081. }
  2082. else
  2083. {
  2084. static if (exhaustive)
  2085. {
  2086. static assert(false,
  2087. "No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`");
  2088. }
  2089. else
  2090. {
  2091. throw new MatchException(
  2092. "No matching handler for types `" ~ valueTypes!caseId.stringof ~ "`");
  2093. }
  2094. }
  2095. }
  2096. }
  2097.  
  2098. assert(false, "unreachable");
  2099. }
  2100. }
  2101.  
  2102. private enum typeCount(SumType) = SumType.Types.length;
  2103.  
  2104. /* A TagTuple represents a single possible set of tags that `args`
  2105. * could have at runtime.
  2106. *
  2107. * Because D does not allow a struct to be the controlling expression
  2108. * of a switch statement, we cannot dispatch on the TagTuple directly.
  2109. * Instead, we must map each TagTuple to a unique integer and generate
  2110. * a case label for each of those integers.
  2111. *
  2112. * This mapping is implemented in `fromCaseId` and `toCaseId`. It uses
  2113. * the same technique that's used to map index tuples to memory offsets
  2114. * in a multidimensional static array.
  2115. *
  2116. * For example, when `args` consists of two SumTypes with two member
  2117. * types each, the TagTuples corresponding to each case label are:
  2118. *
  2119. * case 0: TagTuple([0, 0])
  2120. * case 1: TagTuple([1, 0])
  2121. * case 2: TagTuple([0, 1])
  2122. * case 3: TagTuple([1, 1])
  2123. *
  2124. * When there is only one argument, the caseId is equal to that
  2125. * argument's tag.
  2126. */
  2127. private struct TagTuple(SumTypes...)
  2128. {
  2129. size_t[SumTypes.length] tags;
  2130. alias tags this;
  2131.  
  2132. alias stride(size_t i) = .stride!(i, Map!(typeCount, SumTypes));
  2133.  
  2134. invariant
  2135. {
  2136. static foreach (i; 0 .. tags.length)
  2137. {
  2138. assert(tags[i] < SumTypes[i].Types.length, "Invalid tag");
  2139. }
  2140. }
  2141.  
  2142. this(ref const(SumTypes) args)
  2143. {
  2144. static foreach (i; 0 .. tags.length)
  2145. {
  2146. tags[i] = args[i].tag;
  2147. }
  2148. }
  2149.  
  2150. static TagTuple fromCaseId(size_t caseId)
  2151. {
  2152. TagTuple result;
  2153.  
  2154. // Most-significant to least-significant
  2155. static foreach_reverse (i; 0 .. result.length)
  2156. {
  2157. result[i] = caseId / stride!i;
  2158. caseId %= stride!i;
  2159. }
  2160.  
  2161. return result;
  2162. }
  2163.  
  2164. size_t toCaseId()
  2165. {
  2166. size_t result;
  2167.  
  2168. static foreach (i; 0 .. tags.length)
  2169. {
  2170. result += tags[i] * stride!i;
  2171. }
  2172.  
  2173. return result;
  2174. }
  2175. }
  2176.  
  2177. // Matching
  2178. @safe unittest
  2179. {
  2180. alias MySum = SumType!(int, float);
  2181.  
  2182. MySum x = MySum(42);
  2183. MySum y = MySum(3.14);
  2184.  
  2185. assert(x.match!((int v) => true, (float v) => false));
  2186. assert(y.match!((int v) => false, (float v) => true));
  2187. }
  2188.  
  2189. // Missing handlers
  2190. @safe unittest
  2191. {
  2192. alias MySum = SumType!(int, float);
  2193.  
  2194. MySum x = MySum(42);
  2195.  
  2196. assert(!__traits(compiles, x.match!((int x) => true)));
  2197. assert(!__traits(compiles, x.match!()));
  2198. }
  2199.  
  2200. // Handlers with qualified parameters
  2201. // Disabled in BetterC due to use of dynamic arrays
  2202. version (D_BetterC) {} else
  2203. @safe unittest
  2204. {
  2205. alias MySum = SumType!(int[], float[]);
  2206.  
  2207. MySum x = MySum([1, 2, 3]);
  2208. MySum y = MySum([1.0, 2.0, 3.0]);
  2209.  
  2210. assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false));
  2211. assert(y.match!((const(int[]) v) => false, (const(float[]) v) => true));
  2212. }
  2213.  
  2214. // Handlers for qualified types
  2215. // Disabled in BetterC due to use of dynamic arrays
  2216. version (D_BetterC) {} else
  2217. @safe unittest
  2218. {
  2219. alias MySum = SumType!(immutable(int[]), immutable(float[]));
  2220.  
  2221. MySum x = MySum([1, 2, 3]);
  2222.  
  2223. assert(x.match!((immutable(int[]) v) => true, (immutable(float[]) v) => false));
  2224. assert(x.match!((const(int[]) v) => true, (const(float[]) v) => false));
  2225. // Tail-qualified parameters
  2226. assert(x.match!((immutable(int)[] v) => true, (immutable(float)[] v) => false));
  2227. assert(x.match!((const(int)[] v) => true, (const(float)[] v) => false));
  2228. // Generic parameters
  2229. assert(x.match!((immutable v) => true));
  2230. assert(x.match!((const v) => true));
  2231. // Unqualified parameters
  2232. assert(!__traits(compiles,
  2233. x.match!((int[] v) => true, (float[] v) => false)
  2234. ));
  2235. }
  2236.  
  2237. // Delegate handlers
  2238. // Disabled in BetterC due to use of closures
  2239. version (D_BetterC) {} else
  2240. @safe unittest
  2241. {
  2242. alias MySum = SumType!(int, float);
  2243.  
  2244. int answer = 42;
  2245. MySum x = MySum(42);
  2246. MySum y = MySum(3.14);
  2247.  
  2248. assert(x.match!((int v) => v == answer, (float v) => v == answer));
  2249. assert(!y.match!((int v) => v == answer, (float v) => v == answer));
  2250. }
  2251.  
  2252. version (unittest)
  2253. {
  2254. version (D_BetterC)
  2255. {
  2256. // std.math.isClose depends on core.runtime.math, so use a
  2257. // libc-based version for testing with -betterC
  2258. @safe pure @nogc nothrow
  2259. private bool isClose(double lhs, double rhs)
  2260. {
  2261. import core.stdc.math : fabs;
  2262.  
  2263. return fabs(lhs - rhs) < 1e-5;
  2264. }
  2265. }
  2266. else
  2267. {
  2268. import std.math.operations : isClose;
  2269. }
  2270. }
  2271.  
  2272. // Generic handler
  2273. @safe unittest
  2274. {
  2275. alias MySum = SumType!(int, float);
  2276.  
  2277. MySum x = MySum(42);
  2278. MySum y = MySum(3.14);
  2279.  
  2280. assert(x.match!(v => v*2) == 84);
  2281. assert(y.match!(v => v*2).isClose(6.28));
  2282. }
  2283.  
  2284. // Fallback to generic handler
  2285. // Disabled in BetterC due to use of std.conv.to
  2286. version (D_BetterC) {} else
  2287. @safe unittest
  2288. {
  2289. import std.conv : to;
  2290.  
  2291. alias MySum = SumType!(int, float, string);
  2292.  
  2293. MySum x = MySum(42);
  2294. MySum y = MySum("42");
  2295.  
  2296. assert(x.match!((string v) => v.to!int, v => v*2) == 84);
  2297. assert(y.match!((string v) => v.to!int, v => v*2) == 42);
  2298. }
  2299.  
  2300. // Multiple non-overlapping generic handlers
  2301. @safe unittest
  2302. {
  2303. import std.array : staticArray;
  2304.  
  2305. alias MySum = SumType!(int, float, int[], char[]);
  2306.  
  2307. static ints = staticArray([1, 2, 3]);
  2308. static chars = staticArray(['a', 'b', 'c']);
  2309.  
  2310. MySum x = MySum(42);
  2311. MySum y = MySum(3.14);
  2312. MySum z = MySum(ints[]);
  2313. MySum w = MySum(chars[]);
  2314.  
  2315. assert(x.match!(v => v*2, v => v.length) == 84);
  2316. assert(y.match!(v => v*2, v => v.length).isClose(6.28));
  2317. assert(w.match!(v => v*2, v => v.length) == 3);
  2318. assert(z.match!(v => v*2, v => v.length) == 3);
  2319. }
  2320.  
  2321. // Structural matching
  2322. @safe unittest
  2323. {
  2324. static struct S1 { int x; }
  2325. static struct S2 { int y; }
  2326. alias MySum = SumType!(S1, S2);
  2327.  
  2328. MySum a = MySum(S1(0));
  2329. MySum b = MySum(S2(0));
  2330.  
  2331. assert(a.match!(s1 => s1.x + 1, s2 => s2.y - 1) == 1);
  2332. assert(b.match!(s1 => s1.x + 1, s2 => s2.y - 1) == -1);
  2333. }
  2334.  
  2335. // Separate opCall handlers
  2336. @safe unittest
  2337. {
  2338. static struct IntHandler
  2339. {
  2340. bool opCall(int arg)
  2341. {
  2342. return true;
  2343. }
  2344. }
  2345.  
  2346. static struct FloatHandler
  2347. {
  2348. bool opCall(float arg)
  2349. {
  2350. return false;
  2351. }
  2352. }
  2353.  
  2354. alias MySum = SumType!(int, float);
  2355.  
  2356. MySum x = MySum(42);
  2357. MySum y = MySum(3.14);
  2358.  
  2359. assert(x.match!(IntHandler.init, FloatHandler.init));
  2360. assert(!y.match!(IntHandler.init, FloatHandler.init));
  2361. }
  2362.  
  2363. // Compound opCall handler
  2364. @safe unittest
  2365. {
  2366. static struct CompoundHandler
  2367. {
  2368. bool opCall(int arg)
  2369. {
  2370. return true;
  2371. }
  2372.  
  2373. bool opCall(float arg)
  2374. {
  2375. return false;
  2376. }
  2377. }
  2378.  
  2379. alias MySum = SumType!(int, float);
  2380.  
  2381. MySum x = MySum(42);
  2382. MySum y = MySum(3.14);
  2383.  
  2384. assert(x.match!(CompoundHandler.init));
  2385. assert(!y.match!(CompoundHandler.init));
  2386. }
  2387.  
  2388. // Ordered matching
  2389. @safe unittest
  2390. {
  2391. alias MySum = SumType!(int, float);
  2392.  
  2393. MySum x = MySum(42);
  2394.  
  2395. assert(x.match!((int v) => true, v => false));
  2396. }
  2397.  
  2398. // Non-exhaustive matching
  2399. version (D_Exceptions)
  2400. @system unittest
  2401. {
  2402. import std.exception : assertThrown, assertNotThrown;
  2403.  
  2404. alias MySum = SumType!(int, float);
  2405.  
  2406. MySum x = MySum(42);
  2407. MySum y = MySum(3.14);
  2408.  
  2409. assertNotThrown!MatchException(x.tryMatch!((int n) => true));
  2410. assertThrown!MatchException(y.tryMatch!((int n) => true));
  2411. }
  2412.  
  2413. // Non-exhaustive matching in @safe code
  2414. version (D_Exceptions)
  2415. @safe unittest
  2416. {
  2417. SumType!(int, float) x;
  2418.  
  2419. auto _ = x.tryMatch!(
  2420. (int n) => n + 1,
  2421. );
  2422. }
  2423.  
  2424. // Handlers with ref parameters
  2425. @safe unittest
  2426. {
  2427. alias Value = SumType!(long, double);
  2428.  
  2429. auto value = Value(3.14);
  2430.  
  2431. value.match!(
  2432. (long) {},
  2433. (ref double d) { d *= 2; }
  2434. );
  2435.  
  2436. assert(value.get!double.isClose(6.28));
  2437. }
  2438.  
  2439. // Unreachable handlers
  2440. @safe unittest
  2441. {
  2442. alias MySum = SumType!(int, string);
  2443.  
  2444. MySum s;
  2445.  
  2446. assert(!__traits(compiles,
  2447. s.match!(
  2448. (int _) => 0,
  2449. (string _) => 1,
  2450. (double _) => 2
  2451. )
  2452. ));
  2453.  
  2454. assert(!__traits(compiles,
  2455. s.match!(
  2456. _ => 0,
  2457. (int _) => 1
  2458. )
  2459. ));
  2460. }
  2461.  
  2462. // Unsafe handlers
  2463. @system unittest
  2464. {
  2465. SumType!int x;
  2466. alias unsafeHandler = (int x) @system { return; };
  2467.  
  2468. assert(!__traits(compiles, () @safe
  2469. {
  2470. x.match!unsafeHandler;
  2471. }));
  2472.  
  2473. auto test() @system
  2474. {
  2475. return x.match!unsafeHandler;
  2476. }
  2477. }
  2478.  
  2479. // Overloaded handlers
  2480. @safe unittest
  2481. {
  2482. static struct OverloadSet
  2483. {
  2484. static string fun(int i) { return "int"; }
  2485. static string fun(double d) { return "double"; }
  2486. }
  2487.  
  2488. alias MySum = SumType!(int, double);
  2489.  
  2490. MySum a = 42;
  2491. MySum b = 3.14;
  2492.  
  2493. assert(a.match!(OverloadSet.fun) == "int");
  2494. assert(b.match!(OverloadSet.fun) == "double");
  2495. }
  2496.  
  2497. // Overload sets that include SumType arguments
  2498. @safe unittest
  2499. {
  2500. alias Inner = SumType!(int, double);
  2501. alias Outer = SumType!(Inner, string);
  2502.  
  2503. static struct OverloadSet
  2504. {
  2505. @safe:
  2506. static string fun(int i) { return "int"; }
  2507. static string fun(double d) { return "double"; }
  2508. static string fun(string s) { return "string"; }
  2509. static string fun(Inner i) { return i.match!fun; }
  2510. static string fun(Outer o) { return o.match!fun; }
  2511. }
  2512.  
  2513. Outer a = Inner(42);
  2514. Outer b = Inner(3.14);
  2515. Outer c = "foo";
  2516.  
  2517. assert(OverloadSet.fun(a) == "int");
  2518. assert(OverloadSet.fun(b) == "double");
  2519. assert(OverloadSet.fun(c) == "string");
  2520. }
  2521.  
  2522. // Overload sets with ref arguments
  2523. @safe unittest
  2524. {
  2525. static struct OverloadSet
  2526. {
  2527. static void fun(ref int i) { i = 42; }
  2528. static void fun(ref double d) { d = 3.14; }
  2529. }
  2530.  
  2531. alias MySum = SumType!(int, double);
  2532.  
  2533. MySum x = 0;
  2534. MySum y = 0.0;
  2535.  
  2536. x.match!(OverloadSet.fun);
  2537. y.match!(OverloadSet.fun);
  2538.  
  2539. assert(x.match!((value) => is(typeof(value) == int) && value == 42));
  2540. assert(y.match!((value) => is(typeof(value) == double) && value == 3.14));
  2541. }
  2542.  
  2543. // Overload sets with templates
  2544. @safe unittest
  2545. {
  2546. import std.traits : isNumeric;
  2547.  
  2548. static struct OverloadSet
  2549. {
  2550. static string fun(string arg)
  2551. {
  2552. return "string";
  2553. }
  2554.  
  2555. static string fun(T)(T arg)
  2556. if (isNumeric!T)
  2557. {
  2558. return "numeric";
  2559. }
  2560. }
  2561.  
  2562. alias MySum = SumType!(int, string);
  2563.  
  2564. MySum x = 123;
  2565. MySum y = "hello";
  2566.  
  2567. assert(x.match!(OverloadSet.fun) == "numeric");
  2568. assert(y.match!(OverloadSet.fun) == "string");
  2569. }
  2570.  
  2571. // Github issue #24
  2572. @safe unittest
  2573. {
  2574. void test() @nogc
  2575. {
  2576. int acc = 0;
  2577. SumType!int(1).match!((int x) => acc += x);
  2578. }
  2579. }
  2580.  
  2581. // Github issue #31
  2582. @safe unittest
  2583. {
  2584. void test() @nogc
  2585. {
  2586. int acc = 0;
  2587.  
  2588. SumType!(int, string)(1).match!(
  2589. (int x) => acc += x,
  2590. (string _) => 0,
  2591. );
  2592. }
  2593. }
  2594.  
  2595. // Types that `alias this` a SumType
  2596. @safe unittest
  2597. {
  2598. static struct A {}
  2599. static struct B {}
  2600. static struct D { SumType!(A, B) value; alias value this; }
  2601.  
  2602. auto _ = D().match!(_ => true);
  2603. }
  2604.  
  2605. // Multiple dispatch
  2606. @safe unittest
  2607. {
  2608. alias MySum = SumType!(int, string);
  2609.  
  2610. static int fun(MySum x, MySum y)
  2611. {
  2612. import std.meta : Args = AliasSeq;
  2613.  
  2614. return Args!(x, y).match!(
  2615. (int xv, int yv) => 0,
  2616. (string xv, int yv) => 1,
  2617. (int xv, string yv) => 2,
  2618. (string xv, string yv) => 3
  2619. );
  2620. }
  2621.  
  2622. assert(fun(MySum(0), MySum(0)) == 0);
  2623. assert(fun(MySum(""), MySum(0)) == 1);
  2624. assert(fun(MySum(0), MySum("")) == 2);
  2625. assert(fun(MySum(""), MySum("")) == 3);
  2626. }
  2627.  
  2628. // inout SumTypes
  2629. @safe unittest
  2630. {
  2631. inout(int[]) fun(inout(SumType!(int[])) x)
  2632. {
  2633. return x.match!((inout(int[]) a) => a);
  2634. }
  2635. }
  2636.  
  2637. private void destroyIfOwner(T)(ref T value)
  2638. {
  2639. static if (hasElaborateDestructor!T)
  2640. {
  2641. destroy(value);
  2642. }
  2643. }