The OpenD Programming Language

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