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