From 6bdfb0534737b371e3b3e4abdec2f5dffd629916 Mon Sep 17 00:00:00 2001 From: Eli Friedman Date: Tue, 30 Jun 2015 08:53:50 -0700 Subject: [PATCH] Clarify the usage of "hints" in const_eval. The "hint" mechanism is essentially used as a workaround to compute types for expressions which have not yet been type-checked. This commit clarifies that usage, and limits the effects to the places where it is currently necessary. Fixes #26210. --- src/librustc/diagnostics.rs | 18 ++ src/librustc/middle/check_const.rs | 18 +- src/librustc/middle/check_match.rs | 3 +- src/librustc/middle/const_eval.rs | 171 ++++++++++++------ src/librustc/middle/ty.rs | 20 +- src/librustc_lint/builtin.rs | 3 +- src/librustc_trans/trans/_match.rs | 3 +- src/librustc_trans/trans/consts.rs | 4 +- src/librustc_typeck/astconv.rs | 4 +- src/librustc_typeck/check/_match.rs | 15 +- src/librustc_typeck/diagnostics.rs | 18 -- .../compile-fail/lint-exceeding-bitshifts.rs | 6 +- src/test/compile-fail/match-range-fail-2.rs | 23 +++ src/test/compile-fail/match-range-fail.rs | 6 - 14 files changed, 194 insertions(+), 118 deletions(-) create mode 100644 src/test/compile-fail/match-range-fail-2.rs diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 4b77c211df983..4e21efcf9eb6b 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -335,6 +335,24 @@ This error indicates that an attempt was made to divide by zero (or take the remainder of a zero divisor) in a static or constant expression. "##, +E0030: r##" +When matching against a range, the compiler verifies that the range is +non-empty. Range patterns include both end-points, so this is equivalent to +requiring the start of the range to be less than or equal to the end of the +range. + +For example: + +``` +match 5u32 { + // This range is ok, albeit pointless. + 1 ... 1 => ... + // This range is empty, and the compiler can tell. + 1000 ... 5 => ... +} +``` +"##, + E0079: r##" Enum variants which contain no data can be given a custom integer representation. This error indicates that the value provided is not an diff --git a/src/librustc/middle/check_const.rs b/src/librustc/middle/check_const.rs index 59f91a50f7421..baaf6b6a0401d 100644 --- a/src/librustc/middle/check_const.rs +++ b/src/librustc/middle/check_const.rs @@ -26,6 +26,7 @@ use middle::cast::{CastKind}; use middle::const_eval; +use middle::const_eval::EvalHint::ExprTypeChecked; use middle::def; use middle::expr_use_visitor as euv; use middle::infer; @@ -39,6 +40,7 @@ use syntax::codemap::Span; use syntax::visit::{self, Visitor}; use std::collections::hash_map::Entry; +use std::cmp::Ordering; // Const qualification, from partial to completely promotable. bitflags! { @@ -365,6 +367,19 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { ast::PatRange(ref start, ref end) => { self.global_expr(Mode::Const, &**start); self.global_expr(Mode::Const, &**end); + + match const_eval::compare_lit_exprs(self.tcx, start, end) { + Some(Ordering::Less) | + Some(Ordering::Equal) => {} + Some(Ordering::Greater) => { + span_err!(self.tcx.sess, start.span, E0030, + "lower range bound must be less than or equal to upper"); + } + None => { + self.tcx.sess.span_bug( + start.span, "literals of different types in range pat"); + } + } } _ => visit::walk_pat(self, p) } @@ -457,7 +472,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> { match node_ty.sty { ty::TyUint(_) | ty::TyInt(_) if div_or_rem => { if !self.qualif.intersects(ConstQualif::NOT_CONST) { - match const_eval::eval_const_expr_partial(self.tcx, ex, None) { + match const_eval::eval_const_expr_partial( + self.tcx, ex, ExprTypeChecked) { Ok(_) => {} Err(msg) => { span_err!(self.tcx.sess, msg.span, E0020, diff --git a/src/librustc/middle/check_match.rs b/src/librustc/middle/check_match.rs index fc2444ed5b45c..d8c2341df2d9f 100644 --- a/src/librustc/middle/check_match.rs +++ b/src/librustc/middle/check_match.rs @@ -15,6 +15,7 @@ use self::WitnessPreference::*; use middle::const_eval::{compare_const_vals, ConstVal}; use middle::const_eval::{eval_const_expr, eval_const_expr_partial}; use middle::const_eval::{const_expr_to_pat, lookup_const_by_id}; +use middle::const_eval::EvalHint::ExprTypeChecked; use middle::def::*; use middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Init}; use middle::expr_use_visitor::{JustWrite, LoanCause, MutateMode}; @@ -263,7 +264,7 @@ fn check_for_bindings_named_the_same_as_variants(cx: &MatchCheckCtxt, pat: &Pat) fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) { ast_util::walk_pat(pat, |p| { if let ast::PatLit(ref expr) = p.node { - match eval_const_expr_partial(cx.tcx, &**expr, None) { + match eval_const_expr_partial(cx.tcx, &**expr, ExprTypeChecked) { Ok(ConstVal::Float(f)) if f.is_nan() => { span_warn!(cx.tcx.sess, p.span, E0003, "unmatchable NaN in pattern, \ diff --git a/src/librustc/middle/const_eval.rs b/src/librustc/middle/const_eval.rs index 7d54b8c284f1f..77d42046f1954 100644 --- a/src/librustc/middle/const_eval.rs +++ b/src/librustc/middle/const_eval.rs @@ -12,8 +12,8 @@ #![allow(unsigned_negation)] use self::ConstVal::*; - use self::ErrKind::*; +use self::EvalHint::*; use ast_map; use ast_map::blocks::FnLikeNode; @@ -331,7 +331,7 @@ pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P } pub fn eval_const_expr(tcx: &ty::ctxt, e: &Expr) -> ConstVal { - match eval_const_expr_partial(tcx, e, None) { + match eval_const_expr_partial(tcx, e, ExprTypeChecked) { Ok(r) => r, Err(s) => tcx.sess.span_fatal(s.span, &s.description()) } @@ -436,6 +436,28 @@ impl ConstEvalErr { pub type EvalResult = Result; pub type CastResult = Result; +// FIXME: Long-term, this enum should go away: trying to evaluate +// an expression which hasn't been type-checked is a recipe for +// disaster. That said, it's not clear how to fix ast_ty_to_ty +// to avoid the ordering issue. + +/// Hint to determine how to evaluate constant expressions which +/// might not be type-checked. +#[derive(Copy, Clone, Debug)] +pub enum EvalHint<'tcx> { + /// We have a type-checked expression. + ExprTypeChecked, + /// We have an expression which hasn't been type-checked, but we have + /// an idea of what the type will be because of the context. For example, + /// the length of an array is always `usize`. (This is referred to as + /// a hint because it isn't guaranteed to be consistent with what + /// type-checking would compute.) + UncheckedExprHint(Ty<'tcx>), + /// We have an expression which has not yet been type-checked, and + /// and we have no clue what the type will be. + UncheckedExprNoHint, +} + #[derive(Copy, Clone, PartialEq, Debug)] pub enum IntTy { I8, I16, I32, I64 } #[derive(Copy, Clone, PartialEq, Debug)] @@ -706,26 +728,34 @@ pub_fn_checked_op!{ const_uint_checked_shr_via_int(a: u64, b: i64,.. UintTy) { uint_shift_body overflowing_shr Uint ShiftRightWithOverflow }} -// After type checking, `eval_const_expr_partial` should always suffice. The -// reason for providing `eval_const_expr_with_substs` is to allow -// trait-associated consts to be evaluated *during* type checking, when the -// substs for each expression have not been written into `tcx` yet. +/// Evaluate a constant expression in a context where the expression isn't +/// guaranteed to be evaluatable. `ty_hint` is usually ExprTypeChecked, +/// but a few places need to evaluate constants during type-checking, like +/// computing the length of an array. (See also the FIXME above EvalHint.) pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>, e: &Expr, - ty_hint: Option>) -> EvalResult { - eval_const_expr_with_substs(tcx, e, ty_hint, |id| { - tcx.node_id_item_substs(id).substs - }) -} - -pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>, - e: &Expr, - ty_hint: Option>, - get_substs: S) -> EvalResult - where S: Fn(ast::NodeId) -> subst::Substs<'tcx> { + ty_hint: EvalHint<'tcx>) -> EvalResult { fn fromb(b: bool) -> ConstVal { Int(b as i64) } - let ety = ty_hint.or_else(|| tcx.expr_ty_opt(e)); + // Try to compute the type of the expression based on the EvalHint. + // (See also the definition of EvalHint, and the FIXME above EvalHint.) + let ety = match ty_hint { + ExprTypeChecked => { + // After type-checking, expr_ty is guaranteed to succeed. + Some(tcx.expr_ty(e)) + } + UncheckedExprHint(ty) => { + // Use the type hint; it's not guaranteed to be right, but it's + // usually good enough. + Some(ty) + } + UncheckedExprNoHint => { + // This expression might not be type-checked, and we have no hint. + // Try to query the context for a type anyway; we might get lucky + // (for example, if the expression was imported from another crate). + tcx.expr_ty_opt(e) + } + }; // If type of expression itself is int or uint, normalize in these // bindings so that isize/usize is mapped to a type with an @@ -741,7 +771,7 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>, let result = match e.node { ast::ExprUnary(ast::UnNeg, ref inner) => { - match try!(eval_const_expr_partial(tcx, &**inner, ety)) { + match try!(eval_const_expr_partial(tcx, &**inner, ty_hint)) { Float(f) => Float(-f), Int(n) => try!(const_int_checked_neg(n, e, expr_int_type)), Uint(i) => { @@ -762,7 +792,7 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>, } } ast::ExprUnary(ast::UnNot, ref inner) => { - match try!(eval_const_expr_partial(tcx, &**inner, ety)) { + match try!(eval_const_expr_partial(tcx, &**inner, ty_hint)) { Int(i) => Int(!i), Uint(i) => const_uint_not(i, expr_uint_type), Bool(b) => Bool(!b), @@ -775,10 +805,16 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>, } ast::ExprBinary(op, ref a, ref b) => { let b_ty = match op.node { - ast::BiShl | ast::BiShr => Some(tcx.types.usize), - _ => ety + ast::BiShl | ast::BiShr => { + if let ExprTypeChecked = ty_hint { + ExprTypeChecked + } else { + UncheckedExprHint(tcx.types.usize) + } + } + _ => ty_hint }; - match (try!(eval_const_expr_partial(tcx, &**a, ety)), + match (try!(eval_const_expr_partial(tcx, &**a, ty_hint)), try!(eval_const_expr_partial(tcx, &**b, b_ty))) { (Float(a), Float(b)) => { match op.node { @@ -868,22 +904,25 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>, } } 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. - // - // FIXME (#23833): the type-hint can cause problems, - // e.g. `(i8::MAX + 1_i8) as u32` feeds in `u32` as result - // type to the sum, and thus no overflow is signaled. - let base_hint = tcx.expr_ty_opt(&**base).unwrap_or(ety); - let val = try!(eval_const_expr_partial(tcx, &**base, Some(base_hint))); + let base_hint = if let ExprTypeChecked = ty_hint { + ExprTypeChecked + } else { + // FIXME (#23833): the type-hint can cause problems, + // e.g. `(i8::MAX + 1_i8) as u32` feeds in `u32` as result + // type to the sum, and thus no overflow is signaled. + match tcx.expr_ty_opt(&base) { + Some(t) => UncheckedExprHint(t), + None => ty_hint + } + }; + + let val = try!(eval_const_expr_partial(tcx, &**base, base_hint)); match cast_const(tcx, val, ety) { Ok(val) => val, Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }), @@ -913,12 +952,16 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>, def::FromTrait(trait_id) => match tcx.map.find(def_id.node) { Some(ast_map::NodeTraitItem(ti)) => match ti.node { ast::ConstTraitItem(ref ty, _) => { - let substs = get_substs(e.id); - (resolve_trait_associated_const(tcx, - ti, - trait_id, - substs), - Some(&**ty)) + if let ExprTypeChecked = ty_hint { + let substs = tcx.node_id_item_substs(e.id).substs; + (resolve_trait_associated_const(tcx, + ti, + trait_id, + substs), + Some(&**ty)) + } else { + (None, None) + } } _ => (None, None) }, @@ -947,27 +990,42 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>, Some(actual_e) => actual_e, None => signal!(e, NonConstPath) }; - let ety = ety.or_else(|| const_ty.and_then(|ty| ast_ty_to_prim_ty(tcx, ty))); - try!(eval_const_expr_partial(tcx, const_expr, ety)) + let item_hint = if let UncheckedExprNoHint = ty_hint { + match const_ty { + Some(ty) => match ast_ty_to_prim_ty(tcx, ty) { + Some(ty) => UncheckedExprHint(ty), + None => UncheckedExprNoHint + }, + None => UncheckedExprNoHint + } + } else { + ty_hint + }; + try!(eval_const_expr_partial(tcx, const_expr, item_hint)) } ast::ExprLit(ref lit) => { lit_to_const(&**lit, ety) } - ast::ExprParen(ref e) => try!(eval_const_expr_partial(tcx, &**e, ety)), + ast::ExprParen(ref e) => try!(eval_const_expr_partial(tcx, &**e, ty_hint)), ast::ExprBlock(ref block) => { match block.expr { - Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ety)), + Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ty_hint)), None => Int(0) } } ast::ExprTup(_) => Tuple(e.id), ast::ExprStruct(..) => Struct(e.id), ast::ExprTupField(ref base, index) => { - if let Ok(c) = eval_const_expr_partial(tcx, base, None) { + let base_hint = if let ExprTypeChecked = ty_hint { + ExprTypeChecked + } else { + UncheckedExprNoHint + }; + if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint) { if let Tuple(tup_id) = c { if let ast::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node { if index.node < fields.len() { - return eval_const_expr_partial(tcx, &fields[index.node], None) + return eval_const_expr_partial(tcx, &fields[index.node], base_hint) } else { signal!(e, TupleIndexOutOfBounds); } @@ -983,13 +1041,18 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>, } ast::ExprField(ref base, field_name) => { // Get the base expression if it is a struct and it is constant - if let Ok(c) = eval_const_expr_partial(tcx, base, None) { + let base_hint = if let ExprTypeChecked = ty_hint { + ExprTypeChecked + } else { + UncheckedExprNoHint + }; + if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint) { if let Struct(struct_id) = c { if let ast::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node { // 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, None) + return eval_const_expr_partial(tcx, &*f.expr, base_hint) } else { signal!(e, MissingStructField); } @@ -1165,21 +1228,17 @@ pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option { }) } -pub fn compare_lit_exprs<'tcx, S>(tcx: &ty::ctxt<'tcx>, - a: &Expr, - b: &Expr, - ty_hint: Option>, - get_substs: S) -> Option - where S: Fn(ast::NodeId) -> subst::Substs<'tcx> { - let a = match eval_const_expr_with_substs(tcx, a, ty_hint, - |id| {get_substs(id)}) { +pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>, + a: &Expr, + b: &Expr) -> Option { + let a = match eval_const_expr_partial(tcx, a, ExprTypeChecked) { Ok(a) => a, Err(e) => { tcx.sess.span_err(a.span, &e.description()); return None; } }; - let b = match eval_const_expr_with_substs(tcx, b, ty_hint, get_substs) { + let b = match eval_const_expr_partial(tcx, b, ExprTypeChecked) { Ok(b) => b, Err(e) => { tcx.sess.span_err(b.span, &e.description()); diff --git a/src/librustc/middle/ty.rs b/src/librustc/middle/ty.rs index ef337b4163051..ef4dff68a38c4 100644 --- a/src/librustc/middle/ty.rs +++ b/src/librustc/middle/ty.rs @@ -45,6 +45,7 @@ use middle; use middle::cast; use middle::check_const; use middle::const_eval::{self, ConstVal}; +use middle::const_eval::EvalHint::UncheckedExprHint; use middle::def::{self, DefMap, ExportMap}; use middle::dependency_format; use middle::fast_reject; @@ -5758,20 +5759,8 @@ impl<'tcx> ctxt<'tcx> { Some(ref e) => { debug!("disr expr, checking {}", pprust::expr_to_string(&**e)); - // check_expr (from check_const pass) doesn't guarantee - // that the expression is in a form that eval_const_expr can - // handle, so we may still get an internal compiler error - // - // pnkfelix: The above comment was transcribed from - // the version of this code taken from rustc_typeck. - // Presumably the implication is that we need to deal - // with such ICE's as they arise. - // - // Since this can be called from `ty::enum_variants` - // anyway, best thing is to make `eval_const_expr` - // more robust (on case-by-case basis). - - match const_eval::eval_const_expr_partial(self, &**e, Some(repr_type_ty)) { + let hint = UncheckedExprHint(repr_type_ty); + match const_eval::eval_const_expr_partial(self, &**e, hint) { Ok(ConstVal::Int(val)) => current_disr_val = val as Disr, Ok(ConstVal::Uint(val)) => current_disr_val = val as Disr, Ok(_) => { @@ -6086,7 +6075,8 @@ impl<'tcx> ctxt<'tcx> { // Returns the repeat count for a repeating vector expression. pub fn eval_repeat_count(&self, count_expr: &ast::Expr) -> usize { - match const_eval::eval_const_expr_partial(self, count_expr, Some(self.types.usize)) { + let hint = UncheckedExprHint(self.types.usize); + match const_eval::eval_const_expr_partial(self, count_expr, hint) { Ok(val) => { let found = match val { ConstVal::Uint(count) => return count as usize, diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index 1574080b313b5..9529025b60ff5 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -39,6 +39,7 @@ use middle::ty::{self, Ty}; use middle::traits; use middle::{def, pat_util, stability}; use middle::const_eval::{eval_const_expr_partial, ConstVal}; +use middle::const_eval::EvalHint::ExprTypeChecked; use middle::cfg; use rustc::ast_map; use util::nodemap::{FnvHashMap, NodeSet}; @@ -184,7 +185,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, Some(cx.tcx.types.usize)) { + match eval_const_expr_partial(cx.tcx, &**r, ExprTypeChecked) { Ok(ConstVal::Int(shift)) => { shift as u64 >= bits }, Ok(ConstVal::Uint(shift)) => { shift >= bits }, _ => { false } diff --git a/src/librustc_trans/trans/_match.rs b/src/librustc_trans/trans/_match.rs index 9a9b9c617a853..925da81d77e6b 100644 --- a/src/librustc_trans/trans/_match.rs +++ b/src/librustc_trans/trans/_match.rs @@ -235,8 +235,7 @@ struct ConstantExpr<'a>(&'a ast::Expr); impl<'a> ConstantExpr<'a> { fn eq(self, other: ConstantExpr<'a>, tcx: &ty::ctxt) -> bool { - match const_eval::compare_lit_exprs(tcx, self.0, other.0, None, - |id| {tcx.node_id_item_substs(id).substs}) { + match const_eval::compare_lit_exprs(tcx, self.0, other.0) { Some(result) => result == Ordering::Equal, None => panic!("compare_list_exprs: type mismatch"), } diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index 242eceb8335b2..302ef68bddc7d 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -23,6 +23,8 @@ use middle::const_eval::{const_int_checked_div, const_uint_checked_div}; use middle::const_eval::{const_int_checked_rem, const_uint_checked_rem}; use middle::const_eval::{const_int_checked_shl, const_uint_checked_shl}; use middle::const_eval::{const_int_checked_shr, const_uint_checked_shr}; +use middle::const_eval::EvalHint::ExprTypeChecked; +use middle::const_eval::eval_const_expr_partial; use trans::{adt, closure, debuginfo, expr, inline, machine}; use trans::base::{self, push_ctxt}; use trans::common::*; @@ -591,7 +593,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, ast::ExprIndex(ref base, ref index) => { let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args); - let iv = match const_eval::eval_const_expr_partial(cx.tcx(), &**index, None) { + let iv = match eval_const_expr_partial(cx.tcx(), &index, ExprTypeChecked) { Ok(ConstVal::Int(i)) => i as u64, Ok(ConstVal::Uint(u)) => u, _ => cx.sess().span_bug(index.span, diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv.rs index 3bb3a63004153..0343b14accff9 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv.rs @@ -50,6 +50,7 @@ use middle::astconv_util::{prim_ty_to_ty, check_path_args, NO_TPS, NO_REGIONS}; use middle::const_eval::{self, ConstVal}; +use middle::const_eval::EvalHint::UncheckedExprHint; use middle::def; use middle::implicator::object_region_bounds; use middle::resolve_lifetime as rl; @@ -1627,7 +1628,8 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>, ty } ast::TyFixedLengthVec(ref ty, ref e) => { - match const_eval::eval_const_expr_partial(tcx, &**e, Some(tcx.types.usize)) { + let hint = UncheckedExprHint(tcx.types.usize); + match const_eval::eval_const_expr_partial(tcx, &e, hint) { Ok(r) => { match r { ConstVal::Int(i) => diff --git a/src/librustc_typeck/check/_match.rs b/src/librustc_typeck/check/_match.rs index a995401cf5c81..c4b31d578dbfd 100644 --- a/src/librustc_typeck/check/_match.rs +++ b/src/librustc_typeck/check/_match.rs @@ -8,7 +8,6 @@ // 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}; @@ -23,7 +22,7 @@ use check::{instantiate_path, resolve_ty_and_def_ufcs, structurally_resolved_typ use require_same_types; use util::nodemap::FnvHashMap; -use std::cmp::{self, Ordering}; +use std::cmp; use std::collections::hash_map::Entry::{Occupied, Vacant}; use syntax::ast; use syntax::ast_util; @@ -130,18 +129,6 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>, fcx.write_ty(pat.id, common_type); - // Finally we evaluate the constants and check that the range is non-empty. - let get_substs = |id| fcx.item_substs()[&id].substs.clone(); - match const_eval::compare_lit_exprs(tcx, begin, end, Some(&common_type), get_substs) { - Some(Ordering::Less) | - Some(Ordering::Equal) => {} - Some(Ordering::Greater) => { - span_err!(tcx.sess, begin.span, E0030, - "lower range bound must be less than or equal to upper"); - } - None => tcx.sess.span_bug(begin.span, "literals of different types in range pat") - } - // subtyping doesn't matter here, as the value is some kind of scalar demand::eqtype(fcx, pat.span, expected, lhs_ty); } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index ed04fde463c92..6263adf200ff5 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -169,24 +169,6 @@ match string { ``` "##, -E0030: r##" -When matching against a range, the compiler verifies that the range is -non-empty. Range patterns include both end-points, so this is equivalent to -requiring the start of the range to be less than or equal to the end of the -range. - -For example: - -``` -match 5u32 { - // This range is ok, albeit pointless. - 1 ... 1 => ... - // This range is empty, and the compiler can tell. - 1000 ... 5 => ... -} -``` -"##, - E0033: r##" This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a pattern. Every trait defines a type, but because the diff --git a/src/test/compile-fail/lint-exceeding-bitshifts.rs b/src/test/compile-fail/lint-exceeding-bitshifts.rs index 5867bc2f09deb..160551b81cb2e 100644 --- a/src/test/compile-fail/lint-exceeding-bitshifts.rs +++ b/src/test/compile-fail/lint-exceeding-bitshifts.rs @@ -8,11 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(negate_unsigned)] #![deny(exceeding_bitshifts)] #![allow(unused_variables)] #![allow(dead_code)] -#![feature(num_bits_bytes, negate_unsigned)] +#![feature(num_bits_bytes)] fn main() { let n = 1u8 << 7; @@ -60,4 +59,7 @@ fn main() { let n = 1_isize << std::isize::BITS; //~ ERROR: bitshift exceeds the type's number of bits let n = 1_usize << std::usize::BITS; //~ ERROR: bitshift exceeds the type's number of bits + + + let n = 1i8<<(1isize+-1); } diff --git a/src/test/compile-fail/match-range-fail-2.rs b/src/test/compile-fail/match-range-fail-2.rs new file mode 100644 index 0000000000000..e30f783ce3637 --- /dev/null +++ b/src/test/compile-fail/match-range-fail-2.rs @@ -0,0 +1,23 @@ +// Copyright 2015 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. + +fn main() { + match 5 { + 6 ... 1 => { } + _ => { } + }; + //~^^^ ERROR lower range bound must be less than or equal to upper + + match 5u64 { + 0xFFFF_FFFF_FFFF_FFFF ... 1 => { } + _ => { } + }; + //~^^^ ERROR lower range bound must be less than or equal to upper +} diff --git a/src/test/compile-fail/match-range-fail.rs b/src/test/compile-fail/match-range-fail.rs index 234b74f76d1e0..05b870b8f41cc 100644 --- a/src/test/compile-fail/match-range-fail.rs +++ b/src/test/compile-fail/match-range-fail.rs @@ -9,12 +9,6 @@ // except according to those terms. fn main() { - match 5 { - 6 ... 1 => { } - _ => { } - }; - //~^^^ ERROR lower range bound must be less than or equal to upper - match "wow" { "bar" ... "foo" => { } };