1 // Written in the D programming language.
2 /**
3 $(SCRIPT inhibitQuickIndex = 1;)
4 
5 This module defines facilities for efficient checking of integral operations
6 against overflow, casting with loss of precision, unexpected change of sign,
7 etc. The checking (and possibly correction) can be done at operation level, for
8 example $(LREF opChecked)$(D !"+"(x, y, overflow)) adds two integrals `x` and
9 `y` and sets `overflow` to `true` if an overflow occurred. The flag `overflow`
10 (a `bool` passed by reference) is not touched if the operation succeeded, so the
11 same flag can be reused for a sequence of operations and tested at the end.
12 
13 Issuing individual checked operations is flexible and efficient but often
14 tedious. The $(LREF Checked) facility offers encapsulated integral wrappers that
15 do all checking internally and have configurable behavior upon erroneous
16 results. For example, `Checked!int` is a type that behaves like `int` but aborts
17 execution immediately whenever involved in an operation that produces the
18 arithmetically wrong result. The accompanying convenience function $(LREF
19 checked) uses type deduction to convert a value `x` of integral type `T` to
20 `Checked!T` by means of `checked(x)`. For example:
21 
22 ---
23 void main()
24 {
25     import std.experimental.checkedint, std.stdio;
26     writeln((checked(5) + 7).get); // 12
27     writeln((checked(10) * 1000 * 1000 * 1000).get); // Overflow
28 }
29 ---
30 
31 Similarly, $(D checked(-1) > uint(0)) aborts execution (even though the built-in
32 comparison $(D int(-1) > uint(0)) is surprisingly true due to language's
33 conversion rules modeled after C). Thus, `Checked!int` is a virtually drop-in
34 replacement for `int` useable in debug builds, to be replaced by `int` in
35 release mode if efficiency demands it.
36 
37 `Checked`  has customizable behavior with the help of a second type parameter,
38 `Hook`. Depending on what methods `Hook` defines, core operations on the
39 underlying integral may be verified for overflow or completely redefined. If
40 `Hook` defines no method at all and carries no state, there is no change in
41 behavior, i.e. $(D Checked!(int, void)) is a wrapper around `int` that adds no
42 customization at all.
43 
44 This module provides a few predefined hooks (below) that add useful behavior to
45 `Checked`:
46 
47 $(BOOKTABLE ,
48     $(TR $(TD $(LREF Abort)) $(TD
49         fails every incorrect operation with a message to $(REF
50         stderr, std, stdio) followed by a call to `assert(0)`. It is the default
51         second parameter, i.e. `Checked!short` is the same as
52         $(D Checked!(short, Abort)).
53     ))
54     $(TR $(TD $(LREF Throw)) $(TD
55         fails every incorrect operation by throwing an exception.
56     ))
57     $(TR $(TD $(LREF Warn)) $(TD
58         prints incorrect operations to $(REF stderr, std, stdio)
59         but otherwise preserves the built-in behavior.
60     ))
61     $(TR $(TD $(LREF ProperCompare)) $(TD
62         fixes the comparison operators `==`, `!=`, `<`, `<=`, `>`, and `>=`
63         to return correct results in all circumstances,
64         at a slight cost in efficiency. For example,
65         $(D Checked!(uint, ProperCompare)(1) > -1) is `true`,
66         which is not the case for the built-in comparison. Also, comparing
67         numbers for equality with floating-point numbers only passes if the
68         integral can be converted to the floating-point number precisely,
69         so as to preserve transitivity of equality.
70     ))
71     $(TR $(TD $(LREF WithNaN)) $(TD
72         reserves a special "Not a Number" (NaN) value akin to the homonym value
73         reserved for floating-point values. Once a $(D Checked!(X, WithNaN))
74         gets this special value, it preserves and propagates it until
75         reassigned. $(LREF isNaN) can be used to query whether the object
76         is not a number.
77     ))
78     $(TR $(TD $(LREF Saturate)) $(TD
79         implements saturating arithmetic, i.e. $(D Checked!(int, Saturate))
80         "stops" at `int.max` for all operations that would cause an `int` to
81         overflow toward infinity, and at `int.min` for all operations that would
82         correspondingly overflow toward negative infinity.
83     ))
84 )
85 
86 
87 These policies may be used alone, e.g. $(D Checked!(uint, WithNaN)) defines a
88 `uint`-like type that reaches a stable NaN state for all erroneous operations.
89 They may also be "stacked" on top of each other, owing to the property that a
90 checked integral emulates an actual integral, which means another checked
91 integral can be built on top of it. Some combinations of interest include:
92 
93 $(BOOKTABLE ,
94     $(TR $(TD $(D Checked!(Checked!int, ProperCompare))))
95     $(TR $(TD
96 defines an `int` with fixed
97 comparison operators that will fail with `assert(0)` upon overflow. (Recall that
98 `Abort` is the default policy.) The order in which policies are combined is
99 important because the outermost policy (`ProperCompare` in this case) has the
100 first crack at intercepting an operator. The converse combination $(D
101 Checked!(Checked!(int, ProperCompare))) is meaningless because `Abort` will
102 intercept comparison and will fail without giving `ProperCompare` a chance to
103 intervene.
104     ))
105     $(TR $(TD))
106     $(TR $(TDNW $(D Checked!(Checked!(int, ProperCompare), WithNaN))))
107     $(TR $(TD
108 defines an `int`-like
109 type that supports a NaN value. For values that are not NaN, comparison works
110 properly. Again the composition order is important; $(D Checked!(Checked!(int,
111 WithNaN), ProperCompare)) does not have good semantics because `ProperCompare`
112 intercepts comparisons before the numbers involved are tested for NaN.
113     ))
114 )
115 
116 The hook's members are looked up statically in a Design by Introspection manner
117 and are all optional. The table below illustrates the members that a hook type
118 may define and their influence over the behavior of the `Checked` type using it.
119 In the table, `hook` is an alias for `Hook` if the type `Hook` does not
120 introduce any state, or an object of type `Hook` otherwise.
121 
122 $(TABLE ,
123 $(TR $(TH `Hook` member) $(TH Semantics in $(D Checked!(T, Hook)))
124 )
125 $(TR $(TD `defaultValue`) $(TD If defined, `Hook.defaultValue!T` is used as the
126 default initializer of the payload.)
127 )
128 $(TR $(TD `min`) $(TD If defined, `Hook.min!T` is used as the minimum value of
129 the payload.)
130 )
131 $(TR $(TD `max`) $(TD If defined, `Hook.max!T` is used as the maximum value of
132 the payload.)
133 )
134 $(TR $(TD `hookOpCast`) $(TD If defined, `hook.hookOpCast!U(get)` is forwarded
135 to unconditionally when the payload is to be cast to type `U`.)
136 )
137 $(TR $(TD `onBadCast`) $(TD If defined and `hookOpCast` is $(I not) defined,
138 `onBadCast!U(get)` is forwarded to when the payload is to be cast to type `U`
139 and the cast would lose information or force a change of sign.)
140 )
141 $(TR $(TD `hookOpEquals`) $(TD If defined, $(D hook.hookOpEquals(get, rhs)) is
142 forwarded to unconditionally when the payload is compared for equality against
143 value `rhs` of integral, floating point, or Boolean type.)
144 )
145 $(TR $(TD `hookOpCmp`) $(TD If defined, $(D hook.hookOpCmp(get, rhs)) is
146 forwarded to unconditionally when the payload is compared for ordering against
147 value `rhs` of integral, floating point, or Boolean type.)
148 )
149 $(TR $(TD `hookOpUnary`) $(TD If defined, `hook.hookOpUnary!op(get)` (where `op`
150 is the operator symbol) is forwarded to for unary operators `-` and `~`. In
151 addition, for unary operators `++` and `--`, `hook.hookOpUnary!op(payload)` is
152 called, where `payload` is a reference to the value wrapped by `Checked` so the
153 hook can change it.)
154 )
155 $(TR $(TD `hookOpBinary`) $(TD If defined, $(D hook.hookOpBinary!op(get, rhs))
156 (where `op` is the operator symbol and `rhs` is the right-hand side operand) is
157 forwarded to unconditionally for binary operators `+`,  `-`, `*`, `/`, `%`,
158 `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.)
159 )
160 $(TR $(TD `hookOpBinaryRight`) $(TD If defined, $(D
161 hook.hookOpBinaryRight!op(lhs, get)) (where `op` is the operator symbol and
162 `lhs` is the left-hand side operand) is forwarded to unconditionally for binary
163 operators `+`,  `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.)
164 )
165 $(TR $(TD `onOverflow`) $(TD If defined, `hook.onOverflow!op(get)` is forwarded
166 to for unary operators that overflow but only if `hookOpUnary` is not defined.
167 Unary `~` does not overflow; unary `-` overflows only when the most negative
168 value of a signed type is negated, and the result of the hook call is returned.
169 When the increment or decrement operators overflow, the payload is assigned the
170 result of `hook.onOverflow!op(get)`. When a binary operator overflows, the
171 result of $(D hook.onOverflow!op(get, rhs)) is returned, but only if `Hook` does
172 not define `hookOpBinary`.)
173 )
174 $(TR $(TD `hookOpOpAssign`) $(TD If defined, $(D hook.hookOpOpAssign!op(payload,
175 rhs)) (where `op` is the operator symbol and `rhs` is the right-hand side
176 operand) is forwarded to unconditionally for binary operators `+=`,  `-=`, `*=`, `/=`, `%=`,
177 `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=`.)
178 )
179 $(TR $(TD `onLowerBound`) $(TD If defined, $(D hook.onLowerBound(value, bound))
180 (where `value` is the value being assigned) is forwarded to when the result of
181 binary operators `+=`,  `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`,
182 and `>>>=` is smaller than the smallest value representable by `T`.)
183 )
184 $(TR $(TD `onUpperBound`) $(TD If defined, $(D hook.onUpperBound(value, bound))
185 (where `value` is the value being assigned) is forwarded to when the result of
186 binary operators `+=`,  `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`,
187 and `>>>=` is larger than the largest value representable by `T`.)
188 )
189 $(TR $(TD `hookToHash`) $(TD If defined, $(D hook.hookToHash(payload))
190 (where `payload` is a reference to the value wrapped by Checked) is forwarded
191 to when `toHash` is called on a Checked type. Custom hashing can be implemented
192 in a `Hook`, otherwise the built-in hashing is used.)
193 )
194 )
195 
196 Source: $(PHOBOSSRC std/experimental/checkedint.d)
197 */
198 module std.experimental.checkedint;
199 import std.traits : isFloatingPoint, isIntegral, isNumeric, isUnsigned, Unqual;
200 
201 ///
202 @safe unittest
203 {
concatAndAdd(int[]a,int[]b,int offset)204     int[] concatAndAdd(int[] a, int[] b, int offset)
205     {
206         // Aborts on overflow on size computation
207         auto r = new int[(checked(a.length) + b.length).get];
208         // Aborts on overflow on element computation
209         foreach (i; 0 .. a.length)
210             r[i] = (a[i] + checked(offset)).get;
211         foreach (i; 0 .. b.length)
212             r[i + a.length] = (b[i] + checked(offset)).get;
213         return r;
214     }
215     assert(concatAndAdd([1, 2, 3], [4, 5], -1) == [0, 1, 2, 3, 4]);
216 }
217 
218 
219 /// `Saturate` stops at an overflow
220 @safe unittest
221 {
222     auto x = (cast(byte) 127).checked!Saturate;
223     assert(x == 127);
224     x++;
225     assert(x == 127);
226 }
227 
228 /// `WithNaN` has a special "Not a Number" (NaN) value akin to the homonym value reserved for floating-point values
229 @safe unittest
230 {
231     auto x = 100.checked!WithNaN;
232     assert(x == 100);
233     x /= 0;
234     assert(x.isNaN);
235 }
236 
237 /// `ProperCompare` fixes the comparison operators ==, !=, <, <=, >, and >= to return correct results
238 @safe unittest
239 {
240     uint x = 1;
241     auto y = x.checked!ProperCompare;
242     assert(x < -1); // built-in comparison
243     assert(y > -1); // ProperCompare
244 }
245 
246 /// `Throw` fails every incorrect operation by throwing an exception
247 @safe unittest
248 {
249     import std.exception : assertThrown;
250     auto x = -1.checked!Throw;
251     assertThrown(x / 0);
252     assertThrown(x + int.min);
253     assertThrown(x == uint.max);
254 }
255 
256 /**
257 Checked integral type wraps an integral `T` and customizes its behavior with the
258 help of a `Hook` type. The type wrapped must be one of the predefined integrals
259 (unqualified), or another instance of `Checked`.
260 */
261 struct Checked(T, Hook = Abort)
262 if (isIntegral!T || is(T == Checked!(U, H), U, H))
263 {
264     import std.algorithm.comparison : among;
265     import std.experimental.allocator.common : stateSize;
266     import std.format.spec : FormatSpec;
267     import std.range.primitives : isInputRange, ElementType;
268     import std.traits : hasMember, isSomeChar;
269 
270     /**
271     The type of the integral subject to checking.
272     */
273     alias Representation = T;
274 
275     // state {
276     static if (hasMember!(Hook, "defaultValue"))
277         private T payload = Hook.defaultValue!T;
278     else
279         private T payload;
280     /**
281     `hook` is a member variable if it has state, or an alias for `Hook`
282     otherwise.
283     */
284     static if (stateSize!Hook > 0) Hook hook;
285     else alias hook = Hook;
286     // } state
287 
288     // get
289     /**
290     Returns a copy of the underlying value.
291     */
getChecked292     auto get() inout { return payload; }
293     ///
294     @safe unittest
295     {
296         auto x = checked(ubyte(42));
297         static assert(is(typeof(x.get()) == ubyte));
298         assert(x.get == 42);
299         const y = checked(ubyte(42));
300         static assert(is(typeof(y.get()) == const ubyte));
301         assert(y.get == 42);
302     }
303 
304     /**
305     Defines the minimum and maximum. These values are hookable by defining
306     `Hook.min` and/or `Hook.max`.
307     */
308     static if (hasMember!(Hook, "min"))
309     {
310         enum Checked!(T, Hook) min = Checked!(T, Hook)(Hook.min!T);
311         ///
312         @safe unittest
313         {
314             assert(Checked!short.min == -32768);
315             assert(Checked!(short, WithNaN).min == -32767);
316             assert(Checked!(uint, WithNaN).max == uint.max - 1);
317         }
318     }
319     else
320         enum Checked!(T, Hook) min = Checked(T.min);
321     /// ditto
322     static if (hasMember!(Hook, "max"))
323         enum Checked!(T, Hook) max = Checked(Hook.max!T);
324     else
325         enum Checked!(T, Hook) max = Checked(T.max);
326 
327     /**
328     Constructor taking a value properly convertible to the underlying type. `U`
329     may be either an integral that can be converted to `T` without a loss, or
330     another `Checked` instance whose representation may be in turn converted to
331     `T` without a loss.
332     */
333     this(U)(U rhs)
334     if (valueConvertible!(U, T) ||
335         !isIntegral!T && is(typeof(T(rhs))) ||
336         is(U == Checked!(V, W), V, W) &&
337             is(typeof(Checked!(T, Hook)(rhs.get))))
338     {
339         static if (isIntegral!U)
340             payload = rhs;
341         else
342             payload = rhs.payload;
343     }
344     ///
345     @safe unittest
346     {
347         auto a = checked(42L);
348         assert(a == 42);
349         auto b = Checked!long(4242); // convert 4242 to long
350         assert(b == 4242);
351     }
352 
353     /**
354     Assignment operator. Has the same constraints as the constructor.
355     */
356     ref Checked opAssign(U)(U rhs) return
357     if (is(typeof(Checked!(T, Hook)(rhs))))
358     {
359         static if (isIntegral!U)
360             payload = rhs;
361         else
362             payload = rhs.payload;
363         return this;
364     }
365     ///
366     @safe unittest
367     {
368         Checked!long a;
369         a = 42L;
370         assert(a == 42);
371         a = 4242;
372         assert(a == 4242);
373     }
374 
375     ///
376     @safe unittest
377     {
378         Checked!long a, b;
379         a = b = 3;
380         assert(a == 3 && b == 3);
381     }
382 
383     /**
384     Construct from a decimal string. The conversion follows the same rules as
385     $(REF to, std, conv) converting a string to the wrapped `T` type.
386 
387     Params:
388         str = an $(REF_ALTTEXT input range, isInputRange, std,range,primitives)
389               of characters
390     */
391     this(Range)(Range str)
392     if (isInputRange!Range && isSomeChar!(ElementType!Range))
393     {
394         import std.conv : to;
395 
396         this(to!T(str));
397     }
398 
399     /**
400     $(REF to, std, conv) can convert a string to a `Checked!T`:
401     */
402     @system unittest
403     {
404         import std.conv : to;
405 
406         const a = to!long("1234");
407         const b = to!(Checked!long)("1234");
408         assert(a == b);
409     }
410 
411     // opCast
412     /**
413     Casting operator to integral, `bool`, or floating point type. If `Hook`
414     defines `hookOpCast`, the call immediately returns
415     `hook.hookOpCast!U(get)`. Otherwise, casting to `bool` yields $(D
416     get != 0) and casting to another integral that can represent all
417     values of `T` returns `get` promoted to `U`.
418 
419     If a cast to a floating-point type is requested and `Hook` defines
420     `onBadCast`, the cast is verified by ensuring $(D get == cast(T)
421     U(get)). If that is not `true`, `hook.onBadCast!U(get)` is returned.
422 
423     If a cast to an integral type is requested and `Hook` defines `onBadCast`,
424     the cast is verified by ensuring `get` and $(D cast(U)
425     get) are the same arithmetic number. (Note that `int(-1)` and
426     `uint(1)` are different values arithmetically although they have the same
427     bitwise representation and compare equal by language rules.) If the numbers
428     are not arithmetically equal, `hook.onBadCast!U(get)` is
429     returned.
430 
431     */
432     U opCast(U, this _)()
433     if (isIntegral!U || isFloatingPoint!U || is(U == bool))
434     {
435         static if (hasMember!(Hook, "hookOpCast"))
436         {
437             return hook.hookOpCast!U(payload);
438         }
439         else static if (is(U == bool))
440         {
441             return payload != 0;
442         }
443         else static if (valueConvertible!(T, U))
444         {
445             return payload;
446         }
447         // may lose bits or precision
448         else static if (!hasMember!(Hook, "onBadCast"))
449         {
450             return cast(U) payload;
451         }
452         else
453         {
454             if (isUnsigned!T || !isUnsigned!U ||
455                     T.sizeof > U.sizeof || payload >= 0)
456             {
457                 auto result = cast(U) payload;
458                 // If signedness is different, we need additional checks
459                 if (result == payload &&
460                         (!isUnsigned!T || isUnsigned!U || result >= 0))
461                     return result;
462             }
463             return hook.onBadCast!U(payload);
464         }
465     }
466     ///
467     @safe unittest
468     {
469         assert(cast(uint) checked(42) == 42);
470         assert(cast(uint) checked!WithNaN(-42) == uint.max);
471     }
472 
473     // opEquals
474     /**
475     Compares `this` against `rhs` for equality. If `Hook` defines
476     `hookOpEquals`, the function forwards to $(D
477     hook.hookOpEquals(get, rhs)). Otherwise, the result of the
478     built-in operation $(D get == rhs) is returned.
479 
480     If `U` is also an instance of `Checked`, both hooks (left- and right-hand
481     side) are introspected for the method `hookOpEquals`. If both define it,
482     priority is given to the left-hand side.
483 
484     */
485     bool opEquals(U, this _)(U rhs)
486     if (isIntegral!U || isFloatingPoint!U || is(U == bool) ||
487         is(U == Checked!(V, W), V, W) && is(typeof(this == rhs.payload)))
488     {
489         static if (is(U == Checked!(V, W), V, W))
490         {
491             alias R = typeof(payload + rhs.payload);
492             static if (is(Hook == W))
493             {
494                 // Use the lhs hook if there
495                 return this == rhs.payload;
496             }
497             else static if (valueConvertible!(T, R) && valueConvertible!(V, R))
498             {
499                 return payload == rhs.payload;
500             }
501             else static if (hasMember!(Hook, "hookOpEquals"))
502             {
503                 return hook.hookOpEquals(payload, rhs.payload);
504             }
505             else static if (hasMember!(W, "hookOpEquals"))
506             {
507                 return rhs.hook.hookOpEquals(rhs.payload, payload);
508             }
509             else
510             {
511                 return payload == rhs.payload;
512             }
513         }
514         else static if (hasMember!(Hook, "hookOpEquals"))
515             return hook.hookOpEquals(payload, rhs);
516         else static if (isIntegral!U || isFloatingPoint!U || is(U == bool))
517             return payload == rhs;
518     }
519 
520     ///
521     static if (is(T == int) && is(Hook == void)) @safe unittest
522     {
523         import std.traits : isUnsigned;
524 
525         static struct MyHook
526         {
527             static bool thereWereErrors;
528             static bool hookOpEquals(L, R)(L lhs, R rhs)
529             {
530                 if (lhs != rhs) return false;
531                 static if (isUnsigned!L && !isUnsigned!R)
532                 {
533                     if (lhs > 0 && rhs < 0) thereWereErrors = true;
534                 }
535                 else static if (isUnsigned!R && !isUnsigned!L)
536                     if (lhs < 0 && rhs > 0) thereWereErrors = true;
537                 // Preserve built-in behavior.
538                 return true;
539             }
540         }
541         auto a = checked!MyHook(-42);
542         assert(a == uint(-42));
543         assert(MyHook.thereWereErrors);
544         MyHook.thereWereErrors = false;
545         assert(checked!MyHook(uint(-42)) == -42);
546         assert(MyHook.thereWereErrors);
547         static struct MyHook2
548         {
549             static bool hookOpEquals(L, R)(L lhs, R rhs)
550             {
551                 return lhs == rhs;
552             }
553         }
554         MyHook.thereWereErrors = false;
555         assert(checked!MyHook2(uint(-42)) == a);
556         // Hook on left hand side takes precedence, so no errors
557         assert(!MyHook.thereWereErrors);
558     }
559 
560     // toHash
561     /**
562     Generates a hash for `this`. If `Hook` defines `hookToHash`, the call
563     immediately returns `hook.hookToHash(payload)`. If `Hook` does not
564     implement `hookToHash`, but it has state, a hash will be generated for
565     the `Hook` using the built-in function and it will be xored with the
566     hash of the `payload`.
567     */
568     size_t toHash() const nothrow @safe
569     {
570         static if (hasMember!(Hook, "hookToHash"))
571         {
572             return hook.hookToHash(payload);
573         }
574         else static if (stateSize!Hook > 0)
575         {
576             static if (hasMember!(typeof(payload), "toHash"))
577             {
578                 return payload.toHash() ^ hashOf(hook);
579             }
580             else
581             {
582                 return hashOf(payload) ^ hashOf(hook);
583             }
584         }
585         else static if (hasMember!(typeof(payload), "toHash"))
586         {
587             return payload.toHash();
588         }
589         else
590         {
591             return .hashOf(payload);
592         }
593     }
594 
595     /// ditto
596     size_t toHash(this _)() shared const nothrow @safe
597     {
598         import core.atomic : atomicLoad, MemoryOrder;
599         static if (is(typeof(this.payload.atomicLoad!(MemoryOrder.acq)) P))
600         {
601             auto payload = __ctfe ? cast(P) this.payload
602                                   : this.payload.atomicLoad!(MemoryOrder.acq);
603         }
604         else
605         {
606             alias payload = this.payload;
607         }
608 
609         static if (hasMember!(Hook, "hookToHash"))
610         {
611             return hook.hookToHash(payload);
612         }
613         else static if (stateSize!Hook > 0)
614         {
615             static if (hasMember!(typeof(payload), "toHash"))
616             {
617                 return payload.toHash() ^ hashOf(hook);
618             }
619             else
620             {
621                 return hashOf(payload) ^ hashOf(hook);
622             }
623         }
624         else static if (hasMember!(typeof(payload), "toHash"))
625         {
626             return payload.toHash();
627         }
628         else
629         {
630             return .hashOf(payload);
631         }
632     }
633 
634     /**
635     Writes a string representation of this to a `sink`.
636 
637     Params:
638       sink = A `Char` accepting
639              $(REF_ALTTEXT output range, isOutputRange, std,range,primitives).
640       fmt  = A $(REF FormatSpec, std, format) which controls how this
641              is formatted.
642     */
643     void toString(Writer, Char)(scope ref Writer sink, scope const ref FormatSpec!Char fmt) const
644     {
645         import std.format.write : formatValue;
646         if (fmt.spec == 's')
647             return formatValue(sink, this, fmt);
648         else
649             return formatValue(sink, payload, fmt);
650     }
651 
652     /**
653     `toString` is rarely directly invoked; the usual way of using it is via
654     $(REF format, std, format):
655     */
656     @system unittest
657     {
658         import std.format;
659 
660         assert(format("%04d", checked(15)) == "0015");
661         assert(format("0x%02x", checked(15)) == "0x0f");
662     }
663 
664     // opCmp
665     /**
666 
667     Compares `this` against `rhs` for ordering. If `Hook` defines `hookOpCmp`,
668     the function forwards to $(D hook.hookOpCmp(get, rhs)). Otherwise, the
669     result of the built-in comparison operation is returned.
670 
671     If `U` is also an instance of `Checked`, both hooks (left- and right-hand
672     side) are introspected for the method `hookOpCmp`. If both define it,
673     priority is given to the left-hand side.
674 
675     */
676     auto opCmp(U, this _)(const U rhs) //const pure @safe nothrow @nogc
677     if (isIntegral!U || isFloatingPoint!U || is(U == bool))
678     {
679         static if (hasMember!(Hook, "hookOpCmp"))
680         {
681             return hook.hookOpCmp(payload, rhs);
682         }
683         else static if (valueConvertible!(T, U) || valueConvertible!(U, T))
684         {
685             return payload < rhs ? -1 : payload > rhs;
686         }
687         else static if (isFloatingPoint!U)
688         {
689             U lhs = payload;
690             return lhs < rhs ? U(-1.0)
691                 : lhs > rhs ? U(1.0)
692                 : lhs == rhs ? U(0.0) : U.init;
693         }
694         else
695         {
696             return payload < rhs ? -1 : payload > rhs;
697         }
698     }
699 
700     /// ditto
701     auto opCmp(U, Hook1, this _)(Checked!(U, Hook1) rhs)
702     {
703         alias R = typeof(payload + rhs.payload);
704         static if (valueConvertible!(T, R) && valueConvertible!(U, R))
705         {
706             return payload < rhs.payload ? -1 : payload > rhs.payload;
707         }
708         else static if (is(Hook == Hook1))
709         {
710             // Use the lhs hook
711             return this.opCmp(rhs.payload);
712         }
713         else static if (hasMember!(Hook, "hookOpCmp"))
714         {
715             return hook.hookOpCmp(get, rhs.get);
716         }
717         else static if (hasMember!(Hook1, "hookOpCmp"))
718         {
719             return -rhs.hook.hookOpCmp(rhs.payload, get);
720         }
721         else
722         {
723             return payload < rhs.payload ? -1 : payload > rhs.payload;
724         }
725     }
726 
727     ///
728     static if (is(T == int) && is(Hook == void)) @safe unittest
729     {
730         import std.traits : isUnsigned;
731 
732         static struct MyHook
733         {
734             static bool thereWereErrors;
735             static int hookOpCmp(L, R)(L lhs, R rhs)
736             {
737                 static if (isUnsigned!L && !isUnsigned!R)
738                 {
739                     if (rhs < 0 && rhs >= lhs)
740                         thereWereErrors = true;
741                 }
742                 else static if (isUnsigned!R && !isUnsigned!L)
743                 {
744                     if (lhs < 0 && lhs >= rhs)
745                         thereWereErrors = true;
746                 }
747                 // Preserve built-in behavior.
748                 return lhs < rhs ? -1 : lhs > rhs;
749             }
750         }
751         auto a = checked!MyHook(-42);
752         assert(a > uint(42));
753         assert(MyHook.thereWereErrors);
754         static struct MyHook2
755         {
756             static int hookOpCmp(L, R)(L lhs, R rhs)
757             {
758                 // Default behavior
759                 return lhs < rhs ? -1 : lhs > rhs;
760             }
761         }
762         MyHook.thereWereErrors = false;
763         assert(Checked!(uint, MyHook2)(uint(-42)) <= a);
764         //assert(Checked!(uint, MyHook2)(uint(-42)) >= a);
765         // Hook on left hand side takes precedence, so no errors
766         assert(!MyHook.thereWereErrors);
767         assert(a <= Checked!(uint, MyHook2)(uint(-42)));
768         assert(MyHook.thereWereErrors);
769     }
770 
771     // For coverage
772     static if (is(T == int) && is(Hook == void)) @safe unittest
773     {
774         assert(checked(42) <= checked!void(42));
775         assert(checked!void(42) <= checked(42u));
776         assert(checked!void(42) <= checked!(void*)(42u));
777     }
778 
779     // opUnary
780     /**
781 
782     Defines unary operators `+`, `-`, `~`, `++`, and `--`. Unary `+` is not
783     overridable and always has built-in behavior (returns `this`). For the
784     others, if `Hook` defines `hookOpUnary`, `opUnary` forwards to $(D
785     Checked!(typeof(hook.hookOpUnary!op(get)),
786     Hook)(hook.hookOpUnary!op(get))).
787 
788     If `Hook` does not define `hookOpUnary` but defines `onOverflow`, `opUnary`
789     forwards to `hook.onOverflow!op(get)` in case an overflow occurs.
790     For `++` and `--`, the payload is assigned from the result of the call to
791     `onOverflow`.
792 
793     Note that unary `-` is considered to overflow if `T` is a signed integral of
794     32 or 64 bits and is equal to the most negative value. This is because that
795     value has no positive negation.
796 
797     */
798     auto opUnary(string op, this _)()
799     if (op == "+" || op == "-" || op == "~")
800     {
801         static if (op == "+")
802             return Checked(this); // "+" is not hookable
803         else static if (hasMember!(Hook, "hookOpUnary"))
804         {
805             auto r = hook.hookOpUnary!op(payload);
806             return Checked!(typeof(r), Hook)(r);
807         }
808         else static if (op == "-" && isIntegral!T && T.sizeof >= 4 &&
809                 !isUnsigned!T && hasMember!(Hook, "onOverflow"))
810         {
811             static assert(is(typeof(-payload) == typeof(payload)));
812             bool overflow;
813             import core.checkedint : negs;
814             auto r = negs(payload, overflow);
815             if (overflow) r = hook.onOverflow!op(payload);
816             return Checked(r);
817         }
818         else
819             return Checked(mixin(op ~ "payload"));
820     }
821 
822     /// ditto
823     ref Checked opUnary(string op)() return
824     if (op == "++" || op == "--")
825     {
826         static if (hasMember!(Hook, "hookOpUnary"))
827             hook.hookOpUnary!op(payload);
828         else static if (hasMember!(Hook, "onOverflow"))
829         {
830             static if (op == "++")
831             {
832                 if (payload == max.payload)
833                     payload = hook.onOverflow!"++"(payload);
834                 else
835                     ++payload;
836             }
837             else
838             {
839                 if (payload == min.payload)
840                     payload = hook.onOverflow!"--"(payload);
841                 else
842                     --payload;
843             }
844         }
845         else
846             mixin(op ~ "payload;");
847         return this;
848     }
849 
850     ///
851     static if (is(T == int) && is(Hook == void)) @safe unittest
852     {
853         static struct MyHook
854         {
855             static bool thereWereErrors;
856             static L hookOpUnary(string x, L)(L lhs)
857             {
858                 if (x == "-" && lhs == -lhs) thereWereErrors = true;
859                 return -lhs;
860             }
861         }
862         auto a = checked!MyHook(long.min);
863         assert(a == -a);
864         assert(MyHook.thereWereErrors);
865         auto b = checked!void(42);
866         assert(++b == 43);
867     }
868 
869     // opBinary
870     /**
871 
872     Defines binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`,
873     and `>>>`. If `Hook` defines `hookOpBinary`, `opBinary` forwards to $(D
874     Checked!(typeof(hook.hookOpBinary!op(get, rhs)),
875     Hook)(hook.hookOpBinary!op(get, rhs))).
876 
877     If `Hook` does not define `hookOpBinary` but defines `onOverflow`,
878     `opBinary` forwards to `hook.onOverflow!op(get, rhs)` in case an
879     overflow occurs.
880 
881     If two `Checked` instances are involved in a binary operation and both
882     define `hookOpBinary`, the left-hand side hook has priority. If both define
883     `onOverflow`, a compile-time error occurs.
884 
885     */
886     auto opBinary(string op, Rhs)(const Rhs rhs)
887     if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool))
888     {
889         return opBinaryImpl!(op, Rhs, typeof(this))(rhs);
890     }
891 
892     /// ditto
893     auto opBinary(string op, Rhs)(const Rhs rhs) const
894     if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool))
895     {
896         return opBinaryImpl!(op, Rhs, typeof(this))(rhs);
897     }
898 
899     private auto opBinaryImpl(string op, Rhs, this _)(const Rhs rhs)
900     {
901         alias R = typeof(mixin("payload" ~ op ~ "rhs"));
902         static assert(is(typeof(mixin("payload" ~ op ~ "rhs")) == R));
903         static if (isIntegral!R) alias Result = Checked!(R, Hook);
904         else alias Result = R;
905 
906         static if (hasMember!(Hook, "hookOpBinary"))
907         {
908             auto r = hook.hookOpBinary!op(payload, rhs);
909             return Checked!(typeof(r), Hook)(r);
910         }
911         else static if (is(Rhs == bool))
912         {
913             return mixin("this" ~ op ~ "ubyte(rhs)");
914         }
915         else static if (isFloatingPoint!Rhs)
916         {
917             return mixin("payload" ~ op ~ "rhs");
918         }
919         else static if (hasMember!(Hook, "onOverflow"))
920         {
921             bool overflow;
922             auto r = opChecked!op(payload, rhs, overflow);
923             if (overflow) r = hook.onOverflow!op(payload, rhs);
924             return Result(r);
925         }
926         else
927         {
928             // Default is built-in behavior
929             return Result(mixin("payload" ~ op ~ "rhs"));
930         }
931     }
932 
933     /// ditto
934     auto opBinary(string op, U, Hook1)(Checked!(U, Hook1) rhs)
935     {
936         return opBinaryImpl2!(op, U, Hook1, typeof(this))(rhs);
937     }
938 
939     /// ditto
940     auto opBinary(string op, U, Hook1)(Checked!(U, Hook1) rhs) const
941     {
942         return opBinaryImpl2!(op, U, Hook1, typeof(this))(rhs);
943     }
944 
945     private
946     auto opBinaryImpl2(string op, U, Hook1, this _)(Checked!(U, Hook1) rhs)
947     {
948         alias R = typeof(get + rhs.payload);
949         static if (valueConvertible!(T, R) && valueConvertible!(U, R) ||
950             is(Hook == Hook1))
951         {
952             // Delegate to lhs
953             return mixin("this" ~ op ~ "rhs.payload");
954         }
955         else static if (hasMember!(Hook, "hookOpBinary"))
956         {
957             return hook.hookOpBinary!op(payload, rhs);
958         }
959         else static if (hasMember!(Hook1, "hookOpBinary"))
960         {
961             // Delegate to rhs
962             return mixin("this.payload" ~ op ~ "rhs");
963         }
964         else static if (hasMember!(Hook, "onOverflow") &&
965             !hasMember!(Hook1, "onOverflow"))
966         {
967             // Delegate to lhs
968             return mixin("this" ~ op ~ "rhs.payload");
969         }
970         else static if (hasMember!(Hook1, "onOverflow") &&
971             !hasMember!(Hook, "onOverflow"))
972         {
973             // Delegate to rhs
974             return mixin("this.payload" ~ op ~ "rhs");
975         }
976         else
977         {
978             static assert(0, "Conflict between lhs and rhs hooks," ~
979                 " use .get on one side to disambiguate.");
980         }
981     }
982 
983     static if (is(T == int) && is(Hook == void)) @safe unittest
984     {
985         const a = checked(42);
986         assert(a + 1 == 43);
987         assert(a + checked(uint(42)) == 84);
988         assert(checked(42) + checked!void(42u) == 84);
989         assert(checked!void(42) + checked(42u) == 84);
990 
991         static struct MyHook
992         {
993             static uint tally;
994             static auto hookOpBinary(string x, L, R)(L lhs, R rhs)
995             {
996                 ++tally;
997                 return mixin("lhs" ~ x ~ "rhs");
998             }
999         }
1000         assert(checked!MyHook(42) + checked(42u) == 84);
1001         assert(checked!void(42) + checked!MyHook(42u) == 84);
1002         assert(MyHook.tally == 2);
1003     }
1004 
1005     // opBinaryRight
1006     /**
1007 
1008     Defines binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`, `^`, `<<`,
1009     `>>`, and `>>>` for the case when a built-in numeric or Boolean type is on
1010     the left-hand side, and a `Checked` instance is on the right-hand side.
1011 
1012     */
1013     auto opBinaryRight(string op, Lhs)(const Lhs lhs)
1014     if (isIntegral!Lhs || isFloatingPoint!Lhs || is(Lhs == bool))
1015     {
1016         return opBinaryRightImpl!(op, Lhs, typeof(this))(lhs);
1017     }
1018 
1019     /// ditto
1020     auto opBinaryRight(string op, Lhs)(const Lhs lhs) const
1021     if (isIntegral!Lhs || isFloatingPoint!Lhs || is(Lhs == bool))
1022     {
1023         return opBinaryRightImpl!(op, Lhs, typeof(this))(lhs);
1024     }
1025 
1026     private auto opBinaryRightImpl(string op, Lhs, this _)(const Lhs lhs)
1027     {
1028         static if (hasMember!(Hook, "hookOpBinaryRight"))
1029         {
1030             auto r = hook.hookOpBinaryRight!op(lhs, payload);
1031             return Checked!(typeof(r), Hook)(r);
1032         }
1033         else static if (hasMember!(Hook, "hookOpBinary"))
1034         {
1035             auto r = hook.hookOpBinary!op(lhs, payload);
1036             return Checked!(typeof(r), Hook)(r);
1037         }
1038         else static if (is(Lhs == bool))
1039         {
1040             return mixin("ubyte(lhs)" ~ op ~ "this");
1041         }
1042         else static if (isFloatingPoint!Lhs)
1043         {
1044             return mixin("lhs" ~ op ~ "payload");
1045         }
1046         else static if (hasMember!(Hook, "onOverflow"))
1047         {
1048             bool overflow;
1049             auto r = opChecked!op(lhs, T(payload), overflow);
1050             if (overflow) r = hook.onOverflow!op(lhs, payload);
1051             return Checked!(typeof(r), Hook)(r);
1052         }
1053         else
1054         {
1055             // Default is built-in behavior
1056             auto r = mixin("lhs" ~ op ~ "T(payload)");
1057             return Checked!(typeof(r), Hook)(r);
1058         }
1059     }
1060 
1061     static if (is(T == int) && is(Hook == void)) @safe unittest
1062     {
1063         assert(1 + checked(1) == 2);
1064         static uint tally;
1065         static struct MyHook
1066         {
1067             static auto hookOpBinaryRight(string x, L, R)(L lhs, R rhs)
1068             {
1069                 ++tally;
1070                 return mixin("lhs" ~ x ~ "rhs");
1071             }
1072         }
1073         assert(1 + checked!MyHook(1) == 2);
1074         assert(tally == 1);
1075 
1076         immutable x1 = checked(1);
1077         assert(1 + x1 == 2);
1078         immutable x2 = checked!MyHook(1);
1079         assert(1 + x2 == 2);
1080         assert(tally == 2);
1081     }
1082 
1083     // opOpAssign
1084     /**
1085 
1086     Defines operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`,
1087     `<<=`, `>>=`, and `>>>=`.
1088 
1089     If `Hook` defines `hookOpOpAssign`, `opOpAssign` forwards to
1090     `hook.hookOpOpAssign!op(payload, rhs)`, where `payload` is a reference to
1091     the internally held data so the hook can change it.
1092 
1093     Otherwise, the operator first evaluates $(D auto result =
1094     opBinary!op(payload, rhs).payload), which is subject to the hooks in
1095     `opBinary`. Then, if `result` is less than $(D Checked!(T, Hook).min) and if
1096     `Hook` defines `onLowerBound`, the payload is assigned from $(D
1097     hook.onLowerBound(result, min)). If `result` is greater than $(D Checked!(T,
1098     Hook).max) and if `Hook` defines `onUpperBound`, the payload is assigned
1099     from $(D hook.onUpperBound(result, min)).
1100 
1101     If the right-hand side is also a Checked but with a different hook or
1102     underlying type, the hook and underlying type of this Checked takes
1103     precedence.
1104 
1105     In all other cases, the built-in behavior is carried out.
1106 
1107     Params:
1108     op = The operator involved (without the `"="`, e.g. `"+"` for `"+="` etc)
1109     rhs = The right-hand side of the operator (left-hand side is `this`)
1110 
1111     Returns: A reference to `this`.
1112     */
1113     ref Checked opOpAssign(string op, Rhs)(const Rhs rhs) return
1114     if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool))
1115     {
1116         static assert(is(typeof(mixin("payload" ~ op ~ "=rhs")) == T));
1117 
1118         static if (hasMember!(Hook, "hookOpOpAssign"))
1119         {
1120             hook.hookOpOpAssign!op(payload, rhs);
1121         }
1122         else
1123         {
1124             alias R = typeof(get + rhs);
1125             auto r = opBinary!op(rhs).get;
1126             import std.conv : unsigned;
1127 
1128             static if (ProperCompare.hookOpCmp(R.min, min.get) < 0 &&
1129                 hasMember!(Hook, "onLowerBound"))
1130             {
1131                 if (ProperCompare.hookOpCmp(r, min.get) < 0)
1132                 {
1133                     // Example: Checked!uint(1) += int(-3)
1134                     payload = hook.onLowerBound(r, min.get);
1135                     return this;
1136                 }
1137             }
1138             static if (ProperCompare.hookOpCmp(max.get, R.max) < 0 &&
1139                 hasMember!(Hook, "onUpperBound"))
1140             {
1141                 if (ProperCompare.hookOpCmp(r, max.get) > 0)
1142                 {
1143                     // Example: Checked!uint(1) += long(uint.max)
1144                     payload = hook.onUpperBound(r, max.get);
1145                     return this;
1146                 }
1147             }
1148             payload = cast(T) r;
1149         }
1150         return this;
1151     }
1152 
1153     /// ditto
1154     ref Checked opOpAssign(string op, Rhs)(const Rhs rhs) return
1155     if (is(Rhs == Checked!(RhsT, RhsHook), RhsT, RhsHook))
1156     {
1157         return opOpAssign!(op, typeof(rhs.payload))(rhs.payload);
1158     }
1159 
1160     ///
1161     static if (is(T == int) && is(Hook == void)) @safe unittest
1162     {
1163         static struct MyHook
1164         {
1165             static bool thereWereErrors;
1166             static T onLowerBound(Rhs, T)(Rhs rhs, T bound)
1167             {
1168                 thereWereErrors = true;
1169                 return bound;
1170             }
1171             static T onUpperBound(Rhs, T)(Rhs rhs, T bound)
1172             {
1173                 thereWereErrors = true;
1174                 return bound;
1175             }
1176         }
1177         auto x = checked!MyHook(byte.min);
1178         x -= 1;
1179         assert(MyHook.thereWereErrors);
1180         MyHook.thereWereErrors = false;
1181         x = byte.max;
1182         x += 1;
1183         assert(MyHook.thereWereErrors);
1184     }
1185 }
1186 
1187 /**
1188 
1189 Convenience function that turns an integral into the corresponding `Checked`
1190 instance by using template argument deduction. The hook type may be specified
1191 (by default `Abort`).
1192 
1193 */
1194 Checked!(T, Hook) checked(Hook = Abort, T)(const T value)
1195 if (is(typeof(Checked!(T, Hook)(value))))
1196 {
1197     return Checked!(T, Hook)(value);
1198 }
1199 
1200 ///
1201 @safe unittest
1202 {
1203     static assert(is(typeof(checked(42)) == Checked!int));
1204     assert(checked(42) == Checked!int(42));
1205     static assert(is(typeof(checked!WithNaN(42)) == Checked!(int, WithNaN)));
1206     assert(checked!WithNaN(42) == Checked!(int, WithNaN)(42));
1207 }
1208 
1209 // get
1210 @safe unittest
1211 {
1212     void test(T)()
1213     {
1214         assert(Checked!(T, void)(ubyte(22)).get == 22);
1215     }
1216     test!ubyte;
1217     test!(const ubyte);
1218     test!(immutable ubyte);
1219 }
1220 
1221 @system unittest
1222 {
1223     // https://issues.dlang.org/show_bug.cgi?id=21758
1224     assert(4 * checked(5L) == 20);
1225     assert(20 / checked(5L) == 4);
1226     assert(2 ^^ checked(3L) == 8);
1227     assert(12 % checked(5L) == 2);
1228     assert((0xff & checked(3L)) == 3);
1229     assert((0xf0 | checked(3L)) == 0xf3);
1230     assert((0xff ^ checked(3L)) == 0xfc);
1231 }
1232 
1233 // Abort
1234 /**
1235 
1236 Force all integral errors to fail by printing an error message to `stderr` and
1237 then abort the program. `Abort` is the default second argument for `Checked`.
1238 
1239 */
1240 struct Abort
1241 {
1242 static:
1243     /**
1244 
1245     Called automatically upon a bad cast (one that loses precision or attempts
1246     to convert a negative value to an unsigned type). The source type is `Src`
1247     and the destination type is `Dst`.
1248 
1249     Params:
1250     src = The source of the cast
1251 
1252     Returns: Nominally the result is the desired value of the cast operation,
1253     which will be forwarded as the result of the cast. For `Abort`, the
1254     function never returns because it aborts the program.
1255 
1256     */
1257     Dst onBadCast(Dst, Src)(Src src)
1258     {
1259         Warn.onBadCast!Dst(src);
1260         assert(0);
1261     }
1262 
1263     /**
1264 
1265     Called automatically upon a bounds error.
1266 
1267     Params:
1268     rhs = The right-hand side value in the assignment, after the operator has
1269     been evaluated
1270     bound = The value of the bound being violated
1271 
1272     Returns: Nominally the result is the desired value of the operator, which
1273     will be forwarded as result. For `Abort`, the function never returns because
1274     it aborts the program.
1275 
1276     */
1277     T onLowerBound(Rhs, T)(Rhs rhs, T bound)
1278     {
1279         Warn.onLowerBound(rhs, bound);
1280         assert(0);
1281     }
1282     /// ditto
1283     T onUpperBound(Rhs, T)(Rhs rhs, T bound)
1284     {
1285         Warn.onUpperBound(rhs, bound);
1286         assert(0);
1287     }
1288 
1289     /**
1290 
1291     Called automatically upon a comparison for equality. In case of a erroneous
1292     comparison (one that would make a signed negative value appear equal to an
1293     unsigned positive value), this hook issues `assert(0)` which terminates the
1294     application.
1295 
1296     Params:
1297     lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of
1298       the operator is `Checked!int`
1299     rhs = The right-hand side type involved in the operator
1300 
1301     Returns: Upon a correct comparison, returns the result of the comparison.
1302     Otherwise, the function terminates the application so it never returns.
1303 
1304     */
1305     static bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs)
1306     {
1307         bool error;
1308         auto result = opChecked!"=="(lhs, rhs, error);
1309         if (error)
1310         {
1311             Warn.hookOpEquals(lhs, rhs);
1312             assert(0);
1313         }
1314         return result;
1315     }
1316 
1317     /**
1318 
1319     Called automatically upon a comparison for ordering using one of the
1320     operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e.
1321     it would make a signed negative value appear greater than or equal to an
1322     unsigned positive value), then application is terminated with `assert(0)`.
1323     Otherwise, the three-state result is returned (positive if $(D lhs > rhs),
1324     negative if $(D lhs < rhs), `0` otherwise).
1325 
1326     Params:
1327     lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of
1328       the operator is `Checked!int`
1329     rhs = The right-hand side type involved in the operator
1330 
1331     Returns: For correct comparisons, returns a positive integer if $(D lhs >
1332     rhs), a negative integer if  $(D lhs < rhs), `0` if the two are equal. Upon
1333     a mistaken comparison such as $(D int(-1) < uint(0)), the function never
1334     returns because it aborts the program.
1335 
1336     */
1337     int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)
1338     {
1339         bool error;
1340         auto result = opChecked!"cmp"(lhs, rhs, error);
1341         if (error)
1342         {
1343             Warn.hookOpCmp(lhs, rhs);
1344             assert(0);
1345         }
1346         return result;
1347     }
1348 
1349     /**
1350 
1351     Called automatically upon an overflow during a unary or binary operation.
1352 
1353     Params:
1354     x = The operator, e.g. `-`
1355     lhs = The left-hand side (or sole) argument
1356     rhs = The right-hand side type involved in the operator
1357 
1358     Returns: Nominally the result is the desired value of the operator, which
1359     will be forwarded as result. For `Abort`, the function never returns because
1360     it aborts the program.
1361 
1362     */
1363     typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs)
1364     {
1365         Warn.onOverflow!x(lhs);
1366         assert(0);
1367     }
1368     /// ditto
1369     typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs)
1370     {
1371         Warn.onOverflow!x(lhs, rhs);
1372         assert(0);
1373     }
1374 }
1375 
1376 @safe unittest
1377 {
1378     void test(T)()
1379     {
1380         Checked!(int, Abort) x;
1381         x = 42;
1382         auto x1 = cast(T) x;
1383         assert(x1 == 42);
1384         //x1 += long(int.max);
1385     }
1386     test!short;
1387     test!(const short);
1388     test!(immutable short);
1389 }
1390 
1391 
1392 // Throw
1393 /**
1394 
1395 Force all integral errors to fail by throwing an exception of type
1396 `Throw.CheckFailure`. The message coming with the error is similar to the one
1397 printed by `Warn`.
1398 
1399 */
1400 struct Throw
1401 {
1402     /**
1403     Exception type thrown upon any failure.
1404     */
1405     static class CheckFailure : Exception
1406     {
1407         this(T...)(string f, T vals)
1408         {
1409             import std.format : format;
1410             super(format(f, vals));
1411         }
1412     }
1413 
1414     /**
1415 
1416     Called automatically upon a bad cast (one that loses precision or attempts
1417     to convert a negative value to an unsigned type). The source type is `Src`
1418     and the destination type is `Dst`.
1419 
1420     Params:
1421     src = The source of the cast
1422 
1423     Returns: Nominally the result is the desired value of the cast operation,
1424     which will be forwarded as the result of the cast. For `Throw`, the
1425     function never returns because it throws an exception.
1426 
1427     */
1428     static Dst onBadCast(Dst, Src)(Src src)
1429     {
1430         throw new CheckFailure("Erroneous cast: cast(%s) %s(%s)",
1431             Dst.stringof, Src.stringof, src);
1432     }
1433 
1434     /**
1435 
1436     Called automatically upon a bounds error.
1437 
1438     Params:
1439     rhs = The right-hand side value in the assignment, after the operator has
1440     been evaluated
1441     bound = The value of the bound being violated
1442 
1443     Returns: Nominally the result is the desired value of the operator, which
1444     will be forwarded as result. For `Throw`, the function never returns because
1445     it throws.
1446 
1447     */
1448     static T onLowerBound(Rhs, T)(Rhs rhs, T bound)
1449     {
1450         throw new CheckFailure("Lower bound error: %s(%s) < %s(%s)",
1451             Rhs.stringof, rhs, T.stringof, bound);
1452     }
1453     /// ditto
1454     static T onUpperBound(Rhs, T)(Rhs rhs, T bound)
1455     {
1456         throw new CheckFailure("Upper bound error: %s(%s) > %s(%s)",
1457             Rhs.stringof, rhs, T.stringof, bound);
1458     }
1459 
1460     /**
1461 
1462     Called automatically upon a comparison for equality. Throws upon an
1463     erroneous comparison (one that would make a signed negative value appear
1464     equal to an unsigned positive value).
1465 
1466     Params:
1467     lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of
1468       the operator is `Checked!int`
1469     rhs = The right-hand side type involved in the operator
1470 
1471     Returns: The result of the comparison.
1472 
1473     Throws: `CheckFailure` if the comparison is mathematically erroneous.
1474 
1475     */
1476     static bool hookOpEquals(L, R)(L lhs, R rhs)
1477     {
1478         bool error;
1479         auto result = opChecked!"=="(lhs, rhs, error);
1480         if (error)
1481         {
1482             throw new CheckFailure("Erroneous comparison: %s(%s) == %s(%s)",
1483                 L.stringof, lhs, R.stringof, rhs);
1484         }
1485         return result;
1486     }
1487 
1488     /**
1489 
1490     Called automatically upon a comparison for ordering using one of the
1491     operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e.
1492     it would make a signed negative value appear greater than or equal to an
1493     unsigned positive value), throws a `Throw.CheckFailure` exception.
1494     Otherwise, the three-state result is returned (positive if $(D lhs > rhs),
1495     negative if $(D lhs < rhs), `0` otherwise).
1496 
1497     Params:
1498     lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of
1499       the operator is `Checked!int`
1500     rhs = The right-hand side type involved in the operator
1501 
1502     Returns: For correct comparisons, returns a positive integer if $(D lhs >
1503     rhs), a negative integer if  $(D lhs < rhs), `0` if the two are equal.
1504 
1505     Throws: Upon a mistaken comparison such as $(D int(-1) < uint(0)), the
1506     function never returns because it throws a `Throw.CheckedFailure` exception.
1507 
1508     */
1509     static int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)
1510     {
1511         bool error;
1512         auto result = opChecked!"cmp"(lhs, rhs, error);
1513         if (error)
1514         {
1515             throw new CheckFailure("Erroneous ordering comparison: %s(%s) and %s(%s)",
1516                 Lhs.stringof, lhs, Rhs.stringof, rhs);
1517         }
1518         return result;
1519     }
1520 
1521     /**
1522 
1523     Called automatically upon an overflow during a unary or binary operation.
1524 
1525     Params:
1526     x = The operator, e.g. `-`
1527     lhs = The left-hand side (or sole) argument
1528     rhs = The right-hand side type involved in the operator
1529 
1530     Returns: Nominally the result is the desired value of the operator, which
1531     will be forwarded as result. For `Throw`, the function never returns because
1532     it throws an exception.
1533 
1534     */
1535     static typeof(~Lhs()) onOverflow(string x, Lhs)(Lhs lhs)
1536     {
1537         throw new CheckFailure("Overflow on unary operator: %s%s(%s)",
1538             x, Lhs.stringof, lhs);
1539     }
1540     /// ditto
1541     static typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs)
1542     {
1543         throw new CheckFailure("Overflow on binary operator: %s(%s) %s %s(%s)",
1544             Lhs.stringof, lhs, x, Rhs.stringof, rhs);
1545     }
1546 }
1547 
1548 ///
1549 @safe unittest
1550 {
1551     void test(T)()
1552     {
1553         Checked!(int, Throw) x;
1554         x = 42;
1555         auto x1 = cast(T) x;
1556         assert(x1 == 42);
1557         x = T.max + 1;
1558         import std.exception : assertThrown, assertNotThrown;
1559         assertThrown(cast(T) x);
1560         x = x.max;
1561         assertThrown(x += 42);
1562         assertThrown(x += 42L);
1563         x = x.min;
1564         assertThrown(-x);
1565         assertThrown(x -= 42);
1566         assertThrown(x -= 42L);
1567         x = -1;
1568         assertNotThrown(x == -1);
1569         assertThrown(x == uint(-1));
1570         assertNotThrown(x <= -1);
1571         assertThrown(x <= uint(-1));
1572     }
1573     test!short;
1574     test!(const short);
1575     test!(immutable short);
1576 }
1577 
1578 // Warn
1579 /**
1580 Hook that prints to `stderr` a trace of all integral errors, without affecting
1581 default behavior.
1582 */
1583 struct Warn
1584 {
1585     import std.stdio : writefln;
1586 static:
1587     /**
1588 
1589     Called automatically upon a bad cast from `src` to type `Dst` (one that
1590     loses precision or attempts to convert a negative value to an unsigned
1591     type).
1592 
1593     Params:
1594     src = The source of the cast
1595     Dst = The target type of the cast
1596 
1597     Returns: `cast(Dst) src`
1598 
1599     */
1600     Dst onBadCast(Dst, Src)(Src src)
1601     {
1602         trustedStderr.writefln("Erroneous cast: cast(%s) %s(%s)",
1603             Dst.stringof, Src.stringof, src);
1604         return cast(Dst) src;
1605     }
1606 
1607     /**
1608 
1609     Called automatically upon a bad `opOpAssign` call (one that loses precision
1610     or attempts to convert a negative value to an unsigned type).
1611 
1612     Params:
1613     rhs = The right-hand side value in the assignment, after the operator has
1614     been evaluated
1615     bound = The bound being violated
1616 
1617     Returns: `cast(Lhs) rhs`
1618     */
1619     Lhs onLowerBound(Rhs, T)(Rhs rhs, T bound)
1620     {
1621         trustedStderr.writefln("Lower bound error: %s(%s) < %s(%s)",
1622             Rhs.stringof, rhs, T.stringof, bound);
1623         return cast(T) rhs;
1624     }
1625     /// ditto
1626     T onUpperBound(Rhs, T)(Rhs rhs, T bound)
1627     {
1628         trustedStderr.writefln("Upper bound error: %s(%s) > %s(%s)",
1629             Rhs.stringof, rhs, T.stringof, bound);
1630         return cast(T) rhs;
1631     }
1632 
1633     /**
1634 
1635     Called automatically upon a comparison for equality. In case of an Erroneous
1636     comparison (one that would make a signed negative value appear equal to an
1637     unsigned positive value), writes a warning message to `stderr` as a side
1638     effect.
1639 
1640     Params:
1641     lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of
1642       the operator is `Checked!int`
1643     rhs = The right-hand side type involved in the operator
1644 
1645     Returns: In all cases the function returns the built-in result of $(D lhs ==
1646     rhs).
1647 
1648     */
1649     bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs)
1650     {
1651         bool error;
1652         auto result = opChecked!"=="(lhs, rhs, error);
1653         if (error)
1654         {
1655             trustedStderr.writefln("Erroneous comparison: %s(%s) == %s(%s)",
1656                 Lhs.stringof, lhs, Rhs.stringof, rhs);
1657             return lhs == rhs;
1658         }
1659         return result;
1660     }
1661 
1662     ///
1663     @safe unittest
1664     {
1665         auto x = checked!Warn(-42);
1666         // Passes
1667         assert(x == -42);
1668         // Passes but prints a warning
1669         // assert(x == uint(-42));
1670     }
1671 
1672     /**
1673 
1674     Called automatically upon a comparison for ordering using one of the
1675     operators `<`, `<=`, `>`, or `>=`. In case the comparison is erroneous (i.e.
1676     it would make a signed negative value appear greater than or equal to an
1677     unsigned positive value), then a warning message is printed to `stderr`.
1678 
1679     Params:
1680     lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of
1681       the operator is `Checked!int`
1682     rhs = The right-hand side type involved in the operator
1683 
1684     Returns: In all cases, returns $(D lhs < rhs ? -1 : lhs > rhs). The result
1685     is  not autocorrected in case of an erroneous comparison.
1686 
1687     */
1688     int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)
1689     {
1690         bool error;
1691         auto result = opChecked!"cmp"(lhs, rhs, error);
1692         if (error)
1693         {
1694             trustedStderr.writefln("Erroneous ordering comparison: %s(%s) and %s(%s)",
1695                 Lhs.stringof, lhs, Rhs.stringof, rhs);
1696             return lhs < rhs ? -1 : lhs > rhs;
1697         }
1698         return result;
1699     }
1700 
1701     ///
1702     @safe unittest
1703     {
1704         auto x = checked!Warn(-42);
1705         // Passes
1706         assert(x <= -42);
1707         // Passes but prints a warning
1708         // assert(x <= uint(-42));
1709     }
1710 
1711     /**
1712 
1713     Called automatically upon an overflow during a unary or binary operation.
1714 
1715     Params:
1716     x = The operator involved
1717     Lhs = The first argument of `Checked`, e.g. `int` if the left-hand side of
1718       the operator is `Checked!int`
1719     Rhs = The right-hand side type involved in the operator
1720 
1721     Returns: $(D mixin(x ~ "lhs")) for unary, $(D mixin("lhs" ~ x ~ "rhs")) for
1722     binary
1723 
1724     */
1725     typeof(~Lhs()) onOverflow(string x, Lhs)(ref Lhs lhs)
1726     {
1727         trustedStderr.writefln("Overflow on unary operator: %s%s(%s)",
1728             x, Lhs.stringof, lhs);
1729         return mixin(x ~ "lhs");
1730     }
1731     /// ditto
1732     typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs)
1733     {
1734         trustedStderr.writefln("Overflow on binary operator: %s(%s) %s %s(%s)",
1735             Lhs.stringof, lhs, x, Rhs.stringof, rhs);
1736         static if (x == "/")               // Issue 20743: mixin below would cause SIGFPE on POSIX
1737             return typeof(lhs / rhs).min;  // or EXCEPTION_INT_OVERFLOW on Windows
1738         else
1739             return mixin("lhs" ~ x ~ "rhs");
1740     }
1741 
1742     // This is safe because we do not assign to the reference returned by
1743     // `stderr`. The ability for the caller to do that is why `stderr` is not
1744     // safe in the general case.
1745     private @property auto ref trustedStderr() @trusted
1746     {
1747         import std.stdio : stderr;
1748 
1749         return stderr;
1750     }
1751 }
1752 
1753 ///
1754 @safe unittest
1755 {
1756     auto x = checked!Warn(42);
1757     short x1 = cast(short) x;
1758     //x += long(int.max);
1759     auto y = checked!Warn(cast(const int) 42);
1760     short y1 = cast(const byte) y;
1761 }
1762 
1763 @system unittest
1764 {
1765     auto a = checked!Warn(int.min);
1766     auto b = checked!Warn(-1);
1767     auto x = checked!Abort(int.min);
1768     auto y = checked!Abort(-1);
1769 
1770     // Temporarily redirect output to stderr to make sure we get the right output.
1771     import std.file : exists, remove;
1772     import std.process : uniqueTempPath;
1773     import std.stdio : stderr;
1774     auto tmpname = uniqueTempPath;
1775     scope(exit) if (exists(tmpname)) remove(tmpname);
1776     auto t = stderr;
1777     stderr.open(tmpname, "w");
1778     // Open a new scope to minimize code ran with stderr redirected.
1779     {
1780         scope(exit) stderr = t;
1781         assert(a / b == a * b);
1782         import std.exception : assertThrown;
1783         import core.exception : AssertError;
1784         assertThrown!AssertError(x / y);
1785     }
1786     import std.file : readText;
1787     import std.ascii : newline;
1788     auto witness = readText(tmpname);
1789     auto expected =
1790 "Overflow on binary operator: int(-2147483648) / const(int)(-1)" ~ newline ~
1791 "Overflow on binary operator: int(-2147483648) * const(int)(-1)" ~ newline ~
1792 "Overflow on binary operator: int(-2147483648) / const(int)(-1)" ~ newline;
1793     assert(witness == expected, "'" ~ witness ~ "'");
1794 }
1795 
1796 // ProperCompare
1797 /**
1798 
1799 Hook that provides arithmetically correct comparisons for equality and ordering.
1800 Comparing an object of type $(D Checked!(X, ProperCompare)) against another
1801 integral (for equality or ordering) ensures that no surprising conversions from
1802 signed to unsigned integral occur before the comparison. Using $(D Checked!(X,
1803 ProperCompare)) on either side of a comparison for equality against a
1804 floating-point number makes sure the integral can be properly converted to the
1805 floating point type, thus making sure equality is transitive.
1806 
1807 */
1808 struct ProperCompare
1809 {
1810     /**
1811     Hook for `==` and `!=` that ensures comparison against integral values has
1812     the behavior expected by the usual arithmetic rules. The built-in semantics
1813     yield surprising behavior when comparing signed values against unsigned
1814     values for equality, for example $(D uint.max == -1) or $(D -1_294_967_296 ==
1815     3_000_000_000u). The call $(D hookOpEquals(x, y)) returns `true` if and only
1816     if `x` and `y` represent the same arithmetic number.
1817 
1818     If one of the numbers is an integral and the other is a floating-point
1819     number, $(D hookOpEquals(x, y)) returns `true` if and only if the integral
1820     can be converted exactly (without approximation) to the floating-point
1821     number. This is in order to preserve transitivity of equality: if $(D
1822     hookOpEquals(x, y)) and $(D hookOpEquals(y, z)) then $(D hookOpEquals(y,
1823     z)), in case `x`, `y`, and `z` are a mix of integral and floating-point
1824     numbers.
1825 
1826     Params:
1827     lhs = The left-hand side of the comparison for equality
1828     rhs = The right-hand side of the comparison for equality
1829 
1830     Returns:
1831     The result of the comparison, `true` if the values are equal
1832     */
1833     static bool hookOpEquals(L, R)(L lhs, R rhs)
1834     {
1835         alias C = typeof(lhs + rhs);
1836         static if (isFloatingPoint!C)
1837         {
1838             static if (!isFloatingPoint!L)
1839             {
1840                 return hookOpEquals(rhs, lhs);
1841             }
1842             else static if (!isFloatingPoint!R)
1843             {
1844                 static assert(isFloatingPoint!L && !isFloatingPoint!R);
1845                 auto rhs1 = C(rhs);
1846                 return lhs == rhs1 && cast(R) rhs1 == rhs;
1847             }
1848             else
1849                 return lhs == rhs;
1850         }
1851         else
1852         {
1853             bool error;
1854             auto result = opChecked!"=="(lhs, rhs, error);
1855             if (error)
1856             {
1857                 // Only possible error is a wrong "true"
1858                 return false;
1859             }
1860             return result;
1861         }
1862     }
1863 
1864     /**
1865     Hook for `<`, `<=`, `>`, and `>=` that ensures comparison against integral
1866     values has the behavior expected by the usual arithmetic rules. The built-in
1867     semantics yield surprising behavior when comparing signed values against
1868     unsigned values, for example $(D 0u < -1). The call $(D hookOpCmp(x, y))
1869     returns `-1` if and only if `x` is smaller than `y` in abstract arithmetic
1870     sense.
1871 
1872     If one of the numbers is an integral and the other is a floating-point
1873     number, $(D hookOpEquals(x, y)) returns a floating-point number that is `-1`
1874     if `x < y`, `0` if `x == y`, `1` if `x > y`, and `NaN` if the floating-point
1875     number is `NaN`.
1876 
1877     Params:
1878     lhs = The left-hand side of the comparison for ordering
1879     rhs = The right-hand side of the comparison for ordering
1880 
1881     Returns:
1882     The result of the comparison (negative if $(D lhs < rhs), positive if $(D
1883     lhs > rhs), `0` if the values are equal)
1884     */
1885     static auto hookOpCmp(L, R)(L lhs, R rhs)
1886     {
1887         alias C = typeof(lhs + rhs);
1888         static if (isFloatingPoint!C)
1889         {
1890             return lhs < rhs
1891                 ? C(-1)
1892                 : lhs > rhs ? C(1) : lhs == rhs ? C(0) : C.init;
1893         }
1894         else
1895         {
1896             static if (!valueConvertible!(L, C) || !valueConvertible!(R, C))
1897             {
1898                 static assert(isUnsigned!C);
1899                 static assert(isUnsigned!L != isUnsigned!R);
1900                 if (!isUnsigned!L && lhs < 0)
1901                     return -1;
1902                 if (!isUnsigned!R && rhs < 0)
1903                     return 1;
1904             }
1905             return lhs < rhs ? -1 : lhs > rhs;
1906         }
1907     }
1908 }
1909 
1910 ///
1911 @safe unittest
1912 {
1913     alias opEqualsProper = ProperCompare.hookOpEquals;
1914     assert(opEqualsProper(42, 42));
1915     assert(opEqualsProper(42.0, 42.0));
1916     assert(opEqualsProper(42u, 42));
1917     assert(opEqualsProper(42, 42u));
1918     assert(-1 == 4294967295u);
1919     assert(!opEqualsProper(-1, 4294967295u));
1920     assert(!opEqualsProper(const uint(-1), -1));
1921     assert(!opEqualsProper(uint(-1), -1.0));
1922     assert(3_000_000_000U == -1_294_967_296);
1923     assert(!opEqualsProper(3_000_000_000U, -1_294_967_296));
1924 }
1925 
1926 @safe unittest
1927 {
1928     alias opCmpProper = ProperCompare.hookOpCmp;
1929     assert(opCmpProper(42, 42) == 0);
1930     assert(opCmpProper(42, 42.0) == 0);
1931     assert(opCmpProper(41, 42.0) < 0);
1932     assert(opCmpProper(42, 41.0) > 0);
1933     import std.math.traits : isNaN;
1934     assert(isNaN(opCmpProper(41, double.init)));
1935     assert(opCmpProper(42u, 42) == 0);
1936     assert(opCmpProper(42, 42u) == 0);
1937     assert(opCmpProper(-1, uint(-1)) < 0);
1938     assert(opCmpProper(uint(-1), -1) > 0);
1939     assert(opCmpProper(-1.0, -1) == 0);
1940 }
1941 
1942 @safe unittest
1943 {
1944     auto x1 = Checked!(uint, ProperCompare)(42u);
1945     assert(x1.get < -1);
1946     assert(x1 > -1);
1947 }
1948 
1949 // WithNaN
1950 /**
1951 
1952 Hook that reserves a special value as a "Not a Number" representative. For
1953 signed integrals, the reserved value is `T.min`. For signed integrals, the
1954 reserved value is `T.max`.
1955 
1956 The default value of a $(D Checked!(X, WithNaN)) is its NaN value, so care must
1957 be taken that all variables are explicitly initialized. Any arithmetic and logic
1958 operation involving at least on NaN becomes NaN itself. All of $(D a == b), $(D
1959 a < b), $(D a > b), $(D a <= b), $(D a >= b) yield `false` if at least one of
1960 `a` and `b` is NaN.
1961 
1962 */
1963 struct WithNaN
1964 {
1965 static:
1966     /**
1967     The default value used for values not explicitly initialized. It is the NaN
1968     value, i.e. `T.min` for signed integrals and `T.max` for unsigned integrals.
1969     */
1970     enum T defaultValue(T) = T.min == 0 ? T.max : T.min;
1971     /**
1972     The maximum value representable is `T.max` for signed integrals, $(D
1973     T.max - 1) for unsigned integrals. The minimum value representable is $(D
1974     T.min + 1) for signed integrals, `0` for unsigned integrals.
1975     */
1976     enum T max(T) = cast(T) (T.min == 0 ? T.max - 1 : T.max);
1977     /// ditto
1978     enum T min(T) = cast(T) (T.min == 0 ? T(0) : T.min + 1);
1979 
1980     /**
1981     If `rhs` is `WithNaN.defaultValue!Rhs`, returns
1982     `WithNaN.defaultValue!Lhs`. Otherwise, returns $(D cast(Lhs) rhs).
1983 
1984     Params:
1985     rhs = the value being cast (`Rhs` is the first argument to `Checked`)
1986     Lhs = the target type of the cast
1987 
1988     Returns: The result of the cast operation.
1989     */
1990     Lhs hookOpCast(Lhs, Rhs)(Rhs rhs)
1991     {
1992         static if (is(Lhs == bool))
1993         {
1994             return rhs != defaultValue!Rhs && rhs != 0;
1995         }
1996         else static if (valueConvertible!(Rhs, Lhs))
1997         {
1998             return rhs != defaultValue!Rhs ? Lhs(rhs) : defaultValue!Lhs;
1999         }
2000         else
2001         {
2002             // Not value convertible, only viable option is rhs fits within the
2003             // bounds of Lhs
2004             static if (ProperCompare.hookOpCmp(Rhs.min, Lhs.min) < 0)
2005             {
2006                 // Example: hookOpCast!short(int(42)), hookOpCast!uint(int(42))
2007                 if (ProperCompare.hookOpCmp(rhs, Lhs.min) < 0)
2008                     return defaultValue!Lhs;
2009             }
2010             static if (ProperCompare.hookOpCmp(Rhs.max, Lhs.max) > 0)
2011             {
2012                 // Example: hookOpCast!int(uint(42))
2013                 if (ProperCompare.hookOpCmp(rhs, Lhs.max) > 0)
2014                     return defaultValue!Lhs;
2015             }
2016             return cast(Lhs) rhs;
2017         }
2018     }
2019 
2020     ///
2021     @safe unittest
2022     {
2023         auto x = checked!WithNaN(422);
2024         assert((cast(ubyte) x) == 255);
2025         x = checked!WithNaN(-422);
2026         assert((cast(byte) x) == -128);
2027         assert(cast(short) x == -422);
2028         assert(cast(bool) x);
2029         x = x.init; // set back to NaN
2030         assert(x != true);
2031         assert(x != false);
2032     }
2033 
2034     /**
2035 
2036     Returns `false` if $(D lhs == WithNaN.defaultValue!Lhs), $(D lhs == rhs)
2037     otherwise.
2038 
2039     Params:
2040     lhs = The left-hand side of the comparison (`Lhs` is the first argument to
2041     `Checked`)
2042     rhs = The right-hand side of the comparison
2043 
2044     Returns: `lhs != WithNaN.defaultValue!Lhs && lhs == rhs`
2045     */
2046     bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs)
2047     {
2048         return lhs != defaultValue!Lhs && lhs == rhs;
2049     }
2050 
2051     /**
2052 
2053     If $(D lhs == WithNaN.defaultValue!Lhs), returns `double.init`. Otherwise,
2054     has the same semantics as the default comparison.
2055 
2056     Params:
2057     lhs = The left-hand side of the comparison (`Lhs` is the first argument to
2058     `Checked`)
2059     rhs = The right-hand side of the comparison
2060 
2061     Returns: `double.init` if `lhs == WitnNaN.defaultValue!Lhs`, `-1.0` if $(D
2062     lhs < rhs), `0.0` if $(D lhs == rhs), `1.0` if $(D lhs > rhs).
2063 
2064     */
2065     double hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)
2066     {
2067         if (lhs == defaultValue!Lhs) return double.init;
2068         return lhs < rhs
2069             ? -1.0
2070             : lhs > rhs ? 1.0 : lhs == rhs ? 0.0 : double.init;
2071     }
2072 
2073     ///
2074     @safe unittest
2075     {
2076         Checked!(int, WithNaN) x;
2077         assert(!(x < 0) && !(x > 0) && !(x == 0));
2078         x = 1;
2079         assert(x > 0 && !(x < 0) && !(x == 0));
2080     }
2081 
2082     /**
2083     Defines hooks for unary operators `-`, `~`, `++`, and `--`.
2084 
2085     For `-` and `~`, if $(D v == WithNaN.defaultValue!T), returns
2086     `WithNaN.defaultValue!T`. Otherwise, the semantics is the same as for the
2087     built-in operator.
2088 
2089     For `++` and `--`, if $(D v == WithNaN.defaultValue!Lhs) or the operation
2090     would result in an overflow, sets `v` to `WithNaN.defaultValue!T`.
2091     Otherwise, the semantics is the same as for the built-in operator.
2092 
2093     Params:
2094     x = The operator symbol
2095     v = The left-hand side of the comparison (`T` is the first argument to
2096     `Checked`)
2097 
2098     Returns: $(UL $(LI For $(D x == "-" || x == "~"): If  $(D v ==
2099     WithNaN.defaultValue!T), the function returns `WithNaN.defaultValue!T`.
2100     Otherwise it returns the normal result of the operator.) $(LI For $(D x ==
2101     "++" || x == "--"): The function returns `void`.))
2102 
2103     */
2104     auto hookOpUnary(string x, T)(ref T v)
2105     {
2106         static if (x == "-" || x == "~")
2107         {
2108             return v != defaultValue!T ? mixin(x ~ "v") : v;
2109         }
2110         else static if (x == "++")
2111         {
2112             static if (defaultValue!T == T.min)
2113             {
2114                 if (v != defaultValue!T)
2115                 {
2116                     if (v == T.max) v = defaultValue!T;
2117                     else ++v;
2118                 }
2119             }
2120             else
2121             {
2122                 static assert(defaultValue!T == T.max);
2123                 if (v != defaultValue!T) ++v;
2124             }
2125         }
2126         else static if (x == "--")
2127         {
2128             if (v != defaultValue!T) --v;
2129         }
2130     }
2131 
2132     ///
2133     @safe unittest
2134     {
2135         Checked!(int, WithNaN) x;
2136         ++x;
2137         assert(x.isNaN);
2138         x = 1;
2139         assert(!x.isNaN);
2140         x = -x;
2141         ++x;
2142         assert(!x.isNaN);
2143     }
2144 
2145     @safe unittest // for coverage
2146     {
2147         Checked!(uint, WithNaN) y;
2148         ++y;
2149         assert(y.isNaN);
2150     }
2151 
2152     /**
2153     Defines hooks for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`,
2154      `^`, `<<`, `>>`, and `>>>` for cases where a `Checked` object is the
2155     left-hand side operand. If $(D lhs == WithNaN.defaultValue!Lhs), returns
2156     $(D WithNaN.defaultValue!(typeof(lhs + rhs))) without evaluating the
2157     operand. Otherwise, evaluates the operand. If evaluation does not overflow,
2158     returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs +
2159     rhs))).
2160 
2161     Params:
2162     x = The operator symbol
2163     lhs = The left-hand side operand (`Lhs` is the first argument to `Checked`)
2164     rhs = The right-hand side operand
2165 
2166     Returns: If $(D lhs != WithNaN.defaultValue!Lhs) and the operator does not
2167     overflow, the function returns the same result as the built-in operator. In
2168     all other cases, returns $(D WithNaN.defaultValue!(typeof(lhs + rhs))).
2169     */
2170     auto hookOpBinary(string x, L, R)(L lhs, R rhs)
2171     {
2172         alias Result = typeof(lhs + rhs);
2173         if (lhs != defaultValue!L)
2174         {
2175             bool error;
2176             auto result = opChecked!x(lhs, rhs, error);
2177             if (!error) return result;
2178         }
2179         return defaultValue!Result;
2180     }
2181 
2182     ///
2183     @safe unittest
2184     {
2185         Checked!(int, WithNaN) x;
2186         assert((x + 1).isNaN);
2187         x = 100;
2188         assert(!(x + 1).isNaN);
2189     }
2190 
2191     /**
2192     Defines hooks for binary operators `+`, `-`, `*`, `/`, `%`, `^^`, `&`, `|`,
2193      `^`, `<<`, `>>`, and `>>>` for cases where a `Checked` object is the
2194     right-hand side operand. If $(D rhs == WithNaN.defaultValue!Rhs), returns
2195     $(D WithNaN.defaultValue!(typeof(lhs + rhs))) without evaluating the
2196     operand. Otherwise, evaluates the operand. If evaluation does not overflow,
2197     returns the result. Otherwise, returns $(D WithNaN.defaultValue!(typeof(lhs +
2198     rhs))).
2199 
2200     Params:
2201     x = The operator symbol
2202     lhs = The left-hand side operand
2203     rhs = The right-hand side operand (`Rhs` is the first argument to `Checked`)
2204 
2205     Returns: If $(D rhs != WithNaN.defaultValue!Rhs) and the operator does not
2206     overflow, the function returns the same result as the built-in operator. In
2207     all other cases, returns $(D WithNaN.defaultValue!(typeof(lhs + rhs))).
2208     */
2209     auto hookOpBinaryRight(string x, L, R)(L lhs, R rhs)
2210     {
2211         alias Result = typeof(lhs + rhs);
2212         if (rhs != defaultValue!R)
2213         {
2214             bool error;
2215             auto result = opChecked!x(lhs, rhs, error);
2216             if (!error) return result;
2217         }
2218         return defaultValue!Result;
2219     }
2220     ///
2221     @safe unittest
2222     {
2223         Checked!(int, WithNaN) x;
2224         assert((1 + x).isNaN);
2225         x = 100;
2226         assert(!(1 + x).isNaN);
2227     }
2228 
2229     /**
2230 
2231     Defines hooks for binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`,
2232     `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=` for cases where a `Checked`
2233     object is the left-hand side operand. If $(D lhs ==
2234     WithNaN.defaultValue!Lhs), no action is carried. Otherwise, evaluates the
2235     operand. If evaluation does not overflow and fits in `Lhs` without loss of
2236     information or change of sign, sets `lhs` to the result. Otherwise, sets
2237     `lhs` to `WithNaN.defaultValue!Lhs`.
2238 
2239     Params:
2240     x = The operator symbol (without the `=`)
2241     lhs = The left-hand side operand (`Lhs` is the first argument to `Checked`)
2242     rhs = The right-hand side operand
2243 
2244     Returns: `void`
2245     */
2246     void hookOpOpAssign(string x, L, R)(ref L lhs, R rhs)
2247     {
2248         if (lhs == defaultValue!L)
2249             return;
2250         bool error;
2251         auto temp = opChecked!x(lhs, rhs, error);
2252         lhs = error
2253             ? defaultValue!L
2254             : hookOpCast!L(temp);
2255     }
2256 
2257     ///
2258     @safe unittest
2259     {
2260         Checked!(int, WithNaN) x;
2261         x += 4;
2262         assert(x.isNaN);
2263         x = 0;
2264         x += 4;
2265         assert(!x.isNaN);
2266         x += int.max;
2267         assert(x.isNaN);
2268     }
2269 }
2270 
2271 ///
2272 @safe unittest
2273 {
2274     auto x1 = Checked!(int, WithNaN)();
2275     assert(x1.isNaN);
2276     assert(x1.get == int.min);
2277     assert(x1 != x1);
2278     assert(!(x1 < x1));
2279     assert(!(x1 > x1));
2280     assert(!(x1 == x1));
2281     ++x1;
2282     assert(x1.isNaN);
2283     assert(x1.get == int.min);
2284     --x1;
2285     assert(x1.isNaN);
2286     assert(x1.get == int.min);
2287     x1 = 42;
2288     assert(!x1.isNaN);
2289     assert(x1 == x1);
2290     assert(x1 <= x1);
2291     assert(x1 >= x1);
2292     static assert(x1.min == int.min + 1);
2293     x1 += long(int.max);
2294 }
2295 
2296 /**
2297 Queries whether a $(D Checked!(T, WithNaN)) object is not a number (NaN).
2298 
2299 Params: x = the `Checked` instance queried
2300 
2301 Returns: `true` if `x` is a NaN, `false` otherwise
2302 */
2303 bool isNaN(T)(const Checked!(T, WithNaN) x)
2304 {
2305     return x.get == x.init.get;
2306 }
2307 
2308 ///
2309 @safe unittest
2310 {
2311     auto x1 = Checked!(int, WithNaN)();
2312     assert(x1.isNaN);
2313     x1 = 1;
2314     assert(!x1.isNaN);
2315     x1 = x1.init;
2316     assert(x1.isNaN);
2317 }
2318 
2319 @safe unittest
2320 {
2321     void test1(T)()
2322     {
2323         auto x1 = Checked!(T, WithNaN)();
2324         assert(x1.isNaN);
2325         assert(x1.get == int.min);
2326         assert(x1 != x1);
2327         assert(!(x1 < x1));
2328         assert(!(x1 > x1));
2329         assert(!(x1 == x1));
2330         assert(x1.get == int.min);
2331         auto x2 = Checked!(T, WithNaN)(42);
2332         assert(!x2.isNaN);
2333         assert(x2 == x2);
2334         assert(x2 <= x2);
2335         assert(x2 >= x2);
2336         static assert(x2.min == T.min + 1);
2337     }
2338     test1!int;
2339     test1!(const int);
2340     test1!(immutable int);
2341 
2342     void test2(T)()
2343     {
2344         auto x1 = Checked!(T, WithNaN)();
2345         assert(x1.get == T.min);
2346         assert(x1 != x1);
2347         assert(!(x1 < x1));
2348         assert(!(x1 > x1));
2349         assert(!(x1 == x1));
2350         ++x1;
2351         assert(x1.get == T.min);
2352         --x1;
2353         assert(x1.get == T.min);
2354         x1 = 42;
2355         assert(x1 == x1);
2356         assert(x1 <= x1);
2357         assert(x1 >= x1);
2358         static assert(x1.min == T.min + 1);
2359         x1 += long(T.max);
2360     }
2361     test2!int;
2362 }
2363 
2364 @safe unittest
2365 {
2366     alias Smart(T) = Checked!(Checked!(T, ProperCompare), WithNaN);
2367     Smart!int x1;
2368     assert(x1 != x1);
2369     x1 = -1;
2370     assert(x1 < 1u);
2371     auto x2 = Smart!(const int)(42);
2372 }
2373 
2374 // Saturate
2375 /**
2376 
2377 Hook that implements $(I saturation), i.e. any arithmetic operation that would
2378 overflow leaves the result at its extreme value (`min` or `max` depending on the
2379 direction of the overflow).
2380 
2381 Saturation is not sticky; if a value reaches its saturation value, another
2382 operation may take it back to normal range.
2383 
2384 */
2385 struct Saturate
2386 {
2387 static:
2388     /**
2389 
2390     Implements saturation for operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`,
2391     and `>>>=`. This hook is called if the result of the binary operation does
2392     not fit in `Lhs` without loss of information or a change in sign.
2393 
2394     Params:
2395     Rhs = The right-hand side type in the assignment, after the operation has
2396     been computed
2397     bound = The bound being violated
2398 
2399     Returns: `Lhs.max` if $(D rhs >= 0), `Lhs.min` otherwise.
2400 
2401     */
2402     T onLowerBound(Rhs, T)(Rhs rhs, T bound)
2403     {
2404         return bound;
2405     }
2406     /// ditto
2407     T onUpperBound(Rhs, T)(Rhs rhs, T bound)
2408     {
2409         return bound;
2410     }
2411     ///
2412     @safe unittest
2413     {
2414         auto x = checked!Saturate(short(100));
2415         x += 33000;
2416         assert(x == short.max);
2417         x -= 70000;
2418         assert(x == short.min);
2419     }
2420 
2421     /**
2422 
2423     Implements saturation for operators `+`, `-` (unary and binary), `*`, `/`,
2424     `%`, `^^`, `&`, `|`, `^`, `<<`, `>>`, and `>>>`.
2425 
2426     For unary `-`, `onOverflow` is called if $(D lhs == Lhs.min) and `Lhs` is a
2427     signed type. The function returns `Lhs.max`.
2428 
2429     For binary operators, the result is as follows: $(UL $(LI `Lhs.max` if the
2430     result overflows in the positive direction, on division by `0`, or on
2431     shifting right by a negative value) $(LI `Lhs.min` if the result overflows
2432     in the negative direction) $(LI `0` if `lhs` is being shifted left by a
2433     negative value, or shifted right by a large positive value))
2434 
2435     Params:
2436     x = The operator involved in the `opAssign` operation
2437     Lhs = The left-hand side of the operator (`Lhs` is the first argument to
2438     `Checked`)
2439     Rhs = The right-hand side type in the operator
2440 
2441     Returns: The saturated result of the operator.
2442 
2443     */
2444     auto onOverflow(string x, Lhs)(Lhs lhs)
2445     {
2446         static assert(x == "-" || x == "++" || x == "--");
2447         return x == "--" ? Lhs.min : Lhs.max;
2448     }
2449     /// ditto
2450     typeof(Lhs() + Rhs()) onOverflow(string x, Lhs, Rhs)(Lhs lhs, Rhs rhs)
2451     {
2452         static if (x == "+")
2453             return rhs >= 0 ? Lhs.max : Lhs.min;
2454         else static if (x == "*")
2455             return (lhs >= 0) == (rhs >= 0) ? Lhs.max : Lhs.min;
2456         else static if (x == "^^")
2457             return lhs > 0 || !(rhs & 1) ? Lhs.max : Lhs.min;
2458         else static if (x == "-")
2459             return rhs >= 0 ? Lhs.min : Lhs.max;
2460         else static if (x == "/" || x == "%")
2461             return Lhs.max;
2462         else static if (x == "<<")
2463             return rhs >= 0 ? Lhs.max : 0;
2464         else static if (x == ">>" || x == ">>>")
2465             return rhs >= 0 ? 0 : Lhs.max;
2466         else
2467             static assert(false);
2468     }
2469     ///
2470     @safe unittest
2471     {
2472         assert(checked!Saturate(int.max) + 1 == int.max);
2473         assert(checked!Saturate(100) ^^ 10 == int.max);
2474         assert(checked!Saturate(-100) ^^ 10 == int.max);
2475         assert(checked!Saturate(100) / 0 == int.max);
2476         assert(checked!Saturate(100) << -1 == 0);
2477         assert(checked!Saturate(100) << 33 == int.max);
2478         assert(checked!Saturate(100) >> -1 == int.max);
2479         assert(checked!Saturate(100) >> 33 == 0);
2480     }
2481 }
2482 
2483 ///
2484 @safe unittest
2485 {
2486     auto x = checked!Saturate(int.max);
2487     ++x;
2488     assert(x == int.max);
2489     --x;
2490     assert(x == int.max - 1);
2491     x = int.min;
2492     assert(-x == int.max);
2493     x -= 42;
2494     assert(x == int.min);
2495     assert(x * -2 == int.max);
2496 }
2497 
2498 /*
2499 Yields `true` if `T1` is "value convertible" (by C's "value preserving" rule,
2500 see $(HTTP c-faq.com/expr/preservingrules.html)) to `T2`, where the two are
2501 integral types. That is, all of values in `T1` are also in `T2`. For example
2502 `int` is value convertible to `long` but not to `uint` or `ulong`.
2503 */
2504 private enum valueConvertible(T1, T2) = isIntegral!T1 && isIntegral!T2 &&
2505     is(T1 : T2) && (
2506         isUnsigned!T1 == isUnsigned!T2 || // same signedness
2507         !isUnsigned!T2 && T2.sizeof > T1.sizeof // safely convertible
2508     );
2509 
2510 /**
2511 
2512 Defines binary operations with overflow checking for any two integral types.
2513 The result type obeys the language rules (even when they may be
2514 counterintuitive), and `overflow` is set if an overflow occurs (including
2515 inadvertent change of signedness, e.g. `-1` is converted to `uint`).
2516 Conceptually the behavior is:
2517 
2518 $(OL $(LI Perform the operation in infinite precision)
2519 $(LI If the infinite-precision result fits in the result type, return it and
2520 do not touch `overflow`)
2521 $(LI Otherwise, set `overflow` to `true` and return an unspecified value)
2522 )
2523 
2524 The implementation exploits properties of types and operations to minimize
2525 additional work.
2526 
2527 Params:
2528 x = The binary operator involved, e.g. `/`
2529 lhs = The left-hand side of the operator
2530 rhs = The right-hand side of the operator
2531 overflow = The overflow indicator (assigned `true` in case there's an error)
2532 
2533 Returns:
2534 The result of the operation, which is the same as the built-in operator
2535 */
2536 typeof(mixin(x == "cmp" ? "0" : ("L() " ~ x ~ " R()")))
2537 opChecked(string x, L, R)(const L lhs, const R rhs, ref bool overflow)
2538 if (isIntegral!L && isIntegral!R)
2539 {
2540     static if (x == "cmp")
2541         alias Result = int;
2542     else
2543         alias Result = typeof(mixin("L() " ~ x ~ " R()"));
2544 
2545     import core.checkedint : addu, adds, subs, muls, subu, mulu;
2546     import std.algorithm.comparison : among;
2547     static if (x == "==")
2548     {
2549         alias C = typeof(lhs + rhs);
2550         static if (valueConvertible!(L, C) && valueConvertible!(R, C))
2551         {
2552             // Values are converted to R before comparison, cool.
2553             return lhs == rhs;
2554         }
2555         else
2556         {
2557             static assert(isUnsigned!C);
2558             static assert(isUnsigned!L != isUnsigned!R);
2559             if (lhs != rhs) return false;
2560             // R(lhs) and R(rhs) have the same bit pattern, yet may be
2561             // different due to signedness change.
2562             static if (!isUnsigned!R)
2563             {
2564                 if (rhs >= 0)
2565                     return true;
2566             }
2567             else
2568             {
2569                 if (lhs >= 0)
2570                     return true;
2571             }
2572             overflow = true;
2573             return true;
2574         }
2575     }
2576     else static if (x == "cmp")
2577     {
2578         alias C = typeof(lhs + rhs);
2579         static if (!valueConvertible!(L, C) || !valueConvertible!(R, C))
2580         {
2581             static assert(isUnsigned!C);
2582             static assert(isUnsigned!L != isUnsigned!R);
2583             if (!isUnsigned!L && lhs < 0)
2584             {
2585                 overflow = true;
2586                 return -1;
2587             }
2588             if (!isUnsigned!R && rhs < 0)
2589             {
2590                 overflow = true;
2591                 return 1;
2592             }
2593         }
2594         return lhs < rhs ? -1 : lhs > rhs;
2595     }
2596     else static if (x.among("<<", ">>", ">>>"))
2597     {
2598         // Handle shift separately from all others. The test below covers
2599         // negative rhs as well.
2600         import std.conv : unsigned;
2601         if (unsigned(rhs) > 8 * Result.sizeof) goto fail;
2602         return mixin("lhs" ~ x ~ "rhs");
2603     }
2604     else static if (x.among("&", "|", "^"))
2605     {
2606         // Nothing to check
2607         return mixin("lhs" ~ x ~ "rhs");
2608     }
2609     else static if (x == "^^")
2610     {
2611         // Exponentiation is weird, handle separately
2612         return pow(lhs, rhs, overflow);
2613     }
2614     else static if (valueConvertible!(L, Result) &&
2615             valueConvertible!(R, Result))
2616     {
2617         static if (L.sizeof < Result.sizeof && R.sizeof < Result.sizeof &&
2618             x.among("+", "-", "*"))
2619         {
2620             // No checks - both are value converted and result is in range
2621             return mixin("lhs" ~ x ~ "rhs");
2622         }
2623         else static if (x == "+")
2624         {
2625             static if (isUnsigned!Result) alias impl = addu;
2626             else alias impl = adds;
2627             return impl(Result(lhs), Result(rhs), overflow);
2628         }
2629         else static if (x == "-")
2630         {
2631             static if (isUnsigned!Result) alias impl = subu;
2632             else alias impl = subs;
2633             return impl(Result(lhs), Result(rhs), overflow);
2634         }
2635         else static if (x == "*")
2636         {
2637             static if (!isUnsigned!L && !isUnsigned!R &&
2638                 is(L == Result))
2639             {
2640                 if (lhs == Result.min && rhs == -1) goto fail;
2641             }
2642             static if (isUnsigned!Result) alias impl = mulu;
2643             else alias impl = muls;
2644             return impl(Result(lhs), Result(rhs), overflow);
2645         }
2646         else static if (x == "/" || x == "%")
2647         {
2648             static if (!isUnsigned!L && !isUnsigned!R &&
2649                 is(L == Result) && x == "/")
2650             {
2651                 if (lhs == Result.min && rhs == -1) goto fail;
2652             }
2653             if (rhs == 0) goto fail;
2654             return mixin("lhs" ~ x ~ "rhs");
2655         }
2656         else static assert(0, x);
2657     }
2658     else // Mixed signs
2659     {
2660         static assert(isUnsigned!Result);
2661         static assert(isUnsigned!L != isUnsigned!R);
2662         static if (x == "+")
2663         {
2664             static if (!isUnsigned!L)
2665             {
2666                 if (lhs < 0)
2667                     return subu(Result(rhs), Result(-lhs), overflow);
2668             }
2669             else static if (!isUnsigned!R)
2670             {
2671                 if (rhs < 0)
2672                     return subu(Result(lhs), Result(-rhs), overflow);
2673             }
2674             return addu(Result(lhs), Result(rhs), overflow);
2675         }
2676         else static if (x == "-")
2677         {
2678             static if (!isUnsigned!L)
2679             {
2680                 if (lhs < 0) goto fail;
2681             }
2682             else static if (!isUnsigned!R)
2683             {
2684                 if (rhs < 0)
2685                     return addu(Result(lhs), Result(-rhs), overflow);
2686             }
2687             return subu(Result(lhs), Result(rhs), overflow);
2688         }
2689         else static if (x == "*")
2690         {
2691             static if (!isUnsigned!L)
2692             {
2693                 if (lhs < 0) goto fail;
2694             }
2695             else static if (!isUnsigned!R)
2696             {
2697                 if (rhs < 0) goto fail;
2698             }
2699             return mulu(Result(lhs), Result(rhs), overflow);
2700         }
2701         else static if (x == "/" || x == "%")
2702         {
2703             static if (!isUnsigned!L)
2704             {
2705                 if (lhs < 0 || rhs == 0) goto fail;
2706             }
2707             else static if (!isUnsigned!R)
2708             {
2709                 if (rhs <= 0) goto fail;
2710             }
2711             return mixin("Result(lhs)" ~ x ~ "Result(rhs)");
2712         }
2713         else static assert(0, x);
2714     }
2715     debug assert(false);
2716 fail:
2717     overflow = true;
2718     return Result(0);
2719 }
2720 
2721 ///
2722 @safe unittest
2723 {
2724     bool overflow;
2725     assert(opChecked!"+"(const short(1), short(1), overflow) == 2 && !overflow);
2726     assert(opChecked!"+"(1, 1, overflow) == 2 && !overflow);
2727     assert(opChecked!"+"(1, 1u, overflow) == 2 && !overflow);
2728     assert(opChecked!"+"(-1, 1u, overflow) == 0 && !overflow);
2729     assert(opChecked!"+"(1u, -1, overflow) == 0 && !overflow);
2730 }
2731 
2732 ///
2733 @safe unittest
2734 {
2735     bool overflow;
2736     assert(opChecked!"-"(1, 1, overflow) == 0 && !overflow);
2737     assert(opChecked!"-"(1, 1u, overflow) == 0 && !overflow);
2738     assert(opChecked!"-"(1u, -1, overflow) == 2 && !overflow);
2739     assert(opChecked!"-"(-1, 1u, overflow) == 0 && overflow);
2740 }
2741 
2742 @safe unittest
2743 {
2744     bool overflow;
2745     assert(opChecked!"*"(2, 3, overflow) == 6 && !overflow);
2746     assert(opChecked!"*"(2, 3u, overflow) == 6 && !overflow);
2747     assert(opChecked!"*"(1u, -1, overflow) == 0 && overflow);
2748     //assert(mul(-1, 1u, overflow) == uint.max - 1 && overflow);
2749 }
2750 
2751 @safe unittest
2752 {
2753     bool overflow;
2754     assert(opChecked!"/"(6, 3, overflow) == 2 && !overflow);
2755     assert(opChecked!"/"(6, 3, overflow) == 2 && !overflow);
2756     assert(opChecked!"/"(6u, 3, overflow) == 2 && !overflow);
2757     assert(opChecked!"/"(6, 3u, overflow) == 2 && !overflow);
2758     assert(opChecked!"/"(11, 0, overflow) == 0 && overflow);
2759     overflow = false;
2760     assert(opChecked!"/"(6u, 0, overflow) == 0 && overflow);
2761     overflow = false;
2762     assert(opChecked!"/"(-6, 2u, overflow) == 0 && overflow);
2763     overflow = false;
2764     assert(opChecked!"/"(-6, 0u, overflow) == 0 && overflow);
2765     overflow = false;
2766     assert(opChecked!"cmp"(0u, -6, overflow) == 1 && overflow);
2767     overflow = false;
2768     assert(opChecked!"|"(1, 2, overflow) == 3 && !overflow);
2769 }
2770 
2771 /*
2772 Exponentiation function used by the implementation of operator `^^`.
2773 */
2774 private pure @safe nothrow @nogc
2775 auto pow(L, R)(const L lhs, const R rhs, ref bool overflow)
2776 if (isIntegral!L && isIntegral!R)
2777 {
2778     if (rhs <= 1)
2779     {
2780         if (rhs == 0) return 1;
2781         static if (!isUnsigned!R)
2782             return rhs == 1
2783                 ? lhs
2784                 : (rhs == -1 && (lhs == 1 || lhs == -1)) ? lhs : 0;
2785         else
2786             return lhs;
2787     }
2788 
2789     typeof(lhs ^^ rhs) b = void;
2790     static if (!isUnsigned!L && isUnsigned!(typeof(b)))
2791     {
2792         // Need to worry about mixed-sign stuff
2793         if (lhs < 0)
2794         {
2795             if (rhs & 1)
2796             {
2797                 if (lhs < 0) overflow = true;
2798                 return 0;
2799             }
2800             b = -lhs;
2801         }
2802         else
2803         {
2804             b = lhs;
2805         }
2806     }
2807     else
2808     {
2809         b = lhs;
2810     }
2811     if (b == 1) return 1;
2812     if (b == -1) return (rhs & 1) ? -1 : 1;
2813     if (rhs > 63)
2814     {
2815         overflow = true;
2816         return 0;
2817     }
2818 
2819     assert((b > 1 || b < -1) && rhs > 1);
2820     return powImpl(b, cast(uint) rhs, overflow);
2821 }
2822 
2823 // Inspiration: http://www.stepanovpapers.com/PAM.pdf
2824 pure @safe nothrow @nogc
2825 private T powImpl(T)(T b, uint e, ref bool overflow)
2826 if (isIntegral!T && T.sizeof >= 4)
2827 {
2828     assert(e > 1);
2829 
2830     import core.checkedint : muls, mulu;
2831     static if (isUnsigned!T) alias mul = mulu;
2832     else alias mul = muls;
2833 
2834     T r = b;
2835     --e;
2836     // Loop invariant: r * (b ^^ e) is the actual result
2837     for (;; e /= 2)
2838     {
2839         if (e % 2)
2840         {
2841             r = mul(r, b, overflow);
2842             if (e == 1) break;
2843         }
2844         b = mul(b, b, overflow);
2845     }
2846     return r;
2847 }
2848 
2849 @safe unittest
2850 {
2851     static void testPow(T)(T x, uint e)
2852     {
2853         bool overflow;
2854         assert(opChecked!"^^"(T(0), 0, overflow) == 1);
2855         assert(opChecked!"^^"(-2, T(0), overflow) == 1);
2856         assert(opChecked!"^^"(-2, T(1), overflow) == -2);
2857         assert(opChecked!"^^"(-1, -1, overflow) == -1);
2858         assert(opChecked!"^^"(-2, 1, overflow) == -2);
2859         assert(opChecked!"^^"(-2, -1, overflow) == 0);
2860         assert(opChecked!"^^"(-2, 4u, overflow) == 16);
2861         assert(!overflow);
2862         assert(opChecked!"^^"(-2, 3u, overflow) == 0);
2863         assert(overflow);
2864         overflow = false;
2865         assert(opChecked!"^^"(3, 64u, overflow) == 0);
2866         assert(overflow);
2867         overflow = false;
2868         foreach (uint i; 0 .. e)
2869         {
2870             assert(opChecked!"^^"(x, i, overflow) == x ^^ i);
2871             assert(!overflow);
2872         }
2873         assert(opChecked!"^^"(x, e, overflow) == x ^^ e);
2874         assert(overflow);
2875     }
2876 
2877     testPow!int(3, 21);
2878     testPow!uint(3, 21);
2879     testPow!long(3, 40);
2880     testPow!ulong(3, 41);
2881 }
2882 
2883 version (StdUnittest) private struct CountOverflows
2884 {
2885     uint calls;
2886     auto onOverflow(string op, Lhs)(Lhs lhs)
2887     {
2888         ++calls;
2889         return mixin(op ~ "lhs");
2890     }
2891     auto onOverflow(string op, Lhs, Rhs)(Lhs lhs, Rhs rhs)
2892     {
2893         ++calls;
2894         return mixin("lhs" ~ op ~ "rhs");
2895     }
2896     T onLowerBound(Rhs, T)(Rhs rhs, T bound)
2897     {
2898         ++calls;
2899         return cast(T) rhs;
2900     }
2901     T onUpperBound(Rhs, T)(Rhs rhs, T bound)
2902     {
2903         ++calls;
2904         return cast(T) rhs;
2905     }
2906 }
2907 
2908 // opBinary
2909 @nogc nothrow pure @safe unittest
2910 {
2911     static struct CountOpBinary
2912     {
2913         uint calls;
2914         auto hookOpBinary(string op, Lhs, Rhs)(Lhs lhs, Rhs rhs)
2915         {
2916             ++calls;
2917             return mixin("lhs" ~ op ~ "rhs");
2918         }
2919     }
2920     auto x = Checked!(const int, void)(42), y = Checked!(immutable int, void)(142);
2921     assert(x + y == 184);
2922     assert(x + 100 == 142);
2923     assert(y - x == 100);
2924     assert(200 - x == 158);
2925     assert(y * x == 142 * 42);
2926     assert(x / 1 == 42);
2927     assert(x % 20 == 2);
2928 
2929     auto x1 = Checked!(int, CountOverflows)(42);
2930     assert(x1 + 0 == 42);
2931     assert(x1 + false == 42);
2932     assert(is(typeof(x1 + 0.5) == double));
2933     assert(x1 + 0.5 == 42.5);
2934     assert(x1.hook.calls == 0);
2935     assert(x1 + int.max == int.max + 42);
2936     assert(x1.hook.calls == 1);
2937     assert(x1 * 2 == 84);
2938     assert(x1.hook.calls == 1);
2939     assert(x1 / 2 == 21);
2940     assert(x1.hook.calls == 1);
2941     assert(x1 % 20 == 2);
2942     assert(x1.hook.calls == 1);
2943     assert(x1 << 2 == 42 << 2);
2944     assert(x1.hook.calls == 1);
2945     assert(x1 << 42 == x1.get << x1.get);
2946     assert(x1.hook.calls == 2);
2947     x1 = int.min;
2948     assert(x1 - 1 == int.max);
2949     assert(x1.hook.calls == 3);
2950 
2951     auto x2 = Checked!(int, CountOpBinary)(42);
2952     assert(x2 + 1 == 43);
2953     assert(x2.hook.calls == 1);
2954 
2955     auto x3 = Checked!(uint, CountOverflows)(42u);
2956     assert(x3 + 1 == 43);
2957     assert(x3.hook.calls == 0);
2958     assert(x3 - 1 == 41);
2959     assert(x3.hook.calls == 0);
2960     assert(x3 + (-42) == 0);
2961     assert(x3.hook.calls == 0);
2962     assert(x3 - (-42) == 84);
2963     assert(x3.hook.calls == 0);
2964     assert(x3 * 2 == 84);
2965     assert(x3.hook.calls == 0);
2966     assert(x3 * -2 == -84);
2967     assert(x3.hook.calls == 1);
2968     assert(x3 / 2 == 21);
2969     assert(x3.hook.calls == 1);
2970     assert(x3 / -2 == 0);
2971     assert(x3.hook.calls == 2);
2972     assert(x3 ^^ 2 == 42 * 42);
2973     assert(x3.hook.calls == 2);
2974 
2975     auto x4 = Checked!(int, CountOverflows)(42);
2976     assert(x4 + 1 == 43);
2977     assert(x4.hook.calls == 0);
2978     assert(x4 + 1u == 43);
2979     assert(x4.hook.calls == 0);
2980     assert(x4 - 1 == 41);
2981     assert(x4.hook.calls == 0);
2982     assert(x4 * 2 == 84);
2983     assert(x4.hook.calls == 0);
2984     x4 = -2;
2985     assert(x4 + 2u == 0);
2986     assert(x4.hook.calls == 0);
2987     assert(x4 * 2u == -4);
2988     assert(x4.hook.calls == 1);
2989 
2990     auto x5 = Checked!(int, CountOverflows)(3);
2991     assert(x5 ^^ 0 == 1);
2992     assert(x5 ^^ 1 == 3);
2993     assert(x5 ^^ 2 == 9);
2994     assert(x5 ^^ 3 == 27);
2995     assert(x5 ^^ 4 == 81);
2996     assert(x5 ^^ 5 == 81 * 3);
2997     assert(x5 ^^ 6 == 81 * 9);
2998 }
2999 
3000 // opBinaryRight
3001 @nogc nothrow pure @safe unittest
3002 {
3003     auto x1 = Checked!(int, CountOverflows)(42);
3004     assert(1 + x1 == 43);
3005     assert(true + x1 == 43);
3006     assert(0.5 + x1 == 42.5);
3007     auto x2 = Checked!(int, void)(42);
3008     assert(x1 + x2 == 84);
3009     assert(x2 + x1   == 84);
3010 }
3011 
3012 // opOpAssign
3013 @safe unittest
3014 {
3015     auto x1 = Checked!(int, CountOverflows)(3);
3016     assert((x1 += 2) == 5);
3017     x1 *= 2_000_000_000L;
3018     assert(x1.hook.calls == 1);
3019     x1 *= -2_000_000_000L;
3020     assert(x1.hook.calls == 2);
3021 
3022     auto x2 = Checked!(ushort, CountOverflows)(ushort(3));
3023     assert((x2 += 2) == 5);
3024     assert(x2.hook.calls == 0);
3025     assert((x2 += ushort.max) == cast(ushort) (ushort(5) + ushort.max));
3026     assert(x2.hook.calls == 1);
3027 
3028     auto x3 = Checked!(uint, CountOverflows)(3u);
3029     x3 *= ulong(2_000_000_000);
3030     assert(x3.hook.calls == 1);
3031 }
3032 
3033 // opAssign
3034 @safe unittest
3035 {
3036     Checked!(int, void) x;
3037     x = 42;
3038     assert(x.get == 42);
3039     x = x;
3040     assert(x.get == 42);
3041     x = short(43);
3042     assert(x.get == 43);
3043     x = ushort(44);
3044     assert(x.get == 44);
3045 }
3046 
3047 @safe unittest
3048 {
3049     static assert(!is(typeof(Checked!(short, void)(ushort(42)))));
3050     static assert(!is(typeof(Checked!(int, void)(long(42)))));
3051     static assert(!is(typeof(Checked!(int, void)(ulong(42)))));
3052     assert(Checked!(short, void)(short(42)).get == 42);
3053     assert(Checked!(int, void)(ushort(42)).get == 42);
3054 }
3055 
3056 // opCast
3057 @nogc nothrow pure @safe unittest
3058 {
3059     static assert(is(typeof(cast(float) Checked!(int, void)(42)) == float));
3060     assert(cast(float) Checked!(int, void)(42) == 42);
3061 
3062     assert(is(typeof(cast(long) Checked!(int, void)(42)) == long));
3063     assert(cast(long) Checked!(int, void)(42) == 42);
3064     static assert(is(typeof(cast(long) Checked!(uint, void)(42u)) == long));
3065     assert(cast(long) Checked!(uint, void)(42u) == 42);
3066 
3067     auto x = Checked!(int, void)(42);
3068     if (x) {} else assert(0);
3069     x = 0;
3070     if (x) assert(0);
3071 
3072     static struct Hook1
3073     {
3074         uint calls;
3075         Dst hookOpCast(Dst, Src)(Src value)
3076         {
3077             ++calls;
3078             return 42;
3079         }
3080     }
3081     auto y = Checked!(long, Hook1)(long.max);
3082     assert(cast(int) y == 42);
3083     assert(cast(uint) y == 42);
3084     assert(y.hook.calls == 2);
3085 
3086     static struct Hook2
3087     {
3088         uint calls;
3089         Dst onBadCast(Dst, Src)(Src value)
3090         {
3091             ++calls;
3092             return 42;
3093         }
3094     }
3095     auto x1 = Checked!(uint, Hook2)(100u);
3096     assert(cast(ushort) x1 == 100);
3097     assert(cast(short) x1 == 100);
3098     assert(cast(float) x1 == 100);
3099     assert(cast(double) x1 == 100);
3100     assert(cast(real) x1 == 100);
3101     assert(x1.hook.calls == 0);
3102     assert(cast(int) x1 == 100);
3103     assert(x1.hook.calls == 0);
3104     x1 = uint.max;
3105     assert(cast(int) x1 == 42);
3106     assert(x1.hook.calls == 1);
3107 
3108     auto x2 = Checked!(int, Hook2)(-100);
3109     assert(cast(short) x2 == -100);
3110     assert(cast(ushort) x2 == 42);
3111     assert(cast(uint) x2 == 42);
3112     assert(cast(ulong) x2 == 42);
3113     assert(x2.hook.calls == 3);
3114 }
3115 
3116 // opEquals
3117 @nogc nothrow pure @safe unittest
3118 {
3119     assert(Checked!(int, void)(42) == 42L);
3120     assert(42UL == Checked!(int, void)(42));
3121 
3122     static struct Hook1
3123     {
3124         uint calls;
3125         bool hookOpEquals(Lhs, Rhs)(const Lhs lhs, const Rhs rhs)
3126         {
3127             ++calls;
3128             return lhs != rhs;
3129         }
3130     }
3131     auto x1 = Checked!(int, Hook1)(100);
3132     assert(x1 != Checked!(long, Hook1)(100));
3133     assert(x1.hook.calls == 1);
3134     assert(x1 != 100u);
3135     assert(x1.hook.calls == 2);
3136 
3137     static struct Hook2
3138     {
3139         uint calls;
3140         bool hookOpEquals(Lhs, Rhs)(Lhs lhs, Rhs rhs)
3141         {
3142             ++calls;
3143             return false;
3144         }
3145     }
3146     auto x2 = Checked!(int, Hook2)(-100);
3147     assert(x2 != x1);
3148     // For coverage: lhs has no hookOpEquals, rhs does
3149     assert(Checked!(uint, void)(100u) != x2);
3150     // For coverage: different types, neither has a hookOpEquals
3151     assert(Checked!(uint, void)(100u) == Checked!(int, void*)(100));
3152     assert(x2.hook.calls == 0);
3153     assert(x2 != -100);
3154     assert(x2.hook.calls == 1);
3155     assert(x2 != cast(uint) -100);
3156     assert(x2.hook.calls == 2);
3157     x2 = 100;
3158     assert(x2 != cast(uint) 100);
3159     assert(x2.hook.calls == 3);
3160     x2 = -100;
3161 
3162     auto x3 = Checked!(uint, Hook2)(100u);
3163     assert(x3 != 100);
3164     x3 = uint.max;
3165     assert(x3 != -1);
3166 
3167     assert(x2 != x3);
3168 }
3169 
3170 // opCmp
3171 @nogc nothrow pure @safe unittest
3172 {
3173     Checked!(int, void) x;
3174     assert(x <= x);
3175     assert(x < 45);
3176     assert(x < 45u);
3177     assert(x > -45);
3178     assert(x < 44.2);
3179     assert(x > -44.2);
3180     assert(!(x < double.init));
3181     assert(!(x > double.init));
3182     assert(!(x <= double.init));
3183     assert(!(x >= double.init));
3184 
3185     static struct Hook1
3186     {
3187         uint calls;
3188         int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)
3189         {
3190             ++calls;
3191             return 0;
3192         }
3193     }
3194     auto x1 = Checked!(int, Hook1)(42);
3195     assert(!(x1 < 43u));
3196     assert(!(43u < x1));
3197     assert(x1.hook.calls == 2);
3198 
3199     static struct Hook2
3200     {
3201         uint calls;
3202         int hookOpCmp(Lhs, Rhs)(Lhs lhs, Rhs rhs)
3203         {
3204             ++calls;
3205             return ProperCompare.hookOpCmp(lhs, rhs);
3206         }
3207     }
3208     auto x2 = Checked!(int, Hook2)(-42);
3209     assert(x2 < 43u);
3210     assert(43u > x2);
3211     assert(x2.hook.calls == 2);
3212     x2 = 42;
3213     assert(x2 > 41u);
3214 
3215     auto x3 = Checked!(uint, Hook2)(42u);
3216     assert(x3 > 41);
3217     assert(x3 > -41);
3218 }
3219 
3220 // opUnary
3221 @nogc nothrow pure @safe unittest
3222 {
3223     auto x = Checked!(int, void)(42);
3224     assert(x == +x);
3225     static assert(is(typeof(-x) == typeof(x)));
3226     assert(-x == Checked!(int, void)(-42));
3227     static assert(is(typeof(~x) == typeof(x)));
3228     assert(~x == Checked!(int, void)(~42));
3229     assert(++x == 43);
3230     assert(--x == 42);
3231 
3232     static struct Hook1
3233     {
3234         uint calls;
3235         auto hookOpUnary(string op, T)(T value) if (op == "-")
3236         {
3237             ++calls;
3238             return T(42);
3239         }
3240         auto hookOpUnary(string op, T)(T value) if (op == "~")
3241         {
3242             ++calls;
3243             return T(43);
3244         }
3245     }
3246     auto x1 = Checked!(int, Hook1)(100);
3247     assert(is(typeof(-x1) == typeof(x1)));
3248     assert(-x1 == Checked!(int, Hook1)(42));
3249     assert(is(typeof(~x1) == typeof(x1)));
3250     assert(~x1 == Checked!(int, Hook1)(43));
3251     assert(x1.hook.calls == 2);
3252 
3253     static struct Hook2
3254     {
3255         uint calls;
3256         void hookOpUnary(string op, T)(ref T value) if (op == "++")
3257         {
3258             ++calls;
3259             --value;
3260         }
3261         void hookOpUnary(string op, T)(ref T value) if (op == "--")
3262         {
3263             ++calls;
3264             ++value;
3265         }
3266     }
3267     auto x2 = Checked!(int, Hook2)(100);
3268     assert(++x2 == 99);
3269     assert(x2 == 99);
3270     assert(--x2 == 100);
3271     assert(x2 == 100);
3272 
3273     auto x3 = Checked!(int, CountOverflows)(int.max - 1);
3274     assert(++x3 == int.max);
3275     assert(x3.hook.calls == 0);
3276     assert(++x3 == int.min);
3277     assert(x3.hook.calls == 1);
3278     assert(-x3 == int.min);
3279     assert(x3.hook.calls == 2);
3280 
3281     x3 = int.min + 1;
3282     assert(--x3 == int.min);
3283     assert(x3.hook.calls == 2);
3284     assert(--x3 == int.max);
3285     assert(x3.hook.calls == 3);
3286 }
3287 
3288 //
3289 @nogc nothrow pure @safe unittest
3290 {
3291     Checked!(int, void) x;
3292     assert(x == x);
3293     assert(x == +x);
3294     assert(x == -x);
3295     ++x;
3296     assert(x == 1);
3297     x++;
3298     assert(x == 2);
3299 
3300     x = 42;
3301     assert(x == 42);
3302     const short _short = 43;
3303     x = _short;
3304     assert(x == _short);
3305     ushort _ushort = 44;
3306     x = _ushort;
3307     assert(x == _ushort);
3308     assert(x == 44.0);
3309     assert(x != 44.1);
3310     assert(x < 45);
3311     assert(x < 44.2);
3312     assert(x > -45);
3313     assert(x > -44.2);
3314 
3315     assert(cast(long) x == 44);
3316     assert(cast(short) x == 44);
3317 
3318     const Checked!(uint, void) y;
3319     assert(y <= y);
3320     assert(y == 0);
3321     assert(y < x);
3322     x = -1;
3323     assert(x > y);
3324 }
3325 
3326 @nogc nothrow pure @safe unittest
3327 {
3328     alias cint = Checked!(int, void);
3329     cint a = 1, b = 2;
3330     a += b;
3331     assert(a == cint(3));
3332 
3333     alias ccint = Checked!(cint, Saturate);
3334     ccint c = 14;
3335     a += c;
3336     assert(a == cint(17));
3337 }
3338 
3339 // toHash
3340 @safe unittest
3341 {
3342     assert(checked(42).toHash() == checked(42).toHash());
3343     assert(checked(12).toHash() != checked(19).toHash());
3344 
3345     static struct Hook1
3346     {
3347         static size_t hookToHash(T)(T payload) nothrow @trusted
3348         {
3349             static if (size_t.sizeof == 4)
3350             {
3351                 return typeid(payload).getHash(&payload) ^ 0xFFFF_FFFF;
3352             }
3353             else
3354             {
3355                 return typeid(payload).getHash(&payload) ^ 0xFFFF_FFFF_FFFF_FFFF;
3356             }
3357 
3358         }
3359     }
3360 
3361     auto a = checked!Hook1(78);
3362     auto b = checked!Hook1(78);
3363     assert(a.toHash() == b.toHash());
3364 
3365     assert(checked!Hook1(12).toHash() != checked!Hook1(13).toHash());
3366 
3367     static struct Hook2
3368     {
3369         static if (size_t.sizeof == 4)
3370         {
3371             static size_t hashMask = 0xFFFF_0000;
3372         }
3373         else
3374         {
3375             static size_t hashMask = 0xFFFF_0000_FFFF_0000;
3376         }
3377 
3378         static size_t hookToHash(T)(T payload) nothrow @trusted
3379         {
3380             return typeid(payload).getHash(&payload) ^ hashMask;
3381         }
3382     }
3383 
3384     auto x = checked!Hook2(1901);
3385     auto y = checked!Hook2(1989);
3386 
3387     assert((() nothrow @safe => x.toHash() == x.toHash())());
3388 
3389     assert(x.toHash() == x.toHash());
3390     assert(x.toHash() != y.toHash());
3391     assert(checked!Hook1(1901).toHash() != x.toHash());
3392 
3393     immutable z = checked!Hook1(1901);
3394     immutable t = checked!Hook1(1901);
3395     immutable w = checked!Hook2(1901);
3396 
3397     assert(z.toHash() == t.toHash());
3398     assert(z.toHash() != x.toHash());
3399     assert(z.toHash() != w.toHash());
3400 
3401     const long c = 0xF0F0F0F0;
3402     const long d = 0xF0F0F0F0;
3403 
3404     assert(checked!Hook1(c).toHash() != checked!Hook2(c));
3405     assert(checked!Hook1(c).toHash() != checked!Hook1(d));
3406 
3407     // Hook with state, does not implement hookToHash
3408     static struct Hook3
3409     {
3410         ulong var1 = ulong.max;
3411         uint var2 = uint.max;
3412     }
3413 
3414     assert(checked!Hook3(12).toHash() != checked!Hook3(13).toHash());
3415     assert(checked!Hook3(13).toHash() == checked!Hook3(13).toHash());
3416 
3417     // Hook with no state and no hookToHash, payload has its own hashing function
3418     auto x1 = Checked!(Checked!int, ProperCompare)(123);
3419     auto x2 = Checked!(Checked!int, ProperCompare)(123);
3420     auto x3 = Checked!(Checked!int, ProperCompare)(144);
3421 
3422     assert(x1.toHash() == x2.toHash());
3423     assert(x1.toHash() != x3.toHash());
3424     assert(x2.toHash() != x3.toHash());
3425 
3426     // Check shared.
3427     {
3428         shared shared0 = checked(12345678);
3429         shared shared1 = checked!Hook1(123456789);
3430         shared shared2 = checked!Hook2(234567891);
3431         shared shared3 = checked!Hook3(345678912);
3432         assert(shared0.toHash() == hashOf(shared0));
3433         assert(shared1.toHash() == hashOf(shared1));
3434         assert(shared2.toHash() == hashOf(shared2));
3435         assert(shared3.toHash() == hashOf(shared3));
3436     }
3437 }
3438 
3439 ///
3440 @safe unittest
3441 {
3442     struct MyHook
3443     {
3444         static size_t hookToHash(T)(const T payload) nothrow @trusted
3445         {
3446             return .hashOf(payload);
3447         }
3448     }
3449 
3450     int[Checked!(int, MyHook)] aa;
3451     Checked!(int, MyHook) var = 42;
3452     aa[var] = 100;
3453 
3454     assert(aa[var] == 100);
3455 
3456     int[Checked!(int, Abort)] bb;
3457     Checked!(int, Abort) var2 = 42;
3458     bb[var2] = 100;
3459 
3460     assert(bb[var2] == 100);
3461 }
3462