Auto merge of #26683 - eefriedman:const-eval-hint, r=pnkfelix
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.
This commit is contained in:
@@ -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.
|
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##"
|
E0079: r##"
|
||||||
Enum variants which contain no data can be given a custom integer
|
Enum variants which contain no data can be given a custom integer
|
||||||
representation. This error indicates that the value provided is not an
|
representation. This error indicates that the value provided is not an
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
|
|
||||||
use middle::cast::{CastKind};
|
use middle::cast::{CastKind};
|
||||||
use middle::const_eval;
|
use middle::const_eval;
|
||||||
|
use middle::const_eval::EvalHint::ExprTypeChecked;
|
||||||
use middle::def;
|
use middle::def;
|
||||||
use middle::expr_use_visitor as euv;
|
use middle::expr_use_visitor as euv;
|
||||||
use middle::infer;
|
use middle::infer;
|
||||||
@@ -39,6 +40,7 @@ use syntax::codemap::Span;
|
|||||||
use syntax::visit::{self, Visitor};
|
use syntax::visit::{self, Visitor};
|
||||||
|
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
// Const qualification, from partial to completely promotable.
|
// Const qualification, from partial to completely promotable.
|
||||||
bitflags! {
|
bitflags! {
|
||||||
@@ -365,6 +367,19 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
|||||||
ast::PatRange(ref start, ref end) => {
|
ast::PatRange(ref start, ref end) => {
|
||||||
self.global_expr(Mode::Const, &**start);
|
self.global_expr(Mode::Const, &**start);
|
||||||
self.global_expr(Mode::Const, &**end);
|
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)
|
_ => visit::walk_pat(self, p)
|
||||||
}
|
}
|
||||||
@@ -457,7 +472,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
|
|||||||
match node_ty.sty {
|
match node_ty.sty {
|
||||||
ty::TyUint(_) | ty::TyInt(_) if div_or_rem => {
|
ty::TyUint(_) | ty::TyInt(_) if div_or_rem => {
|
||||||
if !self.qualif.intersects(ConstQualif::NOT_CONST) {
|
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(_) => {}
|
Ok(_) => {}
|
||||||
Err(msg) => {
|
Err(msg) => {
|
||||||
span_err!(self.tcx.sess, msg.span, E0020,
|
span_err!(self.tcx.sess, msg.span, E0020,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ use self::WitnessPreference::*;
|
|||||||
use middle::const_eval::{compare_const_vals, ConstVal};
|
use middle::const_eval::{compare_const_vals, ConstVal};
|
||||||
use middle::const_eval::{eval_const_expr, eval_const_expr_partial};
|
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::{const_expr_to_pat, lookup_const_by_id};
|
||||||
|
use middle::const_eval::EvalHint::ExprTypeChecked;
|
||||||
use middle::def::*;
|
use middle::def::*;
|
||||||
use middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Init};
|
use middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Init};
|
||||||
use middle::expr_use_visitor::{JustWrite, LoanCause, MutateMode};
|
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) {
|
fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) {
|
||||||
ast_util::walk_pat(pat, |p| {
|
ast_util::walk_pat(pat, |p| {
|
||||||
if let ast::PatLit(ref expr) = p.node {
|
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() => {
|
Ok(ConstVal::Float(f)) if f.is_nan() => {
|
||||||
span_warn!(cx.tcx.sess, p.span, E0003,
|
span_warn!(cx.tcx.sess, p.span, E0003,
|
||||||
"unmatchable NaN in pattern, \
|
"unmatchable NaN in pattern, \
|
||||||
|
|||||||
@@ -11,8 +11,8 @@
|
|||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
|
|
||||||
use self::ConstVal::*;
|
use self::ConstVal::*;
|
||||||
|
|
||||||
use self::ErrKind::*;
|
use self::ErrKind::*;
|
||||||
|
use self::EvalHint::*;
|
||||||
|
|
||||||
use ast_map;
|
use ast_map;
|
||||||
use ast_map::blocks::FnLikeNode;
|
use ast_map::blocks::FnLikeNode;
|
||||||
@@ -345,7 +345,7 @@ 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) -> ConstVal {
|
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,
|
Ok(r) => r,
|
||||||
Err(s) => tcx.sess.span_fatal(s.span, &s.description())
|
Err(s) => tcx.sess.span_fatal(s.span, &s.description())
|
||||||
}
|
}
|
||||||
@@ -434,6 +434,28 @@ impl ConstEvalErr {
|
|||||||
pub type EvalResult = Result<ConstVal, ConstEvalErr>;
|
pub type EvalResult = Result<ConstVal, ConstEvalErr>;
|
||||||
pub type CastResult = Result<ConstVal, ErrKind>;
|
pub type CastResult = Result<ConstVal, ErrKind>;
|
||||||
|
|
||||||
|
// 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)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
pub enum IntTy { I8, I16, I32, I64 }
|
pub enum IntTy { I8, I16, I32, I64 }
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
@@ -704,26 +726,34 @@ pub_fn_checked_op!{ const_uint_checked_shr_via_int(a: u64, b: i64,.. UintTy) {
|
|||||||
uint_shift_body overflowing_shr Uint ShiftRightWithOverflow
|
uint_shift_body overflowing_shr Uint ShiftRightWithOverflow
|
||||||
}}
|
}}
|
||||||
|
|
||||||
// After type checking, `eval_const_expr_partial` should always suffice. The
|
/// Evaluate a constant expression in a context where the expression isn't
|
||||||
// reason for providing `eval_const_expr_with_substs` is to allow
|
/// guaranteed to be evaluatable. `ty_hint` is usually ExprTypeChecked,
|
||||||
// trait-associated consts to be evaluated *during* type checking, when the
|
/// but a few places need to evaluate constants during type-checking, like
|
||||||
// substs for each expression have not been written into `tcx` yet.
|
/// computing the length of an array. (See also the FIXME above EvalHint.)
|
||||||
pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||||
e: &Expr,
|
e: &Expr,
|
||||||
ty_hint: Option<Ty<'tcx>>) -> EvalResult {
|
ty_hint: EvalHint<'tcx>) -> 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<Ty<'tcx>>,
|
|
||||||
get_substs: S) -> EvalResult
|
|
||||||
where S: Fn(ast::NodeId) -> subst::Substs<'tcx> {
|
|
||||||
fn fromb(b: bool) -> ConstVal { Int(b as i64) }
|
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
|
// If type of expression itself is int or uint, normalize in these
|
||||||
// bindings so that isize/usize is mapped to a type with an
|
// bindings so that isize/usize is mapped to a type with an
|
||||||
@@ -739,7 +769,7 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
|
|||||||
|
|
||||||
let result = match e.node {
|
let result = match e.node {
|
||||||
ast::ExprUnary(ast::UnNeg, ref inner) => {
|
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),
|
Float(f) => Float(-f),
|
||||||
Int(n) => try!(const_int_checked_neg(n, e, expr_int_type)),
|
Int(n) => try!(const_int_checked_neg(n, e, expr_int_type)),
|
||||||
Uint(i) => {
|
Uint(i) => {
|
||||||
@@ -749,7 +779,7 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ExprUnary(ast::UnNot, ref inner) => {
|
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),
|
Int(i) => Int(!i),
|
||||||
Uint(i) => const_uint_not(i, expr_uint_type),
|
Uint(i) => const_uint_not(i, expr_uint_type),
|
||||||
Bool(b) => Bool(!b),
|
Bool(b) => Bool(!b),
|
||||||
@@ -758,10 +788,16 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
|
|||||||
}
|
}
|
||||||
ast::ExprBinary(op, ref a, ref b) => {
|
ast::ExprBinary(op, ref a, ref b) => {
|
||||||
let b_ty = match op.node {
|
let b_ty = match op.node {
|
||||||
ast::BiShl | ast::BiShr => Some(tcx.types.usize),
|
ast::BiShl | ast::BiShr => {
|
||||||
_ => ety
|
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))) {
|
try!(eval_const_expr_partial(tcx, &**b, b_ty))) {
|
||||||
(Float(a), Float(b)) => {
|
(Float(a), Float(b)) => {
|
||||||
match op.node {
|
match op.node {
|
||||||
@@ -851,22 +887,25 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ExprCast(ref base, ref target_ty) => {
|
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))
|
let ety = ety.or_else(|| ast_ty_to_prim_ty(tcx, &**target_ty))
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
tcx.sess.span_fatal(target_ty.span,
|
tcx.sess.span_fatal(target_ty.span,
|
||||||
"target type not found for const cast")
|
"target type not found for const cast")
|
||||||
});
|
});
|
||||||
|
|
||||||
// Prefer known type to noop, but always have a type hint.
|
let base_hint = if let ExprTypeChecked = ty_hint {
|
||||||
//
|
ExprTypeChecked
|
||||||
// FIXME (#23833): the type-hint can cause problems,
|
} else {
|
||||||
// e.g. `(i8::MAX + 1_i8) as u32` feeds in `u32` as result
|
// FIXME (#23833): the type-hint can cause problems,
|
||||||
// type to the sum, and thus no overflow is signaled.
|
// e.g. `(i8::MAX + 1_i8) as u32` feeds in `u32` as result
|
||||||
let base_hint = tcx.expr_ty_opt(&**base).unwrap_or(ety);
|
// type to the sum, and thus no overflow is signaled.
|
||||||
let val = try!(eval_const_expr_partial(tcx, &**base, Some(base_hint)));
|
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) {
|
match cast_const(tcx, val, ety) {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }),
|
Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }),
|
||||||
@@ -896,12 +935,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) {
|
def::FromTrait(trait_id) => match tcx.map.find(def_id.node) {
|
||||||
Some(ast_map::NodeTraitItem(ti)) => match ti.node {
|
Some(ast_map::NodeTraitItem(ti)) => match ti.node {
|
||||||
ast::ConstTraitItem(ref ty, _) => {
|
ast::ConstTraitItem(ref ty, _) => {
|
||||||
let substs = get_substs(e.id);
|
if let ExprTypeChecked = ty_hint {
|
||||||
(resolve_trait_associated_const(tcx,
|
let substs = tcx.node_id_item_substs(e.id).substs;
|
||||||
ti,
|
(resolve_trait_associated_const(tcx,
|
||||||
trait_id,
|
ti,
|
||||||
substs),
|
trait_id,
|
||||||
Some(&**ty))
|
substs),
|
||||||
|
Some(&**ty))
|
||||||
|
} else {
|
||||||
|
(None, None)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
_ => (None, None)
|
_ => (None, None)
|
||||||
},
|
},
|
||||||
@@ -930,27 +973,42 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
|
|||||||
Some(actual_e) => actual_e,
|
Some(actual_e) => actual_e,
|
||||||
None => signal!(e, NonConstPath)
|
None => signal!(e, NonConstPath)
|
||||||
};
|
};
|
||||||
let ety = ety.or_else(|| const_ty.and_then(|ty| ast_ty_to_prim_ty(tcx, ty)));
|
let item_hint = if let UncheckedExprNoHint = ty_hint {
|
||||||
try!(eval_const_expr_partial(tcx, const_expr, ety))
|
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) => {
|
ast::ExprLit(ref lit) => {
|
||||||
lit_to_const(&**lit, ety)
|
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) => {
|
ast::ExprBlock(ref block) => {
|
||||||
match block.expr {
|
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)
|
None => Int(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::ExprTup(_) => Tuple(e.id),
|
ast::ExprTup(_) => Tuple(e.id),
|
||||||
ast::ExprStruct(..) => Struct(e.id),
|
ast::ExprStruct(..) => Struct(e.id),
|
||||||
ast::ExprTupField(ref base, index) => {
|
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 Tuple(tup_id) = c {
|
||||||
if let ast::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node {
|
if let ast::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node {
|
||||||
if index.node < fields.len() {
|
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 {
|
} else {
|
||||||
signal!(e, TupleIndexOutOfBounds);
|
signal!(e, TupleIndexOutOfBounds);
|
||||||
}
|
}
|
||||||
@@ -966,13 +1024,18 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
|
|||||||
}
|
}
|
||||||
ast::ExprField(ref base, field_name) => {
|
ast::ExprField(ref base, field_name) => {
|
||||||
// Get the base expression if it is a struct and it is constant
|
// 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 Struct(struct_id) = c {
|
||||||
if let ast::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node {
|
if let ast::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node {
|
||||||
// Check that the given field exists and evaluate it
|
// Check that the given field exists and evaluate it
|
||||||
if let Some(f) = fields.iter().find(|f| f.ident.node.as_str()
|
if let Some(f) = fields.iter().find(|f| f.ident.node.as_str()
|
||||||
== field_name.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 {
|
} else {
|
||||||
signal!(e, MissingStructField);
|
signal!(e, MissingStructField);
|
||||||
}
|
}
|
||||||
@@ -1148,21 +1211,17 @@ pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option<Ordering> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compare_lit_exprs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
|
pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||||
a: &Expr,
|
a: &Expr,
|
||||||
b: &Expr,
|
b: &Expr) -> Option<Ordering> {
|
||||||
ty_hint: Option<Ty<'tcx>>,
|
let a = match eval_const_expr_partial(tcx, a, ExprTypeChecked) {
|
||||||
get_substs: S) -> Option<Ordering>
|
|
||||||
where S: Fn(ast::NodeId) -> subst::Substs<'tcx> {
|
|
||||||
let a = match eval_const_expr_with_substs(tcx, a, ty_hint,
|
|
||||||
|id| {get_substs(id)}) {
|
|
||||||
Ok(a) => a,
|
Ok(a) => a,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tcx.sess.span_err(a.span, &e.description());
|
tcx.sess.span_err(a.span, &e.description());
|
||||||
return None;
|
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,
|
Ok(b) => b,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
tcx.sess.span_err(b.span, &e.description());
|
tcx.sess.span_err(b.span, &e.description());
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ use middle;
|
|||||||
use middle::cast;
|
use middle::cast;
|
||||||
use middle::check_const;
|
use middle::check_const;
|
||||||
use middle::const_eval::{self, ConstVal};
|
use middle::const_eval::{self, ConstVal};
|
||||||
|
use middle::const_eval::EvalHint::UncheckedExprHint;
|
||||||
use middle::def::{self, DefMap, ExportMap};
|
use middle::def::{self, DefMap, ExportMap};
|
||||||
use middle::dependency_format;
|
use middle::dependency_format;
|
||||||
use middle::fast_reject;
|
use middle::fast_reject;
|
||||||
@@ -5758,20 +5759,8 @@ impl<'tcx> ctxt<'tcx> {
|
|||||||
Some(ref e) => {
|
Some(ref e) => {
|
||||||
debug!("disr expr, checking {}", pprust::expr_to_string(&**e));
|
debug!("disr expr, checking {}", pprust::expr_to_string(&**e));
|
||||||
|
|
||||||
// check_expr (from check_const pass) doesn't guarantee
|
let hint = UncheckedExprHint(repr_type_ty);
|
||||||
// that the expression is in a form that eval_const_expr can
|
match const_eval::eval_const_expr_partial(self, &**e, hint) {
|
||||||
// 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)) {
|
|
||||||
Ok(ConstVal::Int(val)) => current_disr_val = val as Disr,
|
Ok(ConstVal::Int(val)) => current_disr_val = val as Disr,
|
||||||
Ok(ConstVal::Uint(val)) => current_disr_val = val as Disr,
|
Ok(ConstVal::Uint(val)) => current_disr_val = val as Disr,
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
@@ -6086,7 +6075,8 @@ impl<'tcx> ctxt<'tcx> {
|
|||||||
|
|
||||||
// Returns the repeat count for a repeating vector expression.
|
// Returns the repeat count for a repeating vector expression.
|
||||||
pub fn eval_repeat_count(&self, count_expr: &ast::Expr) -> usize {
|
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) => {
|
Ok(val) => {
|
||||||
let found = match val {
|
let found = match val {
|
||||||
ConstVal::Uint(count) => return count as usize,
|
ConstVal::Uint(count) => return count as usize,
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ use middle::ty::{self, Ty};
|
|||||||
use middle::traits;
|
use middle::traits;
|
||||||
use middle::{def, pat_util, stability};
|
use middle::{def, pat_util, stability};
|
||||||
use middle::const_eval::{eval_const_expr_partial, ConstVal};
|
use middle::const_eval::{eval_const_expr_partial, ConstVal};
|
||||||
|
use middle::const_eval::EvalHint::ExprTypeChecked;
|
||||||
use middle::cfg;
|
use middle::cfg;
|
||||||
use rustc::ast_map;
|
use rustc::ast_map;
|
||||||
use util::nodemap::{FnvHashMap, NodeSet};
|
use util::nodemap::{FnvHashMap, NodeSet};
|
||||||
@@ -178,7 +179,7 @@ impl LintPass for TypeLimits {
|
|||||||
if let ast::LitInt(shift, _) = lit.node { shift >= bits }
|
if let ast::LitInt(shift, _) = lit.node { shift >= bits }
|
||||||
else { false }
|
else { false }
|
||||||
} else {
|
} 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::Int(shift)) => { shift as u64 >= bits },
|
||||||
Ok(ConstVal::Uint(shift)) => { shift >= bits },
|
Ok(ConstVal::Uint(shift)) => { shift >= bits },
|
||||||
_ => { false }
|
_ => { false }
|
||||||
|
|||||||
@@ -235,8 +235,7 @@ struct ConstantExpr<'a>(&'a ast::Expr);
|
|||||||
|
|
||||||
impl<'a> ConstantExpr<'a> {
|
impl<'a> ConstantExpr<'a> {
|
||||||
fn eq(self, other: ConstantExpr<'a>, tcx: &ty::ctxt) -> bool {
|
fn eq(self, other: ConstantExpr<'a>, tcx: &ty::ctxt) -> bool {
|
||||||
match const_eval::compare_lit_exprs(tcx, self.0, other.0, None,
|
match const_eval::compare_lit_exprs(tcx, self.0, other.0) {
|
||||||
|id| {tcx.node_id_item_substs(id).substs}) {
|
|
||||||
Some(result) => result == Ordering::Equal,
|
Some(result) => result == Ordering::Equal,
|
||||||
None => panic!("compare_list_exprs: type mismatch"),
|
None => panic!("compare_list_exprs: type mismatch"),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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_rem, const_uint_checked_rem};
|
||||||
use middle::const_eval::{const_int_checked_shl, const_uint_checked_shl};
|
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::{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::{adt, closure, debuginfo, expr, inline, machine};
|
||||||
use trans::base::{self, push_ctxt};
|
use trans::base::{self, push_ctxt};
|
||||||
use trans::common::*;
|
use trans::common::*;
|
||||||
@@ -591,7 +593,7 @@ fn const_expr_unadjusted<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
|||||||
|
|
||||||
ast::ExprIndex(ref base, ref index) => {
|
ast::ExprIndex(ref base, ref index) => {
|
||||||
let (bv, bt) = const_expr(cx, &**base, param_substs, fn_args);
|
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::Int(i)) => i as u64,
|
||||||
Ok(ConstVal::Uint(u)) => u,
|
Ok(ConstVal::Uint(u)) => u,
|
||||||
_ => cx.sess().span_bug(index.span,
|
_ => cx.sess().span_bug(index.span,
|
||||||
|
|||||||
@@ -50,6 +50,7 @@
|
|||||||
|
|
||||||
use middle::astconv_util::{prim_ty_to_ty, check_path_args, NO_TPS, NO_REGIONS};
|
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::{self, ConstVal};
|
||||||
|
use middle::const_eval::EvalHint::UncheckedExprHint;
|
||||||
use middle::def;
|
use middle::def;
|
||||||
use middle::implicator::object_region_bounds;
|
use middle::implicator::object_region_bounds;
|
||||||
use middle::resolve_lifetime as rl;
|
use middle::resolve_lifetime as rl;
|
||||||
@@ -1623,7 +1624,8 @@ pub fn ast_ty_to_ty<'tcx>(this: &AstConv<'tcx>,
|
|||||||
ty
|
ty
|
||||||
}
|
}
|
||||||
ast::TyFixedLengthVec(ref ty, ref e) => {
|
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) => {
|
Ok(r) => {
|
||||||
match r {
|
match r {
|
||||||
ConstVal::Int(i) =>
|
ConstVal::Int(i) =>
|
||||||
|
|||||||
@@ -8,7 +8,6 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
use middle::const_eval;
|
|
||||||
use middle::def;
|
use middle::def;
|
||||||
use middle::infer;
|
use middle::infer;
|
||||||
use middle::pat_util::{PatIdMap, pat_id_map, pat_is_binding};
|
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 require_same_types;
|
||||||
use util::nodemap::FnvHashMap;
|
use util::nodemap::FnvHashMap;
|
||||||
|
|
||||||
use std::cmp::{self, Ordering};
|
use std::cmp;
|
||||||
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
use std::collections::hash_map::Entry::{Occupied, Vacant};
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::ast_util;
|
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);
|
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
|
// subtyping doesn't matter here, as the value is some kind of scalar
|
||||||
demand::eqtype(fcx, pat.span, expected, lhs_ty);
|
demand::eqtype(fcx, pat.span, expected, lhs_ty);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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##"
|
E0033: r##"
|
||||||
This error indicates that a pointer to a trait type cannot be implicitly
|
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
|
dereferenced by a pattern. Every trait defines a type, but because the
|
||||||
|
|||||||
@@ -8,11 +8,10 @@
|
|||||||
// option. This file may not be copied, modified, or distributed
|
// option. This file may not be copied, modified, or distributed
|
||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
#![feature(negate_unsigned)]
|
|
||||||
#![deny(exceeding_bitshifts)]
|
#![deny(exceeding_bitshifts)]
|
||||||
#![allow(unused_variables)]
|
#![allow(unused_variables)]
|
||||||
#![allow(dead_code)]
|
#![allow(dead_code)]
|
||||||
#![feature(num_bits_bytes, negate_unsigned)]
|
#![feature(num_bits_bytes)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let n = 1u8 << 7;
|
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_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 = 1_usize << std::usize::BITS; //~ ERROR: bitshift exceeds the type's number of bits
|
||||||
|
|
||||||
|
|
||||||
|
let n = 1i8<<(1isize+-1);
|
||||||
}
|
}
|
||||||
|
|||||||
23
src/test/compile-fail/match-range-fail-2.rs
Normal file
23
src/test/compile-fail/match-range-fail-2.rs
Normal file
@@ -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 <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.
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -9,12 +9,6 @@
|
|||||||
// except according to those terms.
|
// except according to those terms.
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
match 5 {
|
|
||||||
6 ... 1 => { }
|
|
||||||
_ => { }
|
|
||||||
};
|
|
||||||
//~^^^ ERROR lower range bound must be less than or equal to upper
|
|
||||||
|
|
||||||
match "wow" {
|
match "wow" {
|
||||||
"bar" ... "foo" => { }
|
"bar" ... "foo" => { }
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user