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