RFC: Value range propagation for if-else
bearophile via Digitalmars-d
digitalmars-d at puremagic.com
Wed Jun 18 15:08:06 PDT 2014
Nordlöw:
> The simplest example is probably the constructor for Bound
> defined something like
Very good, with this you have framed the discussion well :-)
> - value range information is propagated to the constructor
> argument `value`
The problem is, I think this is currently false (even if you call
your constructor with just a number literal). I'd like this
feature in D, but I don't know how much work it needs to be
implemented.
D language is designed to allow you to create data structures in
library code able to act a lot like built-in features (so there's
a refined operator overloading, opCall, static opCall, and even
more unusual things like opDispatch), but there are built-in
features that can't yet be reproduced in library code, observe:
void main() {
int[5] arr;
auto x = arr[7];
}
dmd 2.066alpha gives a compile error:
test.d(3,14): Error: array index 7 is out of bounds arr[0 .. 5]
In D there is opIndex to overload the [ ], but its arguments are
run-time values, so I think currently they have no way to give a
compile-time error if you use an index that is known statically
to be outside the bounds. So currently you can't reproduce that
behavour with library code. Propagating the value range
information to the constructor, plus the new __trait(valueRange,
exp), allow to solve this problem. And indeed this allows to
implement nice ranged values like in Ada, and to do what the
Static_Predicate of Ada does.
Another common example of what you currently can't do with
library code:
void main() {
import std.bigint;
BigInt[] arr = [10, 20];
import std.numeric;
alias I10 = Bound!(int, 0, 10);
I10[] arr = [8, 6, 20];
}
(In theory the compiler should also catch at compile time that
bug, because 20 is outside the valid bounds of I10.)
The "enum precondition" I've suggested elsewhere is a
generalization of that feature (but it's not a strict subset
because it only works with literals, so it's good to have both
features), because it manages not just ranges of a single value,
but also other literals, like an array:
void foo(int[] arr)
enum in {
assert(arr.all!(x => x < 10));
} in {
assert(arr.all!(x => x < 10));
} body {
// ...
}
void main() {
foo([10, 20]); // gives compile-time error.
}
This is possible only if the source code of foo is available in
the compilation unit. In presence of separated compilation the
enum precondition is ignored. So the enum preconditon can't
replace regular pre-conditions, they are useful to catch
statically only a subset of bugs. The same happens with the idea
of propagating range information to the constructors. One way to
avoid or mitigate this problem is to leave available the source
code of functions with an enum pre-conditions, just like with
templates. Perhaps this is not a big problem because enum
preconditions and constructor value range propagation are meant
to be used mostly in library code, like in Bound integers, etc.
Bye,
bearophile
More information about the Digitalmars-d
mailing list