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