@@ -150,12 +150,15 @@ rhs)) (where `op` is the operator symbol and `rhs` is the right-hand side
150
150
operand) is forwarded to unconditionally for binary operators `+=`, `-=`, `*=`, `/=`, `%=`,
151
151
`^^=`, `& =`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=`.))
152
152
153
- $(TR $(TD `onBadOpOpAssign`) $(TD If defined and `hookOpOpAssign` is not
154
- defined, $(D hook.onBadOpOpAssign!op(value)) (where `value` is the value being
155
- assigned) is forwarded to when the result of binary operators `+=`, `-=`, `*=`,
156
- `/=`, `%=`, `^^=`, `& =`, `|=`, `^=`, `<<=`, `>>=`,
157
- and `>>>=` cannot be assigned back to the payload without loss of information or
158
- a change in sign.))
153
+ $(TR $(TD `onLowerBound`) $(TD If defined, $(D hook.onLowerBound(value, bound))
154
+ (where `value` is the value being assigned) is forwarded to when the result of
155
+ binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `& =`, `|=`, `^=`, `<<=`, `>>=`,
156
+ and `>>>=` is smaller than the smallest value representable by `T`.))
157
+
158
+ $(TR $(TD `onUpperBound`) $(TD If defined, $(D hook.onUpperBound(value, bound))
159
+ (where `value` is the value being assigned) is forwarded to when the result of
160
+ binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `& =`, `|=`, `^=`, `<<=`, `>>=`,
161
+ and `>>>=` is larger than the largest value representable by `T`.))
159
162
160
163
)
161
164
@@ -258,10 +261,7 @@ if (isIntegral!T && is(T == Unqual!T) || is(T == Checked!(U, H), U, H))
258
261
is (U == Checked! (V, W), V, W) &&
259
262
is (typeof (Checked! (T, Hook)(rhs.get ))))
260
263
{
261
- static if (isIntegral! U)
262
- payload = rhs;
263
- else
264
- payload = rhs.payload;
264
+ opAssign(rhs);
265
265
}
266
266
// /
267
267
unittest
@@ -769,9 +769,11 @@ if (isIntegral!T && is(T == Unqual!T) || is(T == Checked!(U, H), U, H))
769
769
770
770
Otherwise, the operator first evaluates $(D auto result =
771
771
opBinary!op(payload, rhs).payload), which is subject to the hooks in
772
- `opBinary`. Then, if `result` does not fit in `this` without a loss of data
773
- or a change in sign and if `Hook` defines `onBadOpOpAssign`, the payload is
774
- assigned from `hook.onBadOpOpAssign!T(result)`.
772
+ `opBinary`. Then, if `result` is less than $(D Checked!(T, Hook).min) and if
773
+ `Hook` defines `onLowerBound`, the payload is assigned from $(D
774
+ hook.onLowerBound(result, min)). If `result` is greater than $(D Checked!(T,
775
+ Hook).max) and if `Hook` defines `onUpperBound`, the payload is assigned
776
+ from $(D hook.onUpperBound(result, min)).
775
777
776
778
In all other cases, the built-in behavior is carried out.
777
779
@@ -781,7 +783,7 @@ if (isIntegral!T && is(T == Unqual!T) || is(T == Checked!(U, H), U, H))
781
783
782
784
Returns: A reference to `this`.
783
785
*/
784
- ref Checked opOpAssign (string op, Rhs)(const Rhs rhs)
786
+ ref Checked opOpAssign (string op, Rhs)(const Rhs rhs) return
785
787
if (isIntegral! Rhs || isFloatingPoint! Rhs || is (Rhs == bool ))
786
788
{
787
789
static assert (is (typeof (mixin (" payload" ~ op ~ " =rhs" )) == T));
@@ -794,35 +796,27 @@ if (isIntegral!T && is(T == Unqual!T) || is(T == Checked!(U, H), U, H))
794
796
{
795
797
alias R = typeof (payload + rhs);
796
798
auto r = opBinary! op(rhs).payload;
799
+ import std.conv : unsigned;
797
800
798
- static if (valueConvertible! (R, T) ||
799
- ! hasMember! (Hook, " onBadOpOpAssign" ) ||
800
- op.among(" >>" , " >>>" ))
801
+ static if (ProperCompare.hookOpCmp(R.min, min.payload) < 0 &&
802
+ hasMember! (Hook, " onLowerBound" ))
801
803
{
802
- // No need to check these
803
- payload = cast (T) r;
804
+ if (ProperCompare.hookOpCmp(r, min.get ) < 0 )
805
+ {
806
+ payload = hook.onLowerBound(r, min.get );
807
+ return this ;
808
+ }
804
809
}
805
- else
810
+ static if (ProperCompare.hookOpCmp(max.payload, R.max) < 0 &&
811
+ hasMember! (Hook, " onUpperBound" ))
806
812
{
807
- static if (isUnsigned ! T && ! isUnsigned ! R )
813
+ if (ProperCompare.hookOpCmp(r, max. get ) > 0 )
808
814
{
809
- // Example: ushort += int
810
- import std.conv : unsigned;
811
- const bad = unsigned(r) > max.payload;
815
+ payload = hook.onUpperBound(r, min.get );
816
+ return this ;
812
817
}
813
- else
814
- // Some narrowing is afoot
815
- static if (R.min < min.payload)
816
- // Example: int += long
817
- const bad = r > max.payload || r < min.payload;
818
- else
819
- // Example: uint += ulong
820
- const bad = r > max.payload;
821
- if (bad)
822
- payload = hook.onBadOpOpAssign! T(r);
823
- else
824
- payload = cast (T) r;
825
818
}
819
+ payload = cast (T) r;
826
820
}
827
821
return this ;
828
822
}
@@ -835,13 +829,10 @@ instance by using template argument deduction. The hook type may be specified
835
829
(by default `Abort`).
836
830
837
831
*/
838
- template checked (Hook = Abort)
832
+ Checked! (T, Hook) checked(Hook = Abort, T)(const T value)
833
+ if (is (typeof (Checked! (T, Hook)(value))))
839
834
{
840
- Checked! (T, Hook) checked(T)(const T value)
841
- if (is (typeof (Checked! (T, Hook)(value))))
842
- {
843
- return Checked! (T, Hook)(value);
844
- }
835
+ return Checked! (T, Hook)(value);
845
836
}
846
837
847
838
// /
@@ -891,8 +882,7 @@ static:
891
882
892
883
/**
893
884
894
- Called automatically upon a bad `opOpAssign` call (one that loses precision
895
- or attempts to convert a negative value to an unsigned type).
885
+ Called automatically upon a bounds error.
896
886
897
887
Params:
898
888
rhs = The right-hand side value in the assignment, after the operator has
@@ -903,9 +893,15 @@ static:
903
893
it aborts the program.
904
894
905
895
*/
906
- Lhs onBadOpOpAssign (Lhs, Rhs)(Rhs rhs)
896
+ T onLowerBound (Rhs, T)(Rhs rhs, T bound)
897
+ {
898
+ Warn.onLowerBound(rhs, bound);
899
+ assert (0 );
900
+ }
901
+ // / ditto
902
+ T onUpperBound (Rhs, T)(Rhs rhs, T bound)
907
903
{
908
- Warn.onBadOpOpAssign ! Lhs (rhs);
904
+ Warn.onUpperBound (rhs, bound );
909
905
assert (0 );
910
906
}
911
907
@@ -1026,11 +1022,18 @@ static:
1026
1022
1027
1023
Returns: `cast(Lhs) rhs`
1028
1024
*/
1029
- Lhs onBadOpOpAssign (Lhs, Rhs)(Rhs rhs)
1025
+ Lhs onLowerBound (Rhs, T)(Rhs rhs, T bound)
1026
+ {
1027
+ stderr.writefln(" Lower bound error: %s(%s) < %s(%s)" ,
1028
+ Rhs.stringof, rhs, T.stringof, bound);
1029
+ return cast (T) rhs;
1030
+ }
1031
+ // / ditto
1032
+ T onUpperBound (Rhs, T)(Rhs rhs, T bound)
1030
1033
{
1031
- stderr.writefln(" Erroneous assignment : %s = %s(%s)" ,
1032
- Lhs .stringof, Rhs .stringof, rhs );
1033
- return cast (Lhs ) rhs;
1034
+ stderr.writefln(" Upper bound error : %s(%s) > %s(%s)" ,
1035
+ Rhs .stringof, rhs, T .stringof, bound );
1036
+ return cast (T ) rhs;
1034
1037
}
1035
1038
1036
1039
/**
@@ -1260,7 +1263,7 @@ unittest
1260
1263
/**
1261
1264
1262
1265
Hook that reserves a special value as a "Not a Number" representative. For
1263
- signed integrals, the reserved value is `T.min`. For signed integrals, the
1266
+ signed integrals, the reserved value is `T.min`. For unsigned integrals, the
1264
1267
reserved value is `T.max`.
1265
1268
1266
1269
The default value of a $(D Checked!(X, WithNaN)) is its NaN value, so care must
@@ -1333,9 +1336,14 @@ static:
1333
1336
1334
1337
Returns: `WithNaN.defaultValue!Lhs`
1335
1338
*/
1336
- Lhs onBadOpOpAssign (Lhs, Rhs )(Rhs)
1339
+ T onLowerBound (Rhs, T )(Rhs, T )
1337
1340
{
1338
- return defaultValue! Lhs;
1341
+ return defaultValue! T;
1342
+ }
1343
+ // / ditto
1344
+ T onUpperBound (Rhs, T)(Rhs, T)
1345
+ {
1346
+ return defaultValue! T;
1339
1347
}
1340
1348
1341
1349
/**
@@ -1576,9 +1584,14 @@ static:
1576
1584
Returns: `Lhs.max` if $(D rhs >= 0), `Lhs.min` otherwise.
1577
1585
1578
1586
*/
1579
- Lhs onBadOpOpAssign (Lhs, Rhs)(Rhs rhs)
1587
+ T onLowerBound (Rhs, T)(Rhs rhs, T bound)
1588
+ {
1589
+ return bound;
1590
+ }
1591
+ // / ditto
1592
+ T onUpperBound (Rhs, T)(Rhs rhs, T bound)
1580
1593
{
1581
- return rhs >= 0 ? Lhs.max : Lhs.min ;
1594
+ return bound ;
1582
1595
}
1583
1596
1584
1597
/**
@@ -1995,10 +2008,15 @@ version(unittest) private struct CountOverflows
1995
2008
++ calls;
1996
2009
return mixin (" lhs" ~ op ~ " rhs" );
1997
2010
}
1998
- Lhs onBadOpOpAssign (Lhs, Rhs)(Rhs rhs)
2011
+ T onLowerBound (Rhs, T)(Rhs rhs, T bound)
2012
+ {
2013
+ ++ calls;
2014
+ return cast (T) rhs;
2015
+ }
2016
+ T onUpperBound (Rhs, T)(Rhs rhs, T bound)
1999
2017
{
2000
2018
++ calls;
2001
- return cast (Lhs ) rhs;
2019
+ return cast (T ) rhs;
2002
2020
}
2003
2021
}
2004
2022
0 commit comments