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