Skip to content

Commit 6e236aa

Browse files
committed
rustc: teach const_eval more about types.
1 parent f20e092 commit 6e236aa

File tree

10 files changed

+195
-140
lines changed

10 files changed

+195
-140
lines changed

src/librustc/lint/builtin.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ impl LintPass for TypeLimits {
199199
if let ast::LitInt(shift, _) = lit.node { shift >= bits }
200200
else { false }
201201
} else {
202-
match eval_const_expr_partial(cx.tcx, &**r) {
202+
match eval_const_expr_partial(cx.tcx, &**r, Some(cx.tcx.types.uint)) {
203203
Ok(const_int(shift)) => { shift as u64 >= bits },
204204
Ok(const_uint(shift)) => { shift >= bits },
205205
_ => { false }

src/librustc/middle/check_match.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use middle::mem_categorization::cmt;
2323
use middle::pat_util::*;
2424
use middle::ty::*;
2525
use middle::ty;
26+
use std::cmp::Ordering;
2627
use std::fmt;
2728
use std::iter::{range_inclusive, AdditiveIterator, FromIterator, repeat};
2829
use std::num::Float;
@@ -766,7 +767,9 @@ fn range_covered_by_constructor(ctor: &Constructor,
766767
let cmp_from = compare_const_vals(c_from, from);
767768
let cmp_to = compare_const_vals(c_to, to);
768769
match (cmp_from, cmp_to) {
769-
(Some(val1), Some(val2)) => Some(val1 >= 0 && val2 <= 0),
770+
(Some(cmp_from), Some(cmp_to)) => {
771+
Some(cmp_from != Ordering::Less && cmp_to != Ordering::Greater)
772+
}
770773
_ => None
771774
}
772775
}

src/librustc/middle/const_eval.rs

Lines changed: 142 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,15 @@ pub use self::const_val::*;
1616
use metadata::csearch;
1717
use middle::{astencode, def};
1818
use middle::pat_util::def_to_path;
19-
use middle::ty::{self};
19+
use middle::ty::{self, Ty};
2020
use middle::astconv_util::{ast_ty_to_prim_ty};
2121

2222
use syntax::ast::{self, Expr};
2323
use syntax::parse::token::InternedString;
2424
use syntax::ptr::P;
2525
use syntax::{ast_map, ast_util, codemap};
2626

27+
use std::cmp::Ordering;
2728
use std::collections::hash_map::Entry::Vacant;
2829
use std::rc::Rc;
2930

@@ -204,17 +205,23 @@ pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr) -> P<ast::Pat> {
204205
}
205206

206207
pub fn eval_const_expr(tcx: &ty::ctxt, e: &Expr) -> const_val {
207-
match eval_const_expr_partial(tcx, e) {
208+
match eval_const_expr_partial(tcx, e, None) {
208209
Ok(r) => r,
209210
Err(s) => tcx.sess.span_fatal(e.span, &s[])
210211
}
211212
}
212213

213-
pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, String> {
214+
pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
215+
e: &Expr,
216+
ty_hint: Option<Ty<'tcx>>)
217+
-> Result<const_val, String> {
214218
fn fromb(b: bool) -> Result<const_val, String> { Ok(const_int(b as i64)) }
219+
220+
let ety = ty_hint.or_else(|| ty::expr_ty_opt(tcx, e));
221+
215222
match e.node {
216223
ast::ExprUnary(ast::UnNeg, ref inner) => {
217-
match eval_const_expr_partial(tcx, &**inner) {
224+
match eval_const_expr_partial(tcx, &**inner, ety) {
218225
Ok(const_float(f)) => Ok(const_float(-f)),
219226
Ok(const_int(i)) => Ok(const_int(-i)),
220227
Ok(const_uint(i)) => Ok(const_uint(-i)),
@@ -224,16 +231,20 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
224231
}
225232
}
226233
ast::ExprUnary(ast::UnNot, ref inner) => {
227-
match eval_const_expr_partial(tcx, &**inner) {
234+
match eval_const_expr_partial(tcx, &**inner, ety) {
228235
Ok(const_int(i)) => Ok(const_int(!i)),
229236
Ok(const_uint(i)) => Ok(const_uint(!i)),
230237
Ok(const_bool(b)) => Ok(const_bool(!b)),
231238
_ => Err("not on float or string".to_string())
232239
}
233240
}
234241
ast::ExprBinary(op, ref a, ref b) => {
235-
match (eval_const_expr_partial(tcx, &**a),
236-
eval_const_expr_partial(tcx, &**b)) {
242+
let b_ty = match op.node {
243+
ast::BiShl | ast::BiShr => Some(tcx.types.uint),
244+
_ => ety
245+
};
246+
match (eval_const_expr_partial(tcx, &**a, ety),
247+
eval_const_expr_partial(tcx, &**b, b_ty)) {
237248
(Ok(const_float(a)), Ok(const_float(b))) => {
238249
match op.node {
239250
ast::BiAdd => Ok(const_float(a + b)),
@@ -338,63 +349,53 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
338349
// This tends to get called w/o the type actually having been
339350
// populated in the ctxt, which was causing things to blow up
340351
// (#5900). Fall back to doing a limited lookup to get past it.
341-
let ety = ty::expr_ty_opt(tcx, e)
342-
.or_else(|| ast_ty_to_prim_ty(tcx, &**target_ty))
352+
let ety = ety.or_else(|| ast_ty_to_prim_ty(tcx, &**target_ty))
343353
.unwrap_or_else(|| {
344354
tcx.sess.span_fatal(target_ty.span,
345355
"target type not found for const cast")
346356
});
347-
348-
macro_rules! define_casts {
349-
($val:ident, {
350-
$($ty_pat:pat => (
351-
$intermediate_ty:ty,
352-
$const_type:ident,
353-
$target_ty:ty
354-
)),*
355-
}) => (match ety.sty {
356-
$($ty_pat => {
357-
match $val {
358-
const_bool(b) => Ok($const_type(b as $intermediate_ty as $target_ty)),
359-
const_uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)),
360-
const_int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)),
361-
const_float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)),
362-
_ => Err(concat!(
363-
"can't cast this type to ", stringify!($const_type)
364-
).to_string())
365-
}
366-
},)*
367-
_ => Err("can't cast this type".to_string())
368-
})
369-
}
370-
371-
eval_const_expr_partial(tcx, &**base)
372-
.and_then(|val| define_casts!(val, {
373-
ty::ty_int(ast::TyIs(_)) => (int, const_int, i64),
374-
ty::ty_int(ast::TyI8) => (i8, const_int, i64),
375-
ty::ty_int(ast::TyI16) => (i16, const_int, i64),
376-
ty::ty_int(ast::TyI32) => (i32, const_int, i64),
377-
ty::ty_int(ast::TyI64) => (i64, const_int, i64),
378-
ty::ty_uint(ast::TyUs(_)) => (uint, const_uint, u64),
379-
ty::ty_uint(ast::TyU8) => (u8, const_uint, u64),
380-
ty::ty_uint(ast::TyU16) => (u16, const_uint, u64),
381-
ty::ty_uint(ast::TyU32) => (u32, const_uint, u64),
382-
ty::ty_uint(ast::TyU64) => (u64, const_uint, u64),
383-
ty::ty_float(ast::TyF32) => (f32, const_float, f64),
384-
ty::ty_float(ast::TyF64) => (f64, const_float, f64)
385-
}))
357+
// Prefer known type to noop, but always have a type hint.
358+
let base_hint = ty::expr_ty_opt(tcx, &**base).unwrap_or(ety);
359+
let val = try!(eval_const_expr_partial(tcx, &**base, Some(base_hint)));
360+
cast_const(val, ety)
386361
}
387362
ast::ExprPath(_) | ast::ExprQPath(_) => {
388-
match lookup_const(tcx, e) {
389-
Some(actual_e) => eval_const_expr_partial(tcx, &*actual_e),
390-
None => Err("non-constant path in constant expr".to_string())
391-
}
363+
let opt_def = tcx.def_map.borrow().get(&e.id).cloned();
364+
let (const_expr, const_ty) = match opt_def {
365+
Some(def::DefConst(def_id)) => {
366+
if ast_util::is_local(def_id) {
367+
match tcx.map.find(def_id.node) {
368+
Some(ast_map::NodeItem(it)) => match it.node {
369+
ast::ItemConst(ref ty, ref expr) => {
370+
(Some(&**expr), Some(&**ty))
371+
}
372+
_ => (None, None)
373+
},
374+
_ => (None, None)
375+
}
376+
} else {
377+
(lookup_const_by_id(tcx, def_id), None)
378+
}
379+
}
380+
Some(def::DefVariant(enum_def, variant_def, _)) => {
381+
(lookup_variant_by_id(tcx, enum_def, variant_def), None)
382+
}
383+
_ => (None, None)
384+
};
385+
let const_expr = match const_expr {
386+
Some(actual_e) => actual_e,
387+
None => return Err("non-constant path in constant expr".to_string())
388+
};
389+
let ety = ety.or_else(|| const_ty.and_then(|ty| ast_ty_to_prim_ty(tcx, ty)));
390+
eval_const_expr_partial(tcx, const_expr, ety)
392391
}
393-
ast::ExprLit(ref lit) => Ok(lit_to_const(&**lit)),
394-
ast::ExprParen(ref e) => eval_const_expr_partial(tcx, &**e),
392+
ast::ExprLit(ref lit) => {
393+
Ok(lit_to_const(&**lit, ety))
394+
}
395+
ast::ExprParen(ref e) => eval_const_expr_partial(tcx, &**e, ety),
395396
ast::ExprBlock(ref block) => {
396397
match block.expr {
397-
Some(ref expr) => eval_const_expr_partial(tcx, &**expr),
398+
Some(ref expr) => eval_const_expr_partial(tcx, &**expr, ety),
398399
None => Ok(const_int(0i64))
399400
}
400401
}
@@ -403,7 +404,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
403404
if let Some(&ast::ExprTup(ref fields)) = lookup_const(tcx, &**base).map(|s| &s.node) {
404405
// Check that the given index is within bounds and evaluate its value
405406
if fields.len() > index.node {
406-
return eval_const_expr_partial(tcx, &*fields[index.node])
407+
return eval_const_expr_partial(tcx, &*fields[index.node], None)
407408
} else {
408409
return Err("tuple index out of bounds".to_string())
409410
}
@@ -418,7 +419,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
418419
// Check that the given field exists and evaluate it
419420
if let Some(f) = fields.iter().find(|f|
420421
f.ident.node.as_str() == field_name.node.as_str()) {
421-
return eval_const_expr_partial(tcx, &*f.expr)
422+
return eval_const_expr_partial(tcx, &*f.expr, None)
422423
} else {
423424
return Err("nonexistent struct field".to_string())
424425
}
@@ -430,16 +431,58 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
430431
}
431432
}
432433

433-
pub fn lit_to_const(lit: &ast::Lit) -> const_val {
434+
fn cast_const(val: const_val, ty: Ty) -> Result<const_val, String> {
435+
macro_rules! define_casts {
436+
($($ty_pat:pat => (
437+
$intermediate_ty:ty,
438+
$const_type:ident,
439+
$target_ty:ty
440+
)),*) => (match ty.sty {
441+
$($ty_pat => {
442+
match val {
443+
const_bool(b) => Ok($const_type(b as $intermediate_ty as $target_ty)),
444+
const_uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)),
445+
const_int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)),
446+
const_float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)),
447+
_ => Err(concat!("can't cast this type to ",
448+
stringify!($const_type)).to_string())
449+
}
450+
},)*
451+
_ => Err("can't cast this type".to_string())
452+
})
453+
}
454+
455+
define_casts!{
456+
ty::ty_int(ast::TyIs(_)) => (int, const_int, i64),
457+
ty::ty_int(ast::TyI8) => (i8, const_int, i64),
458+
ty::ty_int(ast::TyI16) => (i16, const_int, i64),
459+
ty::ty_int(ast::TyI32) => (i32, const_int, i64),
460+
ty::ty_int(ast::TyI64) => (i64, const_int, i64),
461+
ty::ty_uint(ast::TyUs(_)) => (uint, const_uint, u64),
462+
ty::ty_uint(ast::TyU8) => (u8, const_uint, u64),
463+
ty::ty_uint(ast::TyU16) => (u16, const_uint, u64),
464+
ty::ty_uint(ast::TyU32) => (u32, const_uint, u64),
465+
ty::ty_uint(ast::TyU64) => (u64, const_uint, u64),
466+
ty::ty_float(ast::TyF32) => (f32, const_float, f64),
467+
ty::ty_float(ast::TyF64) => (f64, const_float, f64)
468+
}
469+
}
470+
471+
fn lit_to_const(lit: &ast::Lit, ty_hint: Option<Ty>) -> const_val {
434472
match lit.node {
435473
ast::LitStr(ref s, _) => const_str((*s).clone()),
436474
ast::LitBinary(ref data) => {
437475
const_binary(Rc::new(data.iter().map(|x| *x).collect()))
438476
}
439477
ast::LitByte(n) => const_uint(n as u64),
440478
ast::LitChar(n) => const_uint(n as u64),
441-
ast::LitInt(n, ast::SignedIntLit(_, ast::Plus)) |
442-
ast::LitInt(n, ast::UnsuffixedIntLit(ast::Plus)) => const_int(n as i64),
479+
ast::LitInt(n, ast::SignedIntLit(_, ast::Plus)) => const_int(n as i64),
480+
ast::LitInt(n, ast::UnsuffixedIntLit(ast::Plus)) => {
481+
match ty_hint.map(|ty| &ty.sty) {
482+
Some(&ty::ty_uint(_)) => const_uint(n),
483+
_ => const_int(n as i64)
484+
}
485+
}
443486
ast::LitInt(n, ast::SignedIntLit(_, ast::Minus)) |
444487
ast::LitInt(n, ast::UnsuffixedIntLit(ast::Minus)) => const_int(-(n as i64)),
445488
ast::LitInt(n, ast::UnsignedIntLit(_)) => const_uint(n),
@@ -451,21 +494,45 @@ pub fn lit_to_const(lit: &ast::Lit) -> const_val {
451494
}
452495
}
453496

454-
fn compare_vals<T: PartialOrd>(a: T, b: T) -> Option<int> {
455-
Some(if a == b { 0 } else if a < b { -1 } else { 1 })
456-
}
457-
pub fn compare_const_vals(a: &const_val, b: &const_val) -> Option<int> {
458-
match (a, b) {
459-
(&const_int(a), &const_int(b)) => compare_vals(a, b),
460-
(&const_uint(a), &const_uint(b)) => compare_vals(a, b),
461-
(&const_float(a), &const_float(b)) => compare_vals(a, b),
462-
(&const_str(ref a), &const_str(ref b)) => compare_vals(a, b),
463-
(&const_bool(a), &const_bool(b)) => compare_vals(a, b),
464-
(&const_binary(ref a), &const_binary(ref b)) => compare_vals(a, b),
465-
_ => None
466-
}
497+
pub fn compare_const_vals(a: &const_val, b: &const_val) -> Option<Ordering> {
498+
Some(match (a, b) {
499+
(&const_int(a), &const_int(b)) => a.cmp(&b),
500+
(&const_uint(a), &const_uint(b)) => a.cmp(&b),
501+
(&const_float(a), &const_float(b)) => {
502+
// This is pretty bad but it is the existing behavior.
503+
if a == b {
504+
Ordering::Equal
505+
} else if a < b {
506+
Ordering::Less
507+
} else {
508+
Ordering::Greater
509+
}
510+
}
511+
(&const_str(ref a), &const_str(ref b)) => a.cmp(b),
512+
(&const_bool(a), &const_bool(b)) => a.cmp(&b),
513+
(&const_binary(ref a), &const_binary(ref b)) => a.cmp(b),
514+
_ => return None
515+
})
467516
}
468517

469-
pub fn compare_lit_exprs(tcx: &ty::ctxt, a: &Expr, b: &Expr) -> Option<int> {
470-
compare_const_vals(&eval_const_expr(tcx, a), &eval_const_expr(tcx, b))
518+
pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>,
519+
a: &Expr,
520+
b: &Expr,
521+
ty_hint: Option<Ty<'tcx>>)
522+
-> Option<Ordering> {
523+
let a = match eval_const_expr_partial(tcx, a, ty_hint) {
524+
Ok(a) => a,
525+
Err(s) => {
526+
tcx.sess.span_err(a.span, &s[]);
527+
return None;
528+
}
529+
};
530+
let b = match eval_const_expr_partial(tcx, b, ty_hint) {
531+
Ok(b) => b,
532+
Err(s) => {
533+
tcx.sess.span_err(b.span, &s[]);
534+
return None;
535+
}
536+
};
537+
compare_const_vals(&a, &b)
471538
}

src/librustc/middle/ty.rs

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5302,26 +5302,25 @@ pub fn enum_variants<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId)
53025302
None => INITIAL_DISCRIMINANT_VALUE
53035303
};
53045304

5305-
match variant.node.disr_expr {
5306-
Some(ref e) =>
5307-
match const_eval::eval_const_expr_partial(cx, &**e) {
5308-
Ok(const_eval::const_int(val)) => {
5309-
discriminant = val as Disr
5310-
}
5311-
Ok(const_eval::const_uint(val)) => {
5312-
discriminant = val as Disr
5313-
}
5314-
Ok(_) => {
5315-
span_err!(cx.sess, e.span, E0304,
5316-
"expected signed integer constant");
5317-
}
5318-
Err(ref err) => {
5319-
span_err!(cx.sess, e.span, E0305,
5320-
"expected constant: {}",
5321-
*err);
5322-
}
5323-
},
5324-
None => {}
5305+
if let Some(ref e) = variant.node.disr_expr {
5306+
// Preserve all values, and prefer signed.
5307+
let ty = Some(cx.types.i64);
5308+
match const_eval::eval_const_expr_partial(cx, &**e, ty) {
5309+
Ok(const_eval::const_int(val)) => {
5310+
discriminant = val as Disr;
5311+
}
5312+
Ok(const_eval::const_uint(val)) => {
5313+
discriminant = val as Disr;
5314+
}
5315+
Ok(_) => {
5316+
span_err!(cx.sess, e.span, E0304,
5317+
"expected signed integer constant");
5318+
}
5319+
Err(err) => {
5320+
span_err!(cx.sess, e.span, E0305,
5321+
"expected constant: {}", err);
5322+
}
5323+
}
53255324
};
53265325

53275326
last_discriminant = Some(discriminant);
@@ -5776,7 +5775,7 @@ pub fn is_binopable<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>, op: ast::BinOp) -> bool
57765775

57775776
// Returns the repeat count for a repeating vector expression.
57785777
pub fn eval_repeat_count(tcx: &ctxt, count_expr: &ast::Expr) -> uint {
5779-
match const_eval::eval_const_expr_partial(tcx, count_expr) {
5778+
match const_eval::eval_const_expr_partial(tcx, count_expr, Some(tcx.types.uint)) {
57805779
Ok(val) => {
57815780
let found = match val {
57825781
const_eval::const_uint(count) => return count as uint,

0 commit comments

Comments
 (0)