rustc: teach const_eval more about types.

This commit is contained in:
Eduard Burtescu 2015-01-29 13:40:14 +02:00
parent 4d8f995c3a
commit cb3c9a1e88
10 changed files with 195 additions and 140 deletions

View File

@ -199,7 +199,7 @@ impl LintPass for TypeLimits {
if let ast::LitInt(shift, _) = lit.node { shift >= bits }
else { false }
} else {
match eval_const_expr_partial(cx.tcx, &**r) {
match eval_const_expr_partial(cx.tcx, &**r, Some(cx.tcx.types.uint)) {
Ok(const_int(shift)) => { shift as u64 >= bits },
Ok(const_uint(shift)) => { shift >= bits },
_ => { false }

View File

@ -23,6 +23,7 @@ use middle::mem_categorization::cmt;
use middle::pat_util::*;
use middle::ty::*;
use middle::ty;
use std::cmp::Ordering;
use std::fmt;
use std::iter::{range_inclusive, AdditiveIterator, FromIterator, repeat};
use std::num::Float;
@ -821,7 +822,9 @@ fn range_covered_by_constructor(ctor: &Constructor,
let cmp_from = compare_const_vals(c_from, from);
let cmp_to = compare_const_vals(c_to, to);
match (cmp_from, cmp_to) {
(Some(val1), Some(val2)) => Some(val1 >= 0 && val2 <= 0),
(Some(cmp_from), Some(cmp_to)) => {
Some(cmp_from != Ordering::Less && cmp_to != Ordering::Greater)
}
_ => None
}
}

View File

@ -16,7 +16,7 @@ pub use self::const_val::*;
use metadata::csearch;
use middle::{astencode, def};
use middle::pat_util::def_to_path;
use middle::ty::{self};
use middle::ty::{self, Ty};
use middle::astconv_util::{ast_ty_to_prim_ty};
use syntax::ast::{self, Expr};
@ -25,6 +25,7 @@ use syntax::parse::token::InternedString;
use syntax::ptr::P;
use syntax::{ast_map, ast_util, codemap};
use std::cmp::Ordering;
use std::collections::hash_map::Entry::Vacant;
use std::rc::Rc;
@ -205,17 +206,23 @@ pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P<ast::Pat>
}
pub fn eval_const_expr(tcx: &ty::ctxt, e: &Expr) -> const_val {
match eval_const_expr_partial(tcx, e) {
match eval_const_expr_partial(tcx, e, None) {
Ok(r) => r,
Err(s) => tcx.sess.span_fatal(e.span, &s[])
}
}
pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, String> {
pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
e: &Expr,
ty_hint: Option<Ty<'tcx>>)
-> Result<const_val, String> {
fn fromb(b: bool) -> Result<const_val, String> { Ok(const_int(b as i64)) }
let ety = ty_hint.or_else(|| ty::expr_ty_opt(tcx, e));
match e.node {
ast::ExprUnary(ast::UnNeg, ref inner) => {
match eval_const_expr_partial(tcx, &**inner) {
match eval_const_expr_partial(tcx, &**inner, ety) {
Ok(const_float(f)) => Ok(const_float(-f)),
Ok(const_int(i)) => Ok(const_int(-i)),
Ok(const_uint(i)) => Ok(const_uint(-i)),
@ -225,7 +232,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
}
}
ast::ExprUnary(ast::UnNot, ref inner) => {
match eval_const_expr_partial(tcx, &**inner) {
match eval_const_expr_partial(tcx, &**inner, ety) {
Ok(const_int(i)) => Ok(const_int(!i)),
Ok(const_uint(i)) => Ok(const_uint(!i)),
Ok(const_bool(b)) => Ok(const_bool(!b)),
@ -233,8 +240,12 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
}
}
ast::ExprBinary(op, ref a, ref b) => {
match (eval_const_expr_partial(tcx, &**a),
eval_const_expr_partial(tcx, &**b)) {
let b_ty = match op.node {
ast::BiShl | ast::BiShr => Some(tcx.types.uint),
_ => ety
};
match (eval_const_expr_partial(tcx, &**a, ety),
eval_const_expr_partial(tcx, &**b, b_ty)) {
(Ok(const_float(a)), Ok(const_float(b))) => {
match op.node {
ast::BiAdd => Ok(const_float(a + b)),
@ -339,63 +350,53 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
// 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 = ty::expr_ty_opt(tcx, e)
.or_else(|| ast_ty_to_prim_ty(tcx, &**target_ty))
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")
});
macro_rules! define_casts {
($val:ident, {
$($ty_pat:pat => (
$intermediate_ty:ty,
$const_type:ident,
$target_ty:ty
)),*
}) => (match ety.sty {
$($ty_pat => {
match $val {
const_bool(b) => Ok($const_type(b as $intermediate_ty as $target_ty)),
const_uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)),
const_int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)),
const_float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)),
_ => Err(concat!(
"can't cast this type to ", stringify!($const_type)
).to_string())
}
},)*
_ => Err("can't cast this type".to_string())
})
}
eval_const_expr_partial(tcx, &**base)
.and_then(|val| define_casts!(val, {
ty::ty_int(ast::TyIs(_)) => (int, const_int, i64),
ty::ty_int(ast::TyI8) => (i8, const_int, i64),
ty::ty_int(ast::TyI16) => (i16, const_int, i64),
ty::ty_int(ast::TyI32) => (i32, const_int, i64),
ty::ty_int(ast::TyI64) => (i64, const_int, i64),
ty::ty_uint(ast::TyUs(_)) => (uint, const_uint, u64),
ty::ty_uint(ast::TyU8) => (u8, const_uint, u64),
ty::ty_uint(ast::TyU16) => (u16, const_uint, u64),
ty::ty_uint(ast::TyU32) => (u32, const_uint, u64),
ty::ty_uint(ast::TyU64) => (u64, const_uint, u64),
ty::ty_float(ast::TyF32) => (f32, const_float, f64),
ty::ty_float(ast::TyF64) => (f64, const_float, f64)
}))
// 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)));
cast_const(val, ety)
}
ast::ExprPath(_) | ast::ExprQPath(_) => {
match lookup_const(tcx, e) {
Some(actual_e) => eval_const_expr_partial(tcx, &*actual_e),
None => Err("non-constant path in constant expr".to_string())
}
let opt_def = tcx.def_map.borrow().get(&e.id).cloned();
let (const_expr, const_ty) = match opt_def {
Some(def::DefConst(def_id)) => {
if ast_util::is_local(def_id) {
match tcx.map.find(def_id.node) {
Some(ast_map::NodeItem(it)) => match it.node {
ast::ItemConst(ref ty, ref expr) => {
(Some(&**expr), Some(&**ty))
}
_ => (None, None)
},
_ => (None, None)
}
} else {
(lookup_const_by_id(tcx, def_id), None)
}
}
Some(def::DefVariant(enum_def, variant_def, _)) => {
(lookup_variant_by_id(tcx, enum_def, variant_def), None)
}
_ => (None, None)
};
let const_expr = match const_expr {
Some(actual_e) => actual_e,
None => return Err("non-constant path in constant expr".to_string())
};
let ety = ety.or_else(|| const_ty.and_then(|ty| ast_ty_to_prim_ty(tcx, ty)));
eval_const_expr_partial(tcx, const_expr, ety)
}
ast::ExprLit(ref lit) => Ok(lit_to_const(&**lit)),
ast::ExprParen(ref e) => eval_const_expr_partial(tcx, &**e),
ast::ExprLit(ref lit) => {
Ok(lit_to_const(&**lit, ety))
}
ast::ExprParen(ref e) => eval_const_expr_partial(tcx, &**e, ety),
ast::ExprBlock(ref block) => {
match block.expr {
Some(ref expr) => eval_const_expr_partial(tcx, &**expr),
Some(ref expr) => eval_const_expr_partial(tcx, &**expr, ety),
None => Ok(const_int(0i64))
}
}
@ -404,7 +405,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
if let Some(&ast::ExprTup(ref fields)) = lookup_const(tcx, &**base).map(|s| &s.node) {
// Check that the given index is within bounds and evaluate its value
if fields.len() > index.node {
return eval_const_expr_partial(tcx, &*fields[index.node])
return eval_const_expr_partial(tcx, &*fields[index.node], None)
} else {
return Err("tuple index out of bounds".to_string())
}
@ -419,7 +420,7 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
// Check that the given field exists and evaluate it
if let Some(f) = fields.iter().find(|f|
f.ident.node.as_str() == field_name.node.as_str()) {
return eval_const_expr_partial(tcx, &*f.expr)
return eval_const_expr_partial(tcx, &*f.expr, None)
} else {
return Err("nonexistent struct field".to_string())
}
@ -431,7 +432,44 @@ pub fn eval_const_expr_partial(tcx: &ty::ctxt, e: &Expr) -> Result<const_val, St
}
}
pub fn lit_to_const(lit: &ast::Lit) -> const_val {
fn cast_const(val: const_val, ty: Ty) -> Result<const_val, String> {
macro_rules! define_casts {
($($ty_pat:pat => (
$intermediate_ty:ty,
$const_type:ident,
$target_ty:ty
)),*) => (match ty.sty {
$($ty_pat => {
match val {
const_bool(b) => Ok($const_type(b as $intermediate_ty as $target_ty)),
const_uint(u) => Ok($const_type(u as $intermediate_ty as $target_ty)),
const_int(i) => Ok($const_type(i as $intermediate_ty as $target_ty)),
const_float(f) => Ok($const_type(f as $intermediate_ty as $target_ty)),
_ => Err(concat!("can't cast this type to ",
stringify!($const_type)).to_string())
}
},)*
_ => Err("can't cast this type".to_string())
})
}
define_casts!{
ty::ty_int(ast::TyIs(_)) => (int, const_int, i64),
ty::ty_int(ast::TyI8) => (i8, const_int, i64),
ty::ty_int(ast::TyI16) => (i16, const_int, i64),
ty::ty_int(ast::TyI32) => (i32, const_int, i64),
ty::ty_int(ast::TyI64) => (i64, const_int, i64),
ty::ty_uint(ast::TyUs(_)) => (uint, const_uint, u64),
ty::ty_uint(ast::TyU8) => (u8, const_uint, u64),
ty::ty_uint(ast::TyU16) => (u16, const_uint, u64),
ty::ty_uint(ast::TyU32) => (u32, const_uint, u64),
ty::ty_uint(ast::TyU64) => (u64, const_uint, u64),
ty::ty_float(ast::TyF32) => (f32, const_float, f64),
ty::ty_float(ast::TyF64) => (f64, const_float, f64)
}
}
fn lit_to_const(lit: &ast::Lit, ty_hint: Option<Ty>) -> const_val {
match lit.node {
ast::LitStr(ref s, _) => const_str((*s).clone()),
ast::LitBinary(ref data) => {
@ -439,8 +477,13 @@ pub fn lit_to_const(lit: &ast::Lit) -> const_val {
}
ast::LitByte(n) => const_uint(n as u64),
ast::LitChar(n) => const_uint(n as u64),
ast::LitInt(n, ast::SignedIntLit(_, ast::Plus)) |
ast::LitInt(n, ast::UnsuffixedIntLit(ast::Plus)) => const_int(n as i64),
ast::LitInt(n, ast::SignedIntLit(_, ast::Plus)) => const_int(n as i64),
ast::LitInt(n, ast::UnsuffixedIntLit(ast::Plus)) => {
match ty_hint.map(|ty| &ty.sty) {
Some(&ty::ty_uint(_)) => const_uint(n),
_ => const_int(n as i64)
}
}
ast::LitInt(n, ast::SignedIntLit(_, ast::Minus)) |
ast::LitInt(n, ast::UnsuffixedIntLit(ast::Minus)) => const_int(-(n as i64)),
ast::LitInt(n, ast::UnsignedIntLit(_)) => const_uint(n),
@ -452,21 +495,45 @@ pub fn lit_to_const(lit: &ast::Lit) -> const_val {
}
}
fn compare_vals<T: PartialOrd>(a: T, b: T) -> Option<int> {
Some(if a == b { 0 } else if a < b { -1 } else { 1 })
}
pub fn compare_const_vals(a: &const_val, b: &const_val) -> Option<int> {
match (a, b) {
(&const_int(a), &const_int(b)) => compare_vals(a, b),
(&const_uint(a), &const_uint(b)) => compare_vals(a, b),
(&const_float(a), &const_float(b)) => compare_vals(a, b),
(&const_str(ref a), &const_str(ref b)) => compare_vals(a, b),
(&const_bool(a), &const_bool(b)) => compare_vals(a, b),
(&const_binary(ref a), &const_binary(ref b)) => compare_vals(a, b),
_ => None
}
pub fn compare_const_vals(a: &const_val, b: &const_val) -> Option<Ordering> {
Some(match (a, b) {
(&const_int(a), &const_int(b)) => a.cmp(&b),
(&const_uint(a), &const_uint(b)) => a.cmp(&b),
(&const_float(a), &const_float(b)) => {
// This is pretty bad but it is the existing behavior.
if a == b {
Ordering::Equal
} else if a < b {
Ordering::Less
} else {
Ordering::Greater
}
}
(&const_str(ref a), &const_str(ref b)) => a.cmp(b),
(&const_bool(a), &const_bool(b)) => a.cmp(&b),
(&const_binary(ref a), &const_binary(ref b)) => a.cmp(b),
_ => return None
})
}
pub fn compare_lit_exprs(tcx: &ty::ctxt, a: &Expr, b: &Expr) -> Option<int> {
compare_const_vals(&eval_const_expr(tcx, a), &eval_const_expr(tcx, b))
pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>,
a: &Expr,
b: &Expr,
ty_hint: Option<Ty<'tcx>>)
-> Option<Ordering> {
let a = match eval_const_expr_partial(tcx, a, ty_hint) {
Ok(a) => a,
Err(s) => {
tcx.sess.span_err(a.span, &s[]);
return None;
}
};
let b = match eval_const_expr_partial(tcx, b, ty_hint) {
Ok(b) => b,
Err(s) => {
tcx.sess.span_err(b.span, &s[]);
return None;
}
};
compare_const_vals(&a, &b)
}

View File

@ -5350,26 +5350,25 @@ pub fn enum_variants<'tcx>(cx: &ctxt<'tcx>, id: ast::DefId)
None => INITIAL_DISCRIMINANT_VALUE
};
match variant.node.disr_expr {
Some(ref e) =>
match const_eval::eval_const_expr_partial(cx, &**e) {
Ok(const_eval::const_int(val)) => {
discriminant = val as Disr
}
Ok(const_eval::const_uint(val)) => {
discriminant = val as Disr
}
Ok(_) => {
span_err!(cx.sess, e.span, E0304,
"expected signed integer constant");
}
Err(ref err) => {
span_err!(cx.sess, e.span, E0305,
"expected constant: {}",
*err);
}
},
None => {}
if let Some(ref e) = variant.node.disr_expr {
// Preserve all values, and prefer signed.
let ty = Some(cx.types.i64);
match const_eval::eval_const_expr_partial(cx, &**e, ty) {
Ok(const_eval::const_int(val)) => {
discriminant = val as Disr;
}
Ok(const_eval::const_uint(val)) => {
discriminant = val as Disr;
}
Ok(_) => {
span_err!(cx.sess, e.span, E0304,
"expected signed integer constant");
}
Err(err) => {
span_err!(cx.sess, e.span, E0305,
"expected constant: {}", err);
}
}
};
last_discriminant = Some(discriminant);
@ -5822,7 +5821,7 @@ pub fn is_binopable<'tcx>(cx: &ctxt<'tcx>, ty: Ty<'tcx>, op: ast::BinOp) -> bool
// Returns the repeat count for a repeating vector expression.
pub fn eval_repeat_count(tcx: &ctxt, count_expr: &ast::Expr) -> uint {
match const_eval::eval_const_expr_partial(tcx, count_expr) {
match const_eval::eval_const_expr_partial(tcx, count_expr, Some(tcx.types.uint)) {
Ok(val) => {
let found = match val {
const_eval::const_uint(count) => return count as uint,

View File

@ -219,6 +219,7 @@ use util::nodemap::FnvHashMap;
use util::ppaux::{Repr, vec_map_to_string};
use std;
use std::cmp::Ordering;
use std::iter::AdditiveIterator;
use std::rc::Rc;
use syntax::ast;
@ -232,10 +233,8 @@ struct ConstantExpr<'a>(&'a ast::Expr);
impl<'a> ConstantExpr<'a> {
fn eq(self, other: ConstantExpr<'a>, tcx: &ty::ctxt) -> bool {
let ConstantExpr(expr) = self;
let ConstantExpr(other_expr) = other;
match const_eval::compare_lit_exprs(tcx, expr, other_expr) {
Some(val1) => val1 == 0,
match const_eval::compare_lit_exprs(tcx, self.0, other.0, None) {
Some(result) => result == Ordering::Equal,
None => panic!("compare_list_exprs: type mismatch"),
}
}

View File

@ -1183,7 +1183,7 @@ pub fn ast_ty_to_ty<'tcx>(
qpath_to_ty(this, rscope, ast_ty, &**qpath)
}
ast::TyFixedLengthVec(ref ty, ref e) => {
match const_eval::eval_const_expr_partial(tcx, &**e) {
match const_eval::eval_const_expr_partial(tcx, &**e, Some(tcx.types.uint)) {
Ok(ref r) => {
match *r {
const_eval::const_int(i) =>

View File

@ -8,6 +8,7 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use middle::const_eval;
use middle::def;
use middle::infer;
use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding, pat_is_const};
@ -15,12 +16,12 @@ use middle::subst::{Substs};
use middle::ty::{self, Ty};
use check::{check_expr, check_expr_has_type, check_expr_with_expectation};
use check::{check_expr_coercable_to_type, demand, FnCtxt, Expectation};
use check::{instantiate_path, structurally_resolved_type, valid_range_bounds};
use check::{instantiate_path, structurally_resolved_type};
use require_same_types;
use util::nodemap::FnvHashMap;
use util::ppaux::Repr;
use std::cmp;
use std::cmp::{self, Ordering};
use std::collections::hash_map::Entry::{Occupied, Vacant};
use syntax::ast;
use syntax::ast_util;
@ -79,16 +80,17 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
lhs_eq_rhs && (ty::type_is_numeric(lhs_ty) || ty::type_is_char(lhs_ty));
if numeric_or_char {
match valid_range_bounds(fcx.ccx, &**begin, &**end) {
Some(false) => {
match const_eval::compare_lit_exprs(tcx, &**begin, &**end, Some(lhs_ty)) {
Some(Ordering::Less) |
Some(Ordering::Equal) => {}
Some(Ordering::Greater) => {
span_err!(tcx.sess, begin.span, E0030,
"lower range bound must be less than upper");
},
}
None => {
span_err!(tcx.sess, begin.span, E0031,
"mismatched types in range");
},
Some(true) => {}
}
}
} else {
span_err!(tcx.sess, begin.span, E0029,

View File

@ -2499,16 +2499,6 @@ fn check_lit<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
}
}
pub fn valid_range_bounds(ccx: &CrateCtxt,
from: &ast::Expr,
to: &ast::Expr)
-> Option<bool> {
match const_eval::compare_lit_exprs(ccx.tcx, from, to) {
Some(val) => Some(val <= 0),
None => None
}
}
pub fn check_expr_has_type<'a, 'tcx>(fcx: &FnCtxt<'a, 'tcx>,
expr: &'tcx ast::Expr,
expected: Ty<'tcx>) {
@ -4550,7 +4540,7 @@ pub fn check_enum_variants<'a,'tcx>(ccx: &CrateCtxt<'a,'tcx>,
// that the expression is in a form that eval_const_expr can
// handle, so we may still get an internal compiler error
match const_eval::eval_const_expr_partial(ccx.tcx, &**e) {
match const_eval::eval_const_expr_partial(ccx.tcx, &**e, Some(declty)) {
Ok(const_eval::const_int(val)) => current_disr_val = val as Disr,
Ok(const_eval::const_uint(val)) => current_disr_val = val as Disr,
Ok(_) => {

View File

@ -1,15 +0,0 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// Trying to create a fixed-length vector with a negative size
fn main() {
let _x = [0; -1]; //~ ERROR found negative integer
}

View File

@ -41,8 +41,18 @@ fn main() {
//~| expected usize
//~| found &-ptr
//~| ERROR expected positive integer for repeat count, found string
let f = [0; -4];
//~^ ERROR expected positive integer for repeat count, found negative integer
let f = [0us; -1];
//~^ ERROR expected positive integer for repeat count, found negative integer
let f = [0; -4is];
//~^ ERROR mismatched types
//~| expected `usize`
//~| found `isize`
//~| expected usize
//~| found isize
//~| ERROR expected positive integer for repeat count, found negative integer
let f = [0us; -1is];
//~^ ERROR mismatched types
//~| expected `usize`
//~| found `isize`
//~| expected usize
//~| found isize
//~| ERROR expected positive integer for repeat count, found negative integer
}