mirror of
https://github.com/dlang/phobos.git
synced 2025-05-02 16:10:45 +03:00
Simplify checked(), replace onBadOpOpAssign with more precise onLowerBound/onUpperBound
This commit is contained in:
parent
047687889a
commit
1a62d66d93
1 changed files with 77 additions and 59 deletions
|
@ -150,12 +150,15 @@ rhs)) (where `op` is the operator symbol and `rhs` is the right-hand side
|
||||||
operand) is forwarded to unconditionally for binary operators `+=`, `-=`, `*=`, `/=`, `%=`,
|
operand) is forwarded to unconditionally for binary operators `+=`, `-=`, `*=`, `/=`, `%=`,
|
||||||
`^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=`.))
|
`^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`, and `>>>=`.))
|
||||||
|
|
||||||
$(TR $(TD `onBadOpOpAssign`) $(TD If defined and `hookOpOpAssign` is not
|
$(TR $(TD `onLowerBound`) $(TD If defined, $(D hook.onLowerBound(value, bound))
|
||||||
defined, $(D hook.onBadOpOpAssign!op(value)) (where `value` is the value being
|
(where `value` is the value being assigned) is forwarded to when the result of
|
||||||
assigned) is forwarded to when the result of binary operators `+=`, `-=`, `*=`,
|
binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`,
|
||||||
`/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`,
|
and `>>>=` is smaller than the smallest value representable by `T`.))
|
||||||
and `>>>=` cannot be assigned back to the payload without loss of information or
|
|
||||||
a change in sign.))
|
$(TR $(TD `onUpperBound`) $(TD If defined, $(D hook.onUpperBound(value, bound))
|
||||||
|
(where `value` is the value being assigned) is forwarded to when the result of
|
||||||
|
binary operators `+=`, `-=`, `*=`, `/=`, `%=`, `^^=`, `&=`, `|=`, `^=`, `<<=`, `>>=`,
|
||||||
|
and `>>>=` is larger than the largest value representable by `T`.))
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -258,10 +261,7 @@ if (isIntegral!T && is(T == Unqual!T) || is(T == Checked!(U, H), U, H))
|
||||||
is(U == Checked!(V, W), V, W) &&
|
is(U == Checked!(V, W), V, W) &&
|
||||||
is(typeof(Checked!(T, Hook)(rhs.get))))
|
is(typeof(Checked!(T, Hook)(rhs.get))))
|
||||||
{
|
{
|
||||||
static if (isIntegral!U)
|
opAssign(rhs);
|
||||||
payload = rhs;
|
|
||||||
else
|
|
||||||
payload = rhs.payload;
|
|
||||||
}
|
}
|
||||||
///
|
///
|
||||||
unittest
|
unittest
|
||||||
|
@ -769,9 +769,11 @@ if (isIntegral!T && is(T == Unqual!T) || is(T == Checked!(U, H), U, H))
|
||||||
|
|
||||||
Otherwise, the operator first evaluates $(D auto result =
|
Otherwise, the operator first evaluates $(D auto result =
|
||||||
opBinary!op(payload, rhs).payload), which is subject to the hooks in
|
opBinary!op(payload, rhs).payload), which is subject to the hooks in
|
||||||
`opBinary`. Then, if `result` does not fit in `this` without a loss of data
|
`opBinary`. Then, if `result` is less than $(D Checked!(T, Hook).min) and if
|
||||||
or a change in sign and if `Hook` defines `onBadOpOpAssign`, the payload is
|
`Hook` defines `onLowerBound`, the payload is assigned from $(D
|
||||||
assigned from `hook.onBadOpOpAssign!T(result)`.
|
hook.onLowerBound(result, min)). If `result` is greater than $(D Checked!(T,
|
||||||
|
Hook).max) and if `Hook` defines `onUpperBound`, the payload is assigned
|
||||||
|
from $(D hook.onUpperBound(result, min)).
|
||||||
|
|
||||||
In all other cases, the built-in behavior is carried out.
|
In all other cases, the built-in behavior is carried out.
|
||||||
|
|
||||||
|
@ -781,7 +783,7 @@ if (isIntegral!T && is(T == Unqual!T) || is(T == Checked!(U, H), U, H))
|
||||||
|
|
||||||
Returns: A reference to `this`.
|
Returns: A reference to `this`.
|
||||||
*/
|
*/
|
||||||
ref Checked opOpAssign(string op, Rhs)(const Rhs rhs)
|
ref Checked opOpAssign(string op, Rhs)(const Rhs rhs) return
|
||||||
if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool))
|
if (isIntegral!Rhs || isFloatingPoint!Rhs || is(Rhs == bool))
|
||||||
{
|
{
|
||||||
static assert(is(typeof(mixin("payload" ~ op ~ "=rhs")) == T));
|
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))
|
||||||
{
|
{
|
||||||
alias R = typeof(payload + rhs);
|
alias R = typeof(payload + rhs);
|
||||||
auto r = opBinary!op(rhs).payload;
|
auto r = opBinary!op(rhs).payload;
|
||||||
|
import std.conv : unsigned;
|
||||||
|
|
||||||
static if (valueConvertible!(R, T) ||
|
static if (ProperCompare.hookOpCmp(R.min, min.payload) < 0 &&
|
||||||
!hasMember!(Hook, "onBadOpOpAssign") ||
|
hasMember!(Hook, "onLowerBound"))
|
||||||
op.among(">>", ">>>"))
|
|
||||||
{
|
{
|
||||||
// No need to check these
|
if (ProperCompare.hookOpCmp(r, min.get) < 0)
|
||||||
payload = cast(T) r;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
static if (isUnsigned!T && !isUnsigned!R)
|
|
||||||
{
|
{
|
||||||
// Example: ushort += int
|
payload = hook.onLowerBound(r, min.get);
|
||||||
import std.conv : unsigned;
|
return this;
|
||||||
const bad = unsigned(r) > max.payload;
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
// Some narrowing is afoot
|
|
||||||
static if (R.min < min.payload)
|
|
||||||
// Example: int += long
|
|
||||||
const bad = r > max.payload || r < min.payload;
|
|
||||||
else
|
|
||||||
// Example: uint += ulong
|
|
||||||
const bad = r > max.payload;
|
|
||||||
if (bad)
|
|
||||||
payload = hook.onBadOpOpAssign!T(r);
|
|
||||||
else
|
|
||||||
payload = cast(T) r;
|
|
||||||
}
|
}
|
||||||
|
static if (ProperCompare.hookOpCmp(max.payload, R.max) < 0 &&
|
||||||
|
hasMember!(Hook, "onUpperBound"))
|
||||||
|
{
|
||||||
|
if (ProperCompare.hookOpCmp(r, max.get) > 0)
|
||||||
|
{
|
||||||
|
payload = hook.onUpperBound(r, min.get);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
payload = cast(T) r;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -835,13 +829,10 @@ instance by using template argument deduction. The hook type may be specified
|
||||||
(by default `Abort`).
|
(by default `Abort`).
|
||||||
|
|
||||||
*/
|
*/
|
||||||
template checked(Hook = Abort)
|
Checked!(T, Hook) checked(Hook = Abort, T)(const T value)
|
||||||
|
if (is(typeof(Checked!(T, Hook)(value))))
|
||||||
{
|
{
|
||||||
Checked!(T, Hook) checked(T)(const T value)
|
return Checked!(T, Hook)(value);
|
||||||
if (is(typeof(Checked!(T, Hook)(value))))
|
|
||||||
{
|
|
||||||
return Checked!(T, Hook)(value);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
|
@ -891,8 +882,7 @@ static:
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
||||||
Called automatically upon a bad `opOpAssign` call (one that loses precision
|
Called automatically upon a bounds error.
|
||||||
or attempts to convert a negative value to an unsigned type).
|
|
||||||
|
|
||||||
Params:
|
Params:
|
||||||
rhs = The right-hand side value in the assignment, after the operator has
|
rhs = The right-hand side value in the assignment, after the operator has
|
||||||
|
@ -903,9 +893,15 @@ static:
|
||||||
it aborts the program.
|
it aborts the program.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
Lhs onBadOpOpAssign(Lhs, Rhs)(Rhs rhs)
|
T onLowerBound(Rhs, T)(Rhs rhs, T bound)
|
||||||
{
|
{
|
||||||
Warn.onBadOpOpAssign!Lhs(rhs);
|
Warn.onLowerBound(rhs, bound);
|
||||||
|
assert(0);
|
||||||
|
}
|
||||||
|
/// ditto
|
||||||
|
T onUpperBound(Rhs, T)(Rhs rhs, T bound)
|
||||||
|
{
|
||||||
|
Warn.onUpperBound(rhs, bound);
|
||||||
assert(0);
|
assert(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1026,11 +1022,18 @@ static:
|
||||||
|
|
||||||
Returns: `cast(Lhs) rhs`
|
Returns: `cast(Lhs) rhs`
|
||||||
*/
|
*/
|
||||||
Lhs onBadOpOpAssign(Lhs, Rhs)(Rhs rhs)
|
Lhs onLowerBound(Rhs, T)(Rhs rhs, T bound)
|
||||||
{
|
{
|
||||||
stderr.writefln("Erroneous assignment: %s = %s(%s)",
|
stderr.writefln("Lower bound error: %s(%s) < %s(%s)",
|
||||||
Lhs.stringof, Rhs.stringof, rhs);
|
Rhs.stringof, rhs, T.stringof, bound);
|
||||||
return cast(Lhs) rhs;
|
return cast(T) rhs;
|
||||||
|
}
|
||||||
|
/// ditto
|
||||||
|
T onUpperBound(Rhs, T)(Rhs rhs, T bound)
|
||||||
|
{
|
||||||
|
stderr.writefln("Upper bound error: %s(%s) > %s(%s)",
|
||||||
|
Rhs.stringof, rhs, T.stringof, bound);
|
||||||
|
return cast(T) rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1260,7 +1263,7 @@ unittest
|
||||||
/**
|
/**
|
||||||
|
|
||||||
Hook that reserves a special value as a "Not a Number" representative. For
|
Hook that reserves a special value as a "Not a Number" representative. For
|
||||||
signed integrals, the reserved value is `T.min`. For signed integrals, the
|
signed integrals, the reserved value is `T.min`. For unsigned integrals, the
|
||||||
reserved value is `T.max`.
|
reserved value is `T.max`.
|
||||||
|
|
||||||
The default value of a $(D Checked!(X, WithNaN)) is its NaN value, so care must
|
The default value of a $(D Checked!(X, WithNaN)) is its NaN value, so care must
|
||||||
|
@ -1333,9 +1336,14 @@ static:
|
||||||
|
|
||||||
Returns: `WithNaN.defaultValue!Lhs`
|
Returns: `WithNaN.defaultValue!Lhs`
|
||||||
*/
|
*/
|
||||||
Lhs onBadOpOpAssign(Lhs, Rhs)(Rhs)
|
T onLowerBound(Rhs, T)(Rhs, T)
|
||||||
{
|
{
|
||||||
return defaultValue!Lhs;
|
return defaultValue!T;
|
||||||
|
}
|
||||||
|
/// ditto
|
||||||
|
T onUpperBound(Rhs, T)(Rhs, T)
|
||||||
|
{
|
||||||
|
return defaultValue!T;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1576,9 +1584,14 @@ static:
|
||||||
Returns: `Lhs.max` if $(D rhs >= 0), `Lhs.min` otherwise.
|
Returns: `Lhs.max` if $(D rhs >= 0), `Lhs.min` otherwise.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
Lhs onBadOpOpAssign(Lhs, Rhs)(Rhs rhs)
|
T onLowerBound(Rhs, T)(Rhs rhs, T bound)
|
||||||
{
|
{
|
||||||
return rhs >= 0 ? Lhs.max : Lhs.min;
|
return bound;
|
||||||
|
}
|
||||||
|
/// ditto
|
||||||
|
T onUpperBound(Rhs, T)(Rhs rhs, T bound)
|
||||||
|
{
|
||||||
|
return bound;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1995,10 +2008,15 @@ version(unittest) private struct CountOverflows
|
||||||
++calls;
|
++calls;
|
||||||
return mixin("lhs" ~ op ~ "rhs");
|
return mixin("lhs" ~ op ~ "rhs");
|
||||||
}
|
}
|
||||||
Lhs onBadOpOpAssign(Lhs, Rhs)(Rhs rhs)
|
T onLowerBound(Rhs, T)(Rhs rhs, T bound)
|
||||||
{
|
{
|
||||||
++calls;
|
++calls;
|
||||||
return cast(Lhs) rhs;
|
return cast(T) rhs;
|
||||||
|
}
|
||||||
|
T onUpperBound(Rhs, T)(Rhs rhs, T bound)
|
||||||
|
{
|
||||||
|
++calls;
|
||||||
|
return cast(T) rhs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue