PATCH COMMITTED: Don't break tests for enum in range
Ian Lance Taylor
iant@google.com
Wed Jun 6 15:42:00 GMT 2007
Eric Botcazou <ebotcazou@adacore.com> writes:
> > I noticed that gcc was in some cases breaking tests for whether a
> > value was in range for an enum. An example is this, in C++:
> >
> > enum E { V = 1 };
> > static const E E_MIN = V;
> > static const E E_MAX = V;
> >
> > bool valid(E v) { return v >= E_MIN && v <= E_MAX; }
> >
> > int main() { return valid(E(2)); }
> >
> > Here the test in main was returning 1 when one would expect it to
> > return 0.
>
> Quite interesting, this is precisely THE situation where VRP breaks Ada if
> counter-measures a la VIEW_CONVERT_EXPR are not used. AFAIK the C++ compiler
> doesn't generate VIEW_CONVERT_EXPR in this case, so can't VRP draw the same
> wrong conclusion too?
I *think* that for a case like this VRP will tend to draw the right
conclusion: that valid should return false.
> > Looking at the code, the cases where range_successor and
> > range_predecessor return NULL are cases where the ranges being merged
> > are contradictory. For example, in the above test case it happens
> > when merging [L1,H1] and [L2,H2] such that L1 == L2 and H1 < H2. The
> > code asks for the successor of H1, and fails. But note that H1 < H2,
> > so if H1 has no successor, where did H2 come from (in truth it came
> > from TYPE_MAX_VALUE of type_for_mode)? Rather than return a nonsense
> > result, I think it's better for merge_ranges to simply punt. And
> > making that change fixes the bug.
>
> Could you clarify in which case we are exactly for merge_ranges?
make_range is dealing with "(int) v <= E_MAX". The enum is unsigned,
so when handling the NOP_EXPR it enter this code in make_range:
if (!TYPE_UNSIGNED (exp_type) && TYPE_UNSIGNED (arg0_type))
Then low == NULL, so it does this one:
/* Otherwise, "or" the range with the range of the input
that will be interpreted as negative. */
if (! merge_ranges (&n_in_p, &n_low, &n_high,
0, n_low, n_high, 1,
fold_convert (arg0_type,
integer_zero_node),
high_positive))
Here n_low == NULL and n_high == 1 in type E. high_positive is
0x7fffffff, also in type E (note this is out of range according to
TYPE_MAX_VALUE, but not according to TYPE_PRECISION which is 32).
In merge_ranges, low0 is NULL, high0 is 1, low1 is 0, high1 is
0x7fffffff, in0_p is false, in1_p is true, lowequal is false,
highequal is false, no_overlap is false, subset is false. We come to
this code:
low = range_successor (high0);
high = high1;
in_p = (low != 0);
range_successor (high0) returns NULL. We wind up setting *plow to
NULL, *phigh to 0x7fffffff, and *pin_p to 0. Since we have an
unsigned type, this is the range [0x80000000, 0xffffffff]. This is
meaningless. The range we actually want is [2, 0x7fffffff].
Alternatively it would probably work to return an empty range, if we
had a way to represent that. Returning [0x80000000, 0xffffffff] gives
us garbage. When that range is merged with the range from the other
side of the conditional, we get the wrong answer.
Ian
More information about the Gcc-patches
mailing list