From cb3c9a1e885964309445e097cab64fe302e144e1 Mon Sep 17 00:00:00 2001 From: Eduard Burtescu Date: Thu, 29 Jan 2015 13:40:14 +0200 Subject: [PATCH] rustc: teach const_eval more about types. --- src/librustc/lint/builtin.rs | 2 +- src/librustc/middle/check_match.rs | 5 +- src/librustc/middle/const_eval.rs | 217 +++++++++++++++++--------- src/librustc/middle/ty.rs | 41 +++-- src/librustc_trans/trans/_match.rs | 7 +- src/librustc_typeck/astconv.rs | 2 +- src/librustc_typeck/check/_match.rs | 16 +- src/librustc_typeck/check/mod.rs | 12 +- src/test/compile-fail/issue-6977.rs | 15 -- src/test/compile-fail/repeat_count.rs | 18 ++- 10 files changed, 195 insertions(+), 140 deletions(-) delete mode 100644 src/test/compile-fail/issue-6977.rs diff --git a/src/librustc/lint/builtin.rs b/src/librustc/lint/builtin.rs index fe1d695ab7b..08db053d3f4 100644 --- a/src/librustc/lint/builtin.rs +++ b/src/librustc/lint/builtin.rs @@ -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 } diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index 7ac690f02e1..03456f85290 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -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 } } diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 04c054db0b7..72600347334 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -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 } 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 { +pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, + e: &Expr, + ty_hint: Option>) + -> Result { fn fromb(b: bool) -> Result { 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 { - 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 { - 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 ( - $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 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 Result const_val { +fn cast_const(val: const_val, ty: Ty) -> Result { + 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) -> 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(a: T, b: T) -> Option { - Some(if a == b { 0 } else if a < b { -1 } else { 1 }) -} -pub fn compare_const_vals(a: &const_val, b: &const_val) -> Option { - 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 { + 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 { - 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>) + -> Option { + 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) } diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index e276683236f..6e14f7b09f8 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -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, diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index 0f014800480..6a296674f3f 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -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"), } } diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 1951d9946bc..68054c79f27 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -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) => diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index 3ea2743c63e..81868f3695c 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -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, diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index d12b23187b8..27b17c9fc97 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -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 { - 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(_) => { diff --git a/src/test/compile-fail/issue-6977.rs b/src/test/compile-fail/issue-6977.rs deleted file mode 100644 index c2bd810abad..00000000000 --- a/src/test/compile-fail/issue-6977.rs +++ /dev/null @@ -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 or the MIT license -// , 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 -} diff --git a/src/test/compile-fail/repeat_count.rs b/src/test/compile-fail/repeat_count.rs index df69e13bf1e..d730add00b7 100644 --- a/src/test/compile-fail/repeat_count.rs +++ b/src/test/compile-fail/repeat_count.rs @@ -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 }