Description
const_eval sometimes confuses a guess at expected type with expression's actual type
Here's an example:
use std::fmt;
use std::{i8, i16, i32, i64, isize};
use std::{u8, u16, u32, u64, usize};
const A_I8_T
: [u32; (i8::MAX as i8 - 1i8) as usize]
= [0; (i8::MAX as usize) - 1];
fn main() {
foo(&A_I8_T[..]);
}
fn foo<T:fmt::Debug>(x: T) {
println!("{:?}", x);
}
playpen yields:
<anon>:6:14: 6:33 error: array length constant evaluation error: can't do this op on a usize and isize [E0250]
<anon>:6 : [u32; (i8::MAX as i8 - 1i8) as usize]
^~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
playpen: application terminated with error code 101
I believe this is due to this line in const_eval.rs
:
https://github.com/rust-lang/rust/blob/master/src/librustc/middle/const_eval.rs#L504
(transcribed immediately below for ease of reference:)
Namely, the logic here takes a constant expression like the one in the above count expression and feeds the expected usize
type down into the computation of (i8::MAX as i8 - 1i8)
.
ast::ExprCast(ref base, ref target_ty) => {
// This tends to get called w/o the type actually having been
// populated in the ctxt, which was causing things to blow up
// (#5900). Fall back to doing a limited lookup to get past it.
let ety = ety.or_else(|| ast_ty_to_prim_ty(tcx, &**target_ty))
.unwrap_or_else(|| {
tcx.sess.span_fatal(target_ty.span,
"target type not found for const cast")
});
// Prefer known type to noop, but always have a type hint.
let base_hint = ty::expr_ty_opt(tcx, &**base).unwrap_or(ety);
let val = try!(eval_const_expr_partial(tcx, &**base, Some(base_hint)));
match cast_const(val, ety) {
Ok(val) => val,
Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }),
}
}
Even though there are many many hints in (i8::MAX as i8 - 1i8)
that are attempting to force the left- and right-hand sides to both be i8
, the const_eval is ignoring them and trying to force the left-hand-side to be a usize
.
(This causes problems for proper overflow detection in const_eval.)