Skip to content

Commit 6bdfb05

Browse files
committed
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.
1 parent e05ac39 commit 6bdfb05

File tree

14 files changed

+194
-118
lines changed

14 files changed

+194
-118
lines changed

src/librustc/diagnostics.rs

+18
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,24 @@ This error indicates that an attempt was made to divide by zero (or take the
335335
remainder of a zero divisor) in a static or constant expression.
336336
"##,
337337

338+
E0030: r##"
339+
When matching against a range, the compiler verifies that the range is
340+
non-empty. Range patterns include both end-points, so this is equivalent to
341+
requiring the start of the range to be less than or equal to the end of the
342+
range.
343+
344+
For example:
345+
346+
```
347+
match 5u32 {
348+
// This range is ok, albeit pointless.
349+
1 ... 1 => ...
350+
// This range is empty, and the compiler can tell.
351+
1000 ... 5 => ...
352+
}
353+
```
354+
"##,
355+
338356
E0079: r##"
339357
Enum variants which contain no data can be given a custom integer
340358
representation. This error indicates that the value provided is not an

src/librustc/middle/check_const.rs

+17-1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
use middle::cast::{CastKind};
2828
use middle::const_eval;
29+
use middle::const_eval::EvalHint::ExprTypeChecked;
2930
use middle::def;
3031
use middle::expr_use_visitor as euv;
3132
use middle::infer;
@@ -39,6 +40,7 @@ use syntax::codemap::Span;
3940
use syntax::visit::{self, Visitor};
4041

4142
use std::collections::hash_map::Entry;
43+
use std::cmp::Ordering;
4244

4345
// Const qualification, from partial to completely promotable.
4446
bitflags! {
@@ -365,6 +367,19 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
365367
ast::PatRange(ref start, ref end) => {
366368
self.global_expr(Mode::Const, &**start);
367369
self.global_expr(Mode::Const, &**end);
370+
371+
match const_eval::compare_lit_exprs(self.tcx, start, end) {
372+
Some(Ordering::Less) |
373+
Some(Ordering::Equal) => {}
374+
Some(Ordering::Greater) => {
375+
span_err!(self.tcx.sess, start.span, E0030,
376+
"lower range bound must be less than or equal to upper");
377+
}
378+
None => {
379+
self.tcx.sess.span_bug(
380+
start.span, "literals of different types in range pat");
381+
}
382+
}
368383
}
369384
_ => visit::walk_pat(self, p)
370385
}
@@ -457,7 +472,8 @@ impl<'a, 'tcx, 'v> Visitor<'v> for CheckCrateVisitor<'a, 'tcx> {
457472
match node_ty.sty {
458473
ty::TyUint(_) | ty::TyInt(_) if div_or_rem => {
459474
if !self.qualif.intersects(ConstQualif::NOT_CONST) {
460-
match const_eval::eval_const_expr_partial(self.tcx, ex, None) {
475+
match const_eval::eval_const_expr_partial(
476+
self.tcx, ex, ExprTypeChecked) {
461477
Ok(_) => {}
462478
Err(msg) => {
463479
span_err!(self.tcx.sess, msg.span, E0020,

src/librustc/middle/check_match.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use self::WitnessPreference::*;
1515
use middle::const_eval::{compare_const_vals, ConstVal};
1616
use middle::const_eval::{eval_const_expr, eval_const_expr_partial};
1717
use middle::const_eval::{const_expr_to_pat, lookup_const_by_id};
18+
use middle::const_eval::EvalHint::ExprTypeChecked;
1819
use middle::def::*;
1920
use middle::expr_use_visitor::{ConsumeMode, Delegate, ExprUseVisitor, Init};
2021
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)
263264
fn check_for_static_nan(cx: &MatchCheckCtxt, pat: &Pat) {
264265
ast_util::walk_pat(pat, |p| {
265266
if let ast::PatLit(ref expr) = p.node {
266-
match eval_const_expr_partial(cx.tcx, &**expr, None) {
267+
match eval_const_expr_partial(cx.tcx, &**expr, ExprTypeChecked) {
267268
Ok(ConstVal::Float(f)) if f.is_nan() => {
268269
span_warn!(cx.tcx.sess, p.span, E0003,
269270
"unmatchable NaN in pattern, \

src/librustc/middle/const_eval.rs

+115-56
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
#![allow(unsigned_negation)]
1313

1414
use self::ConstVal::*;
15-
1615
use self::ErrKind::*;
16+
use self::EvalHint::*;
1717

1818
use ast_map;
1919
use ast_map::blocks::FnLikeNode;
@@ -331,7 +331,7 @@ pub fn const_expr_to_pat(tcx: &ty::ctxt, expr: &Expr, span: Span) -> P<ast::Pat>
331331
}
332332

333333
pub fn eval_const_expr(tcx: &ty::ctxt, e: &Expr) -> ConstVal {
334-
match eval_const_expr_partial(tcx, e, None) {
334+
match eval_const_expr_partial(tcx, e, ExprTypeChecked) {
335335
Ok(r) => r,
336336
Err(s) => tcx.sess.span_fatal(s.span, &s.description())
337337
}
@@ -436,6 +436,28 @@ impl ConstEvalErr {
436436
pub type EvalResult = Result<ConstVal, ConstEvalErr>;
437437
pub type CastResult = Result<ConstVal, ErrKind>;
438438

439+
// FIXME: Long-term, this enum should go away: trying to evaluate
440+
// an expression which hasn't been type-checked is a recipe for
441+
// disaster. That said, it's not clear how to fix ast_ty_to_ty
442+
// to avoid the ordering issue.
443+
444+
/// Hint to determine how to evaluate constant expressions which
445+
/// might not be type-checked.
446+
#[derive(Copy, Clone, Debug)]
447+
pub enum EvalHint<'tcx> {
448+
/// We have a type-checked expression.
449+
ExprTypeChecked,
450+
/// We have an expression which hasn't been type-checked, but we have
451+
/// an idea of what the type will be because of the context. For example,
452+
/// the length of an array is always `usize`. (This is referred to as
453+
/// a hint because it isn't guaranteed to be consistent with what
454+
/// type-checking would compute.)
455+
UncheckedExprHint(Ty<'tcx>),
456+
/// We have an expression which has not yet been type-checked, and
457+
/// and we have no clue what the type will be.
458+
UncheckedExprNoHint,
459+
}
460+
439461
#[derive(Copy, Clone, PartialEq, Debug)]
440462
pub enum IntTy { I8, I16, I32, I64 }
441463
#[derive(Copy, Clone, PartialEq, Debug)]
@@ -706,26 +728,34 @@ pub_fn_checked_op!{ const_uint_checked_shr_via_int(a: u64, b: i64,.. UintTy) {
706728
uint_shift_body overflowing_shr Uint ShiftRightWithOverflow
707729
}}
708730

709-
// After type checking, `eval_const_expr_partial` should always suffice. The
710-
// reason for providing `eval_const_expr_with_substs` is to allow
711-
// trait-associated consts to be evaluated *during* type checking, when the
712-
// substs for each expression have not been written into `tcx` yet.
731+
/// Evaluate a constant expression in a context where the expression isn't
732+
/// guaranteed to be evaluatable. `ty_hint` is usually ExprTypeChecked,
733+
/// but a few places need to evaluate constants during type-checking, like
734+
/// computing the length of an array. (See also the FIXME above EvalHint.)
713735
pub fn eval_const_expr_partial<'tcx>(tcx: &ty::ctxt<'tcx>,
714736
e: &Expr,
715-
ty_hint: Option<Ty<'tcx>>) -> EvalResult {
716-
eval_const_expr_with_substs(tcx, e, ty_hint, |id| {
717-
tcx.node_id_item_substs(id).substs
718-
})
719-
}
720-
721-
pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
722-
e: &Expr,
723-
ty_hint: Option<Ty<'tcx>>,
724-
get_substs: S) -> EvalResult
725-
where S: Fn(ast::NodeId) -> subst::Substs<'tcx> {
737+
ty_hint: EvalHint<'tcx>) -> EvalResult {
726738
fn fromb(b: bool) -> ConstVal { Int(b as i64) }
727739

728-
let ety = ty_hint.or_else(|| tcx.expr_ty_opt(e));
740+
// Try to compute the type of the expression based on the EvalHint.
741+
// (See also the definition of EvalHint, and the FIXME above EvalHint.)
742+
let ety = match ty_hint {
743+
ExprTypeChecked => {
744+
// After type-checking, expr_ty is guaranteed to succeed.
745+
Some(tcx.expr_ty(e))
746+
}
747+
UncheckedExprHint(ty) => {
748+
// Use the type hint; it's not guaranteed to be right, but it's
749+
// usually good enough.
750+
Some(ty)
751+
}
752+
UncheckedExprNoHint => {
753+
// This expression might not be type-checked, and we have no hint.
754+
// Try to query the context for a type anyway; we might get lucky
755+
// (for example, if the expression was imported from another crate).
756+
tcx.expr_ty_opt(e)
757+
}
758+
};
729759

730760
// If type of expression itself is int or uint, normalize in these
731761
// 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>,
741771

742772
let result = match e.node {
743773
ast::ExprUnary(ast::UnNeg, ref inner) => {
744-
match try!(eval_const_expr_partial(tcx, &**inner, ety)) {
774+
match try!(eval_const_expr_partial(tcx, &**inner, ty_hint)) {
745775
Float(f) => Float(-f),
746776
Int(n) => try!(const_int_checked_neg(n, e, expr_int_type)),
747777
Uint(i) => {
@@ -762,7 +792,7 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
762792
}
763793
}
764794
ast::ExprUnary(ast::UnNot, ref inner) => {
765-
match try!(eval_const_expr_partial(tcx, &**inner, ety)) {
795+
match try!(eval_const_expr_partial(tcx, &**inner, ty_hint)) {
766796
Int(i) => Int(!i),
767797
Uint(i) => const_uint_not(i, expr_uint_type),
768798
Bool(b) => Bool(!b),
@@ -775,10 +805,16 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
775805
}
776806
ast::ExprBinary(op, ref a, ref b) => {
777807
let b_ty = match op.node {
778-
ast::BiShl | ast::BiShr => Some(tcx.types.usize),
779-
_ => ety
808+
ast::BiShl | ast::BiShr => {
809+
if let ExprTypeChecked = ty_hint {
810+
ExprTypeChecked
811+
} else {
812+
UncheckedExprHint(tcx.types.usize)
813+
}
814+
}
815+
_ => ty_hint
780816
};
781-
match (try!(eval_const_expr_partial(tcx, &**a, ety)),
817+
match (try!(eval_const_expr_partial(tcx, &**a, ty_hint)),
782818
try!(eval_const_expr_partial(tcx, &**b, b_ty))) {
783819
(Float(a), Float(b)) => {
784820
match op.node {
@@ -868,22 +904,25 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
868904
}
869905
}
870906
ast::ExprCast(ref base, ref target_ty) => {
871-
// This tends to get called w/o the type actually having been
872-
// populated in the ctxt, which was causing things to blow up
873-
// (#5900). Fall back to doing a limited lookup to get past it.
874907
let ety = ety.or_else(|| ast_ty_to_prim_ty(tcx, &**target_ty))
875908
.unwrap_or_else(|| {
876909
tcx.sess.span_fatal(target_ty.span,
877910
"target type not found for const cast")
878911
});
879912

880-
// Prefer known type to noop, but always have a type hint.
881-
//
882-
// FIXME (#23833): the type-hint can cause problems,
883-
// e.g. `(i8::MAX + 1_i8) as u32` feeds in `u32` as result
884-
// type to the sum, and thus no overflow is signaled.
885-
let base_hint = tcx.expr_ty_opt(&**base).unwrap_or(ety);
886-
let val = try!(eval_const_expr_partial(tcx, &**base, Some(base_hint)));
913+
let base_hint = if let ExprTypeChecked = ty_hint {
914+
ExprTypeChecked
915+
} else {
916+
// FIXME (#23833): the type-hint can cause problems,
917+
// e.g. `(i8::MAX + 1_i8) as u32` feeds in `u32` as result
918+
// type to the sum, and thus no overflow is signaled.
919+
match tcx.expr_ty_opt(&base) {
920+
Some(t) => UncheckedExprHint(t),
921+
None => ty_hint
922+
}
923+
};
924+
925+
let val = try!(eval_const_expr_partial(tcx, &**base, base_hint));
887926
match cast_const(tcx, val, ety) {
888927
Ok(val) => val,
889928
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>,
913952
def::FromTrait(trait_id) => match tcx.map.find(def_id.node) {
914953
Some(ast_map::NodeTraitItem(ti)) => match ti.node {
915954
ast::ConstTraitItem(ref ty, _) => {
916-
let substs = get_substs(e.id);
917-
(resolve_trait_associated_const(tcx,
918-
ti,
919-
trait_id,
920-
substs),
921-
Some(&**ty))
955+
if let ExprTypeChecked = ty_hint {
956+
let substs = tcx.node_id_item_substs(e.id).substs;
957+
(resolve_trait_associated_const(tcx,
958+
ti,
959+
trait_id,
960+
substs),
961+
Some(&**ty))
962+
} else {
963+
(None, None)
964+
}
922965
}
923966
_ => (None, None)
924967
},
@@ -947,27 +990,42 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
947990
Some(actual_e) => actual_e,
948991
None => signal!(e, NonConstPath)
949992
};
950-
let ety = ety.or_else(|| const_ty.and_then(|ty| ast_ty_to_prim_ty(tcx, ty)));
951-
try!(eval_const_expr_partial(tcx, const_expr, ety))
993+
let item_hint = if let UncheckedExprNoHint = ty_hint {
994+
match const_ty {
995+
Some(ty) => match ast_ty_to_prim_ty(tcx, ty) {
996+
Some(ty) => UncheckedExprHint(ty),
997+
None => UncheckedExprNoHint
998+
},
999+
None => UncheckedExprNoHint
1000+
}
1001+
} else {
1002+
ty_hint
1003+
};
1004+
try!(eval_const_expr_partial(tcx, const_expr, item_hint))
9521005
}
9531006
ast::ExprLit(ref lit) => {
9541007
lit_to_const(&**lit, ety)
9551008
}
956-
ast::ExprParen(ref e) => try!(eval_const_expr_partial(tcx, &**e, ety)),
1009+
ast::ExprParen(ref e) => try!(eval_const_expr_partial(tcx, &**e, ty_hint)),
9571010
ast::ExprBlock(ref block) => {
9581011
match block.expr {
959-
Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ety)),
1012+
Some(ref expr) => try!(eval_const_expr_partial(tcx, &**expr, ty_hint)),
9601013
None => Int(0)
9611014
}
9621015
}
9631016
ast::ExprTup(_) => Tuple(e.id),
9641017
ast::ExprStruct(..) => Struct(e.id),
9651018
ast::ExprTupField(ref base, index) => {
966-
if let Ok(c) = eval_const_expr_partial(tcx, base, None) {
1019+
let base_hint = if let ExprTypeChecked = ty_hint {
1020+
ExprTypeChecked
1021+
} else {
1022+
UncheckedExprNoHint
1023+
};
1024+
if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint) {
9671025
if let Tuple(tup_id) = c {
9681026
if let ast::ExprTup(ref fields) = tcx.map.expect_expr(tup_id).node {
9691027
if index.node < fields.len() {
970-
return eval_const_expr_partial(tcx, &fields[index.node], None)
1028+
return eval_const_expr_partial(tcx, &fields[index.node], base_hint)
9711029
} else {
9721030
signal!(e, TupleIndexOutOfBounds);
9731031
}
@@ -983,13 +1041,18 @@ pub fn eval_const_expr_with_substs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
9831041
}
9841042
ast::ExprField(ref base, field_name) => {
9851043
// Get the base expression if it is a struct and it is constant
986-
if let Ok(c) = eval_const_expr_partial(tcx, base, None) {
1044+
let base_hint = if let ExprTypeChecked = ty_hint {
1045+
ExprTypeChecked
1046+
} else {
1047+
UncheckedExprNoHint
1048+
};
1049+
if let Ok(c) = eval_const_expr_partial(tcx, base, base_hint) {
9871050
if let Struct(struct_id) = c {
9881051
if let ast::ExprStruct(_, ref fields, _) = tcx.map.expect_expr(struct_id).node {
9891052
// Check that the given field exists and evaluate it
9901053
if let Some(f) = fields.iter().find(|f| f.ident.node.as_str()
9911054
== field_name.node.as_str()) {
992-
return eval_const_expr_partial(tcx, &*f.expr, None)
1055+
return eval_const_expr_partial(tcx, &*f.expr, base_hint)
9931056
} else {
9941057
signal!(e, MissingStructField);
9951058
}
@@ -1165,21 +1228,17 @@ pub fn compare_const_vals(a: &ConstVal, b: &ConstVal) -> Option<Ordering> {
11651228
})
11661229
}
11671230

1168-
pub fn compare_lit_exprs<'tcx, S>(tcx: &ty::ctxt<'tcx>,
1169-
a: &Expr,
1170-
b: &Expr,
1171-
ty_hint: Option<Ty<'tcx>>,
1172-
get_substs: S) -> Option<Ordering>
1173-
where S: Fn(ast::NodeId) -> subst::Substs<'tcx> {
1174-
let a = match eval_const_expr_with_substs(tcx, a, ty_hint,
1175-
|id| {get_substs(id)}) {
1231+
pub fn compare_lit_exprs<'tcx>(tcx: &ty::ctxt<'tcx>,
1232+
a: &Expr,
1233+
b: &Expr) -> Option<Ordering> {
1234+
let a = match eval_const_expr_partial(tcx, a, ExprTypeChecked) {
11761235
Ok(a) => a,
11771236
Err(e) => {
11781237
tcx.sess.span_err(a.span, &e.description());
11791238
return None;
11801239
}
11811240
};
1182-
let b = match eval_const_expr_with_substs(tcx, b, ty_hint, get_substs) {
1241+
let b = match eval_const_expr_partial(tcx, b, ExprTypeChecked) {
11831242
Ok(b) => b,
11841243
Err(e) => {
11851244
tcx.sess.span_err(b.span, &e.description());

0 commit comments

Comments
 (0)