diff options
| author | Eduard-Mihai Burtescu <edy.burt@gmail.com> | 2017-02-15 15:00:20 +0200 |
|---|---|---|
| committer | Eduard-Mihai Burtescu <edy.burt@gmail.com> | 2017-02-25 18:35:26 +0200 |
| commit | e7a48821c0d30ccd6b7ea88be24eed1bcba5f48a (patch) | |
| tree | 82387e47712464862cadd6749f6c8556d59df63b /src/librustc_const_eval | |
| parent | c832e6f3272fd01e22a6370e7745e305fe13a4c9 (diff) | |
rustc_const_eval: always demand typeck_tables for evaluating constants.
Diffstat (limited to 'src/librustc_const_eval')
| -rw-r--r-- | src/librustc_const_eval/_match.rs | 25 | ||||
| -rw-r--r-- | src/librustc_const_eval/check_match.rs | 2 | ||||
| -rw-r--r-- | src/librustc_const_eval/diagnostics.rs | 16 | ||||
| -rw-r--r-- | src/librustc_const_eval/eval.rs | 693 | ||||
| -rw-r--r-- | src/librustc_const_eval/lib.rs | 1 | ||||
| -rw-r--r-- | src/librustc_const_eval/pattern.rs | 24 |
6 files changed, 251 insertions, 510 deletions
diff --git a/src/librustc_const_eval/_match.rs b/src/librustc_const_eval/_match.rs index 5a9f885719c..53a7e872928 100644 --- a/src/librustc_const_eval/_match.rs +++ b/src/librustc_const_eval/_match.rs @@ -221,21 +221,21 @@ impl<'a, 'tcx> MatchCheckCtxt<'a, 'tcx> { } #[derive(Clone, Debug, PartialEq)] -pub enum Constructor { +pub enum Constructor<'tcx> { /// The constructor of all patterns that don't vary by constructor, /// e.g. struct patterns and fixed-length arrays. Single, /// Enum variants. Variant(DefId), /// Literal values. - ConstantValue(ConstVal), + ConstantValue(ConstVal<'tcx>), /// Ranges of literal values (`2...5` and `2..5`). - ConstantRange(ConstVal, ConstVal, RangeEnd), + ConstantRange(ConstVal<'tcx>, ConstVal<'tcx>, RangeEnd), /// Array patterns of length n. Slice(usize), } -impl<'tcx> Constructor { +impl<'tcx> Constructor<'tcx> { fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> usize { match self { &Variant(vid) => adt.variant_index_with_id(vid), @@ -289,7 +289,7 @@ impl<'tcx> Witness<'tcx> { fn push_wild_constructor<'a>( mut self, cx: &MatchCheckCtxt<'a, 'tcx>, - ctor: &Constructor, + ctor: &Constructor<'tcx>, ty: Ty<'tcx>) -> Self { @@ -321,7 +321,7 @@ impl<'tcx> Witness<'tcx> { fn apply_constructor<'a>( mut self, cx: &MatchCheckCtxt<'a,'tcx>, - ctor: &Constructor, + ctor: &Constructor<'tcx>, ty: Ty<'tcx>) -> Self { @@ -399,7 +399,8 @@ impl<'tcx> Witness<'tcx> { /// We make sure to omit constructors that are statically impossible. eg for /// Option<!> we do not include Some(_) in the returned list of constructors. fn all_constructors<'a, 'tcx: 'a>(cx: &mut MatchCheckCtxt<'a, 'tcx>, - pcx: PatternContext<'tcx>) -> Vec<Constructor> + pcx: PatternContext<'tcx>) + -> Vec<Constructor<'tcx>> { debug!("all_constructors({:?})", pcx.ty); match pcx.ty.sty { @@ -664,7 +665,7 @@ fn is_useful_specialized<'p, 'a:'p, 'tcx: 'a>( cx: &mut MatchCheckCtxt<'a, 'tcx>, &Matrix(ref m): &Matrix<'p, 'tcx>, v: &[&'p Pattern<'tcx>], - ctor: Constructor, + ctor: Constructor<'tcx>, lty: Ty<'tcx>, witness: WitnessPreference) -> Usefulness<'tcx> { @@ -702,10 +703,10 @@ fn is_useful_specialized<'p, 'a:'p, 'tcx: 'a>( /// `[a, b, ..tail]` can match a slice of length 2, 3, 4 and so on. /// /// Returns None in case of a catch-all, which can't be specialized. -fn pat_constructors(_cx: &mut MatchCheckCtxt, - pat: &Pattern, - pcx: PatternContext) - -> Option<Vec<Constructor>> +fn pat_constructors<'tcx>(_cx: &mut MatchCheckCtxt, + pat: &Pattern<'tcx>, + pcx: PatternContext) + -> Option<Vec<Constructor<'tcx>>> { match *pat.kind { PatternKind::Binding { .. } | PatternKind::Wild => diff --git a/src/librustc_const_eval/check_match.rs b/src/librustc_const_eval/check_match.rs index db5df72267d..e2b9f174ff0 100644 --- a/src/librustc_const_eval/check_match.rs +++ b/src/librustc_const_eval/check_match.rs @@ -124,7 +124,7 @@ impl<'a, 'tcx> MatchVisitor<'a, 'tcx> { "statics cannot be referenced in patterns"); } PatternError::ConstEval(err) => { - report_const_eval_err(self.tcx, &err, pat_span, "pattern").emit(); + report_const_eval_err(self.tcx, &err, pat_span, "pattern"); } } } diff --git a/src/librustc_const_eval/diagnostics.rs b/src/librustc_const_eval/diagnostics.rs index 8c8b2b5da36..9937cbbf8e1 100644 --- a/src/librustc_const_eval/diagnostics.rs +++ b/src/librustc_const_eval/diagnostics.rs @@ -576,22 +576,6 @@ integer type: https://doc.rust-lang.org/reference.html#ffi-attributes "##, - -E0306: r##" -In an array type `[T; N]`, `N` is the number of elements in the array. This -must be an unsigned integer. Erroneous code example: - -```compile_fail,E0306 -const X: [i32; true] = [0]; // error: expected `usize` for array length, - // found boolean -``` - -Working example: - -``` -const X: [i32; 1] = [0]; -``` -"##, } diff --git a/src/librustc_const_eval/eval.rs b/src/librustc_const_eval/eval.rs index 0ab2255aab0..284991a11b1 100644 --- a/src/librustc_const_eval/eval.rs +++ b/src/librustc_const_eval/eval.rs @@ -11,7 +11,6 @@ use rustc::middle::const_val::ConstVal::*; use rustc::middle::const_val::ConstVal; use self::ErrKind::*; -use self::EvalHint::*; use rustc::hir::map as hir_map; use rustc::hir::map::blocks::FnLikeNode; @@ -20,7 +19,7 @@ use rustc::hir::def::Def; use rustc::hir::def_id::DefId; use rustc::ty::{self, Ty, TyCtxt}; use rustc::ty::util::IntTypeExt; -use rustc::ty::subst::Substs; +use rustc::ty::subst::{Substs, Subst}; use rustc::traits::Reveal; use rustc::util::common::ErrorReported; use rustc::util::nodemap::DefIdMap; @@ -28,7 +27,6 @@ use rustc::util::nodemap::DefIdMap; use graphviz::IntoCow; use syntax::ast; use rustc::hir::{self, Expr}; -use syntax::attr::IntType; use syntax_pos::Span; use std::borrow::Cow; @@ -37,18 +35,24 @@ use std::cmp::Ordering; use rustc_const_math::*; use rustc_errors::DiagnosticBuilder; +macro_rules! signal { + ($e:expr, $exn:expr) => { + return Err(ConstEvalErr { span: $e.span, kind: $exn }) + } +} + macro_rules! math { ($e:expr, $op:expr) => { match $op { Ok(val) => val, - Err(e) => signal!($e, Math(e)), + Err(e) => signal!($e, ErrKind::from(e)), } } } fn lookup_variant_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, variant_def: DefId) - -> Option<(&'tcx Expr, Option<&'a ty::TypeckTables<'tcx>>)> { + -> Option<(&'tcx Expr, &'a ty::TypeckTables<'tcx>)> { if let Some(variant_node_id) = tcx.hir.as_local_node_id(variant_def) { let enum_node_id = tcx.hir.get_parent(variant_node_id); if let Some(hir_map::NodeItem(it)) = tcx.hir.find(enum_node_id) { @@ -58,7 +62,7 @@ fn lookup_variant_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, return variant.node.disr_expr.map(|e| { let def_id = tcx.hir.body_owner_def_id(e); (&tcx.hir.body(e).value, - tcx.maps.typeck_tables.borrow().get(&def_id).cloned()) + tcx.item_tables(def_id)) }); } } @@ -75,55 +79,41 @@ fn lookup_variant_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, /// This generally happens in late/trans const evaluation. pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, - substs: Option<&'tcx Substs<'tcx>>) + substs: &'tcx Substs<'tcx>) -> Option<(&'tcx Expr, - Option<&'a ty::TypeckTables<'tcx>>, - Option<ty::Ty<'tcx>>)> { + &'a ty::TypeckTables<'tcx>)> { if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { match tcx.hir.find(node_id) { None => None, Some(hir_map::NodeItem(&hir::Item { - node: hir::ItemConst(ref ty, body), .. + node: hir::ItemConst(_, body), .. })) | Some(hir_map::NodeImplItem(&hir::ImplItem { - node: hir::ImplItemKind::Const(ref ty, body), .. + node: hir::ImplItemKind::Const(_, body), .. })) => { Some((&tcx.hir.body(body).value, - tcx.maps.typeck_tables.borrow().get(&def_id).cloned(), - tcx.ast_ty_to_prim_ty(ty))) + tcx.item_tables(def_id))) } Some(hir_map::NodeTraitItem(ti)) => match ti.node { - hir::TraitItemKind::Const(ref ty, default) => { - if let Some(substs) = substs { - // If we have a trait item and the substitutions for it, - // `resolve_trait_associated_const` will select an impl - // or the default. - let trait_id = tcx.hir.get_parent(node_id); - let trait_id = tcx.hir.local_def_id(trait_id); - let default_value = default.map(|body| { - (&tcx.hir.body(body).value, - tcx.maps.typeck_tables.borrow().get(&def_id).cloned(), - tcx.ast_ty_to_prim_ty(ty)) - }); - resolve_trait_associated_const(tcx, def_id, default_value, trait_id, substs) - } else { - // Technically, without knowing anything about the - // expression that generates the obligation, we could - // still return the default if there is one. However, - // it's safer to return `None` than to return some value - // that may differ from what you would get from - // correctly selecting an impl. - None - } + hir::TraitItemKind::Const(_, default) => { + // If we have a trait item and the substitutions for it, + // `resolve_trait_associated_const` will select an impl + // or the default. + let trait_id = tcx.hir.get_parent(node_id); + let trait_id = tcx.hir.local_def_id(trait_id); + let default_value = default.map(|body| { + (&tcx.hir.body(body).value, + tcx.item_tables(def_id)) + }); + resolve_trait_associated_const(tcx, def_id, default_value, trait_id, substs) } _ => None }, Some(_) => None } } else { - let expr_tables_ty = tcx.sess.cstore.maybe_get_item_body(tcx, def_id).map(|body| { - (&body.value, Some(tcx.item_tables(def_id)), - Some(tcx.item_type(def_id))) + let expr_and_tables = tcx.sess.cstore.maybe_get_item_body(tcx, def_id).map(|body| { + (&body.value, tcx.item_tables(def_id)) }); match tcx.sess.cstore.describe_def(def_id) { Some(Def::AssociatedConst(_)) => { @@ -133,30 +123,26 @@ pub fn lookup_const_by_id<'a, 'tcx: 'a>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // trait-associated const if the caller gives us the // substitutions for the reference to it. if let Some(trait_id) = trait_id { - if let Some(substs) = substs { - resolve_trait_associated_const(tcx, def_id, expr_tables_ty, - trait_id, substs) - } else { - None - } + resolve_trait_associated_const(tcx, def_id, expr_and_tables, + trait_id, substs) } else { - expr_tables_ty + expr_and_tables } }, - Some(Def::Const(..)) => expr_tables_ty, + Some(Def::Const(..)) => expr_and_tables, _ => None } } } fn lookup_const_fn_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) - -> Option<(&'tcx hir::Body, Option<&'a ty::TypeckTables<'tcx>>)> + -> Option<(&'tcx hir::Body, &'a ty::TypeckTables<'tcx>)> { if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { FnLikeNode::from_node(tcx.hir.get(node_id)).and_then(|fn_like| { if fn_like.constness() == hir::Constness::Const { Some((tcx.hir.body(fn_like.body()), - tcx.maps.typeck_tables.borrow().get(&def_id).cloned())) + tcx.item_tables(def_id))) } else { None } @@ -164,7 +150,7 @@ fn lookup_const_fn_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) } else { if tcx.sess.cstore.is_const_fn(def_id) { tcx.sess.cstore.maybe_get_item_body(tcx, def_id).map(|body| { - (body, Some(tcx.item_tables(def_id))) + (body, tcx.item_tables(def_id)) }) } else { None @@ -172,7 +158,7 @@ fn lookup_const_fn_by_id<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) } } -pub fn report_const_eval_err<'a, 'tcx>( +fn build_const_eval_err<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, err: &ConstEvalErr, primary_span: Span, @@ -189,6 +175,18 @@ pub fn report_const_eval_err<'a, 'tcx>( diag } +pub fn report_const_eval_err<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + err: &ConstEvalErr, + primary_span: Span, + primary_kind: &str) +{ + if let TypeckError = err.kind { + return; + } + build_const_eval_err(tcx, err, primary_span, primary_kind).emit(); +} + pub fn fatal_const_eval_err<'a, 'tcx>( tcx: TyCtxt<'a, 'tcx, 'tcx>, err: &ConstEvalErr, @@ -196,7 +194,7 @@ pub fn fatal_const_eval_err<'a, 'tcx>( primary_kind: &str) -> ! { - report_const_eval_err(tcx, err, primary_span, primary_kind).emit(); + report_const_eval_err(tcx, err, primary_span, primary_kind); tcx.sess.abort_if_errors(); unreachable!() } @@ -222,69 +220,57 @@ pub fn note_const_eval_err<'a, 'tcx>( pub struct ConstContext<'a, 'tcx: 'a> { tcx: TyCtxt<'a, 'tcx, 'tcx>, - tables: Option<&'a ty::TypeckTables<'tcx>>, - fn_args: Option<DefIdMap<ConstVal>> + tables: &'a ty::TypeckTables<'tcx>, + substs: &'tcx Substs<'tcx>, + fn_args: Option<DefIdMap<ConstVal<'tcx>>> } impl<'a, 'tcx> ConstContext<'a, 'tcx> { pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, body: hir::BodyId) -> Self { let def_id = tcx.hir.body_owner_def_id(body); - ConstContext { - tcx: tcx, - tables: tcx.maps.typeck_tables.borrow().get(&def_id).cloned(), - fn_args: None - } + ConstContext::with_tables(tcx, tcx.item_tables(def_id)) } pub fn with_tables(tcx: TyCtxt<'a, 'tcx, 'tcx>, tables: &'a ty::TypeckTables<'tcx>) -> Self { ConstContext { tcx: tcx, - tables: Some(tables), + tables: tables, + substs: tcx.intern_substs(&[]), fn_args: None } } /// 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(&self, e: &Expr, ty_hint: EvalHint<'tcx>) -> EvalResult { - eval_const_expr_partial(self, e, ty_hint) + /// guaranteed to be evaluatable. + pub fn eval(&self, e: &Expr) -> EvalResult<'tcx> { + if self.tables.tainted_by_errors { + signal!(e, TypeckError); + } + eval_const_expr_partial(self, e) } } #[derive(Clone, Debug)] -pub struct ConstEvalErr { +pub struct ConstEvalErr<'tcx> { pub span: Span, - pub kind: ErrKind, + pub kind: ErrKind<'tcx>, } #[derive(Clone, Debug)] -pub enum ErrKind { +pub enum ErrKind<'tcx> { CannotCast, - CannotCastTo(&'static str), - InvalidOpForInts(hir::BinOp_), - InvalidOpForBools(hir::BinOp_), - InvalidOpForFloats(hir::BinOp_), - InvalidOpForIntUint(hir::BinOp_), - InvalidOpForUintInt(hir::BinOp_), - NegateOn(ConstVal), - NotOn(ConstVal), - CallOn(ConstVal), - MissingStructField, + NegateOn(ConstVal<'tcx>), + NotOn(ConstVal<'tcx>), + CallOn(ConstVal<'tcx>), + NonConstPath, UnimplementedConstVal(&'static str), - UnresolvedPath, ExpectedConstTuple, ExpectedConstStruct, - TupleIndexOutOfBounds, IndexedNonVec, - IndexNegative, - IndexNotInt, + IndexNotUsize, IndexOutOfBounds { len: u64, index: u64 }, - RepeatCountNotNatural, - RepeatCountNotInt, MiscBinaryOp, MiscCatchAll, @@ -292,18 +278,17 @@ pub enum ErrKind { IndexOpFeatureGated, Math(ConstMathErr), - IntermediateUnsignedNegative, - /// Expected, Got - TypeMismatch(String, ConstInt), + ErroneousReferencedConstant(Box<ConstEvalErr<'tcx>>), - BadType(ConstVal), - ErroneousReferencedConstant(Box<ConstEvalErr>), - CharCast(ConstInt), + TypeckError } -impl From<ConstMathErr> for ErrKind { - fn from(err: ConstMathErr) -> ErrKind { - Math(err) +impl<'tcx> From<ConstMathErr> for ErrKind<'tcx> { + fn from(err: ConstMathErr) -> ErrKind<'tcx> { + match err { + ConstMathErr::UnsignedNegation => TypeckError, + _ => Math(err) + } } } @@ -321,7 +306,7 @@ impl<'a> ConstEvalErrDescription<'a> { } } -impl ConstEvalErr { +impl<'tcx> ConstEvalErr<'tcx> { pub fn description(&self) -> ConstEvalErrDescription { use self::ErrKind::*; use self::ConstEvalErrDescription::*; @@ -335,12 +320,6 @@ impl ConstEvalErr { match self.kind { CannotCast => simple!("can't cast this type"), - CannotCastTo(s) => simple!("can't cast this type to {}", s), - InvalidOpForInts(_) => simple!("can't do this op on integrals"), - InvalidOpForBools(_) => simple!("can't do this op on bools"), - InvalidOpForFloats(_) => simple!("can't do this op on floats"), - InvalidOpForIntUint(..) => simple!("can't do this op on an isize and usize"), - InvalidOpForUintInt(..) => simple!("can't do this op on a usize and isize"), NegateOn(ref const_val) => simple!("negate on {}", const_val.description()), NotOn(ref const_val) => simple!("not on {}", const_val.description()), CallOn(ref const_val) => simple!("call on {}", const_val.description()), @@ -349,111 +328,42 @@ impl ConstEvalErr { NonConstPath => simple!("non-constant path in constant expression"), UnimplementedConstVal(what) => simple!("unimplemented constant expression: {}", what), - UnresolvedPath => simple!("unresolved path in constant expression"), ExpectedConstTuple => simple!("expected constant tuple"), ExpectedConstStruct => simple!("expected constant struct"), - TupleIndexOutOfBounds => simple!("tuple index out of bounds"), IndexedNonVec => simple!("indexing is only supported for arrays"), - IndexNegative => simple!("indices must be non-negative integers"), - IndexNotInt => simple!("indices must be integers"), + IndexNotUsize => simple!("indices must be of type `usize`"), IndexOutOfBounds { len, index } => { simple!("index out of bounds: the len is {} but the index is {}", len, index) } - RepeatCountNotNatural => simple!("repeat count must be a natural number"), - RepeatCountNotInt => simple!("repeat count must be integers"), MiscBinaryOp => simple!("bad operands for binary"), MiscCatchAll => simple!("unsupported constant expr"), IndexOpFeatureGated => simple!("the index operation on const values is unstable"), Math(ref err) => Simple(err.description().into_cow()), - IntermediateUnsignedNegative => simple!( - "during the computation of an unsigned a negative \ - number was encountered. This is most likely a bug in\ - the constant evaluator"), - - TypeMismatch(ref expected, ref got) => { - simple!("expected {}, found {}", expected, got.description()) - }, - BadType(ref i) => simple!("value of wrong type: {:?}", i), ErroneousReferencedConstant(_) => simple!("could not evaluate referenced constant"), - CharCast(ref got) => { - simple!("only `u8` can be cast as `char`, not `{}`", got.description()) - }, - } - } -} - -pub type EvalResult = Result<ConstVal, ConstEvalErr>; -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, -} -impl<'tcx> EvalHint<'tcx> { - fn erase_hint(&self) -> EvalHint<'tcx> { - match *self { - ExprTypeChecked => ExprTypeChecked, - UncheckedExprHint(_) | UncheckedExprNoHint => UncheckedExprNoHint, - } - } - fn checked_or(&self, ty: Ty<'tcx>) -> EvalHint<'tcx> { - match *self { - ExprTypeChecked => ExprTypeChecked, - _ => UncheckedExprHint(ty), + TypeckError => simple!("type-checking failed"), } } } -macro_rules! signal { - ($e:expr, $exn:expr) => { - return Err(ConstEvalErr { span: $e.span, kind: $exn }) - } -} +pub type EvalResult<'tcx> = Result<ConstVal<'tcx>, ConstEvalErr<'tcx>>; +pub type CastResult<'tcx> = Result<ConstVal<'tcx>, ErrKind<'tcx>>; fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, - e: &Expr, - ty_hint: EvalHint<'tcx>) -> EvalResult { + e: &Expr) -> EvalResult<'tcx> { let tcx = cx.tcx; - // 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. - cx.tables.map(|tables| tables.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). - cx.tables.and_then(|tables| tables.expr_ty_opt(e)) - } + let ety = cx.tables.expr_ty(e); + + // Avoid applying substitutions if they're empty, that'd ICE. + let ety = if cx.substs.is_empty() { + ety + } else { + ety.subst(tcx, cx.substs) }; + let result = match e.node { hir::ExprUnary(hir::UnNeg, ref inner) => { // unary neg literals already got their sign during creation @@ -465,28 +375,28 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, const I32_OVERFLOW: u128 = i32::min_value() as u32 as u128; const I64_OVERFLOW: u128 = i64::min_value() as u64 as u128; const I128_OVERFLOW: u128 = i128::min_value() as u128; - match (&lit.node, ety.map(|t| &t.sty)) { - (&LitKind::Int(I8_OVERFLOW, _), Some(&ty::TyInt(IntTy::I8))) | + match (&lit.node, &ety.sty) { + (&LitKind::Int(I8_OVERFLOW, _), &ty::TyInt(IntTy::I8)) | (&LitKind::Int(I8_OVERFLOW, Signed(IntTy::I8)), _) => { return Ok(Integral(I8(i8::min_value()))) }, - (&LitKind::Int(I16_OVERFLOW, _), Some(&ty::TyInt(IntTy::I16))) | + (&LitKind::Int(I16_OVERFLOW, _), &ty::TyInt(IntTy::I16)) | (&LitKind::Int(I16_OVERFLOW, Signed(IntTy::I16)), _) => { return Ok(Integral(I16(i16::min_value()))) }, - (&LitKind::Int(I32_OVERFLOW, _), Some(&ty::TyInt(IntTy::I32))) | + (&LitKind::Int(I32_OVERFLOW, _), &ty::TyInt(IntTy::I32)) | (&LitKind::Int(I32_OVERFLOW, Signed(IntTy::I32)), _) => { return Ok(Integral(I32(i32::min_value()))) }, - (&LitKind::Int(I64_OVERFLOW, _), Some(&ty::TyInt(IntTy::I64))) | + (&LitKind::Int(I64_OVERFLOW, _), &ty::TyInt(IntTy::I64)) | (&LitKind::Int(I64_OVERFLOW, Signed(IntTy::I64)), _) => { return Ok(Integral(I64(i64::min_value()))) }, - (&LitKind::Int(I128_OVERFLOW, _), Some(&ty::TyInt(IntTy::I128))) | + (&LitKind::Int(I128_OVERFLOW, _), &ty::TyInt(IntTy::I128)) | (&LitKind::Int(I128_OVERFLOW, Signed(IntTy::I128)), _) => { return Ok(Integral(I128(i128::min_value()))) }, - (&LitKind::Int(n, _), Some(&ty::TyInt(IntTy::Is))) | + (&LitKind::Int(n, _), &ty::TyInt(IntTy::Is)) | (&LitKind::Int(n, Signed(IntTy::Is)), _) => { match tcx.sess.target.int_type { IntTy::I16 => if n == I16_OVERFLOW { @@ -498,20 +408,20 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, IntTy::I64 => if n == I64_OVERFLOW { return Ok(Integral(Isize(Is64(i64::min_value())))); }, - _ => bug!(), + _ => span_bug!(e.span, "typeck error") } }, _ => {}, } } - match cx.eval(inner, ty_hint)? { + match cx.eval(inner)? { Float(f) => Float(-f), Integral(i) => Integral(math!(e, -i)), const_val => signal!(e, NegateOn(const_val)), } } hir::ExprUnary(hir::UnNot, ref inner) => { - match cx.eval(inner, ty_hint)? { + match cx.eval(inner)? { Integral(i) => Integral(math!(e, !i)), Bool(b) => Bool(!b), const_val => signal!(e, NotOn(const_val)), @@ -519,16 +429,11 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, } hir::ExprUnary(hir::UnDeref, _) => signal!(e, UnimplementedConstVal("deref operation")), hir::ExprBinary(op, ref a, ref b) => { - let b_ty = match op.node { - hir::BiShl | hir::BiShr => ty_hint.erase_hint(), - _ => ty_hint - }; // technically, if we don't have type hints, but integral eval // gives us a type through a type-suffix, cast or const def type // we need to re-eval the other value of the BinOp if it was // not inferred - match (cx.eval(a, ty_hint)?, - cx.eval(b, b_ty)?) { + match (cx.eval(a)?, cx.eval(b)?) { (Float(a), Float(b)) => { use std::cmp::Ordering::*; match op.node { @@ -543,7 +448,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal), hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less), hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater), - _ => signal!(e, InvalidOpForFloats(op.node)), + _ => span_bug!(e.span, "typeck error"), } } (Integral(a), Integral(b)) => { @@ -565,7 +470,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, hir::BiNe => Bool(math!(e, a.try_cmp(b)) != Equal), hir::BiGe => Bool(math!(e, a.try_cmp(b)) != Less), hir::BiGt => Bool(math!(e, a.try_cmp(b)) == Greater), - _ => signal!(e, InvalidOpForInts(op.node)), + _ => span_bug!(e.span, "typeck error"), } } (Bool(a), Bool(b)) => { @@ -581,90 +486,57 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, hir::BiLe => a <= b, hir::BiGe => a >= b, hir::BiGt => a > b, - _ => signal!(e, InvalidOpForBools(op.node)), + _ => span_bug!(e.span, "typeck error"), }) } _ => signal!(e, MiscBinaryOp), } } - hir::ExprCast(ref base, ref target_ty) => { - let ety = tcx.ast_ty_to_prim_ty(&target_ty).or(ety) - .unwrap_or_else(|| { - tcx.sess.span_fatal(target_ty.span, - "target type not found for const cast") - }); - - let base_hint = if let ExprTypeChecked = ty_hint { - ExprTypeChecked - } else { - match cx.tables.and_then(|tables| tables.expr_ty_opt(&base)) { - Some(t) => UncheckedExprHint(t), - None => ty_hint - } - }; - - let val = match cx.eval(base, base_hint) { - Ok(val) => val, - Err(ConstEvalErr { kind: ErroneousReferencedConstant( - box ConstEvalErr { kind: TypeMismatch(_, val), .. }), .. }) | - Err(ConstEvalErr { kind: TypeMismatch(_, val), .. }) => { - // Something like `5i8 as usize` doesn't need a type hint for the base - // instead take the type hint from the inner value - let hint = match val.int_type() { - Some(IntType::UnsignedInt(ty)) => ty_hint.checked_or(tcx.mk_mach_uint(ty)), - Some(IntType::SignedInt(ty)) => ty_hint.checked_or(tcx.mk_mach_int(ty)), - // we had a type hint, so we can't have an unknown type - None => bug!(), - }; - cx.eval(base, hint)? - }, - Err(e) => return Err(e), - }; - match cast_const(tcx, val, ety) { + hir::ExprCast(ref base, _) => { + match cast_const(tcx, cx.eval(base)?, ety) { Ok(val) => val, Err(kind) => return Err(ConstEvalErr { span: e.span, kind: kind }), } } hir::ExprPath(ref qpath) => { - let def = cx.tables.map(|tables| tables.qpath_def(qpath, e.id)).unwrap_or_else(|| { - // There are no tables so we can only handle already-resolved HIR. - match *qpath { - hir::QPath::Resolved(_, ref path) => path.def, - hir::QPath::TypeRelative(..) => Def::Err - } - }); - match def { + let substs = cx.tables.node_id_item_substs(e.id) + .unwrap_or_else(|| tcx.intern_substs(&[])); + + // Avoid applying substitutions if they're empty, that'd ICE. + let substs = if cx.substs.is_empty() { + substs + } else { + substs.subst(tcx, cx.substs) + }; + + match cx.tables.qpath_def(qpath, e.id) { Def::Const(def_id) | Def::AssociatedConst(def_id) => { - let substs = if let ExprTypeChecked = ty_hint { - Some(cx.tables.and_then(|tables| tables.node_id_item_substs(e.id)) - .unwrap_or_else(|| tcx.intern_substs(&[]))) - } else { - None - }; - if let Some((expr, tables, ty)) = lookup_const_by_id(tcx, def_id, substs) { - let item_hint = match ty { - Some(ty) => ty_hint.checked_or(ty), - None => ty_hint, - }; - let cx = ConstContext { tcx: tcx, tables: tables, fn_args: None }; - match cx.eval(expr, item_hint) { + if let Some((expr, tables)) = lookup_const_by_id(tcx, def_id, substs) { + let cx = ConstContext::with_tables(tcx, tables); + match cx.eval(expr) { Ok(val) => val, + Err(ConstEvalErr { kind: TypeckError, .. }) => { + signal!(e, TypeckError); + } Err(err) => { debug!("bad reference: {:?}, {:?}", err.description(), err.span); signal!(e, ErroneousReferencedConstant(box err)) }, } } else { - signal!(e, NonConstPath); + signal!(e, TypeckError); } }, Def::VariantCtor(variant_def, ..) => { if let Some((expr, tables)) = lookup_variant_by_id(tcx, variant_def) { - let cx = ConstContext { tcx: tcx, tables: tables, fn_args: None }; - match cx.eval(expr, ty_hint) { + let cx = ConstContext::with_tables(tcx, tables); + match cx.eval(expr) { Ok(val) => val, + Err(ConstEvalErr { kind: TypeckError, .. }) => { + signal!(e, TypeckError); + } Err(err) => { debug!("bad reference: {:?}, {:?}", err.description(), err.span); signal!(e, ErroneousReferencedConstant(box err)) @@ -685,16 +557,14 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, signal!(e, NonConstPath); } }, - Def::Method(id) | Def::Fn(id) => Function(id), - Def::Err => signal!(e, UnresolvedPath), + Def::Method(id) | Def::Fn(id) => Function(id, substs), + Def::Err => span_bug!(e.span, "typeck error"), _ => signal!(e, NonConstPath), } } hir::ExprCall(ref callee, ref args) => { - let sub_ty_hint = ty_hint.erase_hint(); - let callee_val = cx.eval(callee, sub_ty_hint)?; - let did = match callee_val { - Function(did) => did, + let (did, substs) = match cx.eval(callee)? { + Function(did, substs) => (did, substs), Struct(_) => signal!(e, UnimplementedConstVal("tuple struct constructors")), callee => signal!(e, CallOn(callee)), }; @@ -711,8 +581,7 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, let mut call_args = DefIdMap(); for (arg, arg_expr) in arg_defs.into_iter().zip(args.iter()) { - let arg_hint = ty_hint.erase_hint(); - let arg_val = cx.eval(arg_expr, arg_hint)?; + let arg_val = cx.eval(arg_expr)?; debug!("const call arg: {:?}", arg); if let Some(def_id) = arg { assert!(call_args.insert(def_id, arg_val).is_none()); @@ -722,9 +591,10 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, let callee_cx = ConstContext { tcx: tcx, tables: tables, + substs: substs, fn_args: Some(call_args) }; - callee_cx.eval(&body.value, ty_hint)? + callee_cx.eval(&body.value)? }, hir::ExprLit(ref lit) => match lit_to_const(&lit.node, tcx, ety) { Ok(val) => val, @@ -732,32 +602,27 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, }, hir::ExprBlock(ref block) => { match block.expr { - Some(ref expr) => cx.eval(expr, ty_hint)?, - None => signal!(e, UnimplementedConstVal("empty block")), + Some(ref expr) => cx.eval(expr)?, + None => Tuple(vec![]), } } - hir::ExprType(ref e, _) => cx.eval(e, ty_hint)?, + hir::ExprType(ref e, _) => cx.eval(e)?, hir::ExprTup(ref fields) => { - let field_hint = ty_hint.erase_hint(); - Tuple(fields.iter().map(|e| cx.eval(e, field_hint)).collect::<Result<_, _>>()?) + Tuple(fields.iter().map(|e| cx.eval(e)).collect::<Result<_, _>>()?) } hir::ExprStruct(_, ref fields, _) => { - let field_hint = ty_hint.erase_hint(); Struct(fields.iter().map(|f| { - cx.eval(&f.expr, field_hint).map(|v| (f.name.node, v)) + cx.eval(&f.expr).map(|v| (f.name.node, v)) }).collect::<Result<_, _>>()?) } hir::ExprIndex(ref arr, ref idx) => { if !tcx.sess.features.borrow().const_indexing { signal!(e, IndexOpFeatureGated); } - let arr_hint = ty_hint.erase_hint(); - let arr = cx.eval(arr, arr_hint)?; - let idx_hint = ty_hint.checked_or(tcx.types.usize); - let idx = match cx.eval(idx, idx_hint)? { + let arr = cx.eval(arr)?; + let idx = match cx.eval(idx)? { Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type), - Integral(_) => bug!(), - _ => signal!(idx, IndexNotInt), + _ => signal!(idx, IndexNotUsize), }; assert_eq!(idx as usize as u64, idx); match arr { @@ -787,45 +652,25 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, } } hir::ExprArray(ref v) => { - let elem_hint = ty_hint.erase_hint(); - Array(v.iter().map(|e| cx.eval(e, elem_hint)).collect::<Result<_, _>>()?) + Array(v.iter().map(|e| cx.eval(e)).collect::<Result<_, _>>()?) } - hir::ExprRepeat(ref elem, count) => { - let elem_hint = ty_hint.erase_hint(); - let len_hint = ty_hint.checked_or(tcx.types.usize); - let n = if let Some(ty) = ety { - // For cross-crate constants, we have the type already, - // but not the body for `count`, so use the type. - match ty.sty { - ty::TyArray(_, n) => n as u64, - _ => bug!() - } - } else { - let n = &tcx.hir.body(count).value; - match ConstContext::new(tcx, count).eval(n, len_hint)? { - Integral(Usize(i)) => i.as_u64(tcx.sess.target.uint_type), - Integral(_) => signal!(e, RepeatCountNotNatural), - _ => signal!(e, RepeatCountNotInt), - } + hir::ExprRepeat(ref elem, _) => { + let n = match ety.sty { + ty::TyArray(_, n) => n as u64, + _ => span_bug!(e.span, "typeck error") }; - Repeat(Box::new(cx.eval(elem, elem_hint)?), n) + Repeat(Box::new(cx.eval(elem)?), n) }, hir::ExprTupField(ref base, index) => { - let base_hint = ty_hint.erase_hint(); - let c = cx.eval(base, base_hint)?; + let c = cx.eval(base)?; if let Tuple(ref fields) = c { - if let Some(elem) = fields.get(index.node) { - elem.clone() - } else { - signal!(e, TupleIndexOutOfBounds); - } + fields[index.node].clone() } else { signal!(base, ExpectedConstTuple); } } hir::ExprField(ref base, field_name) => { - let base_hint = ty_hint.erase_hint(); - let c = cx.eval(base, base_hint)?; + let c = cx.eval(base)?; if let Struct(ref fields) = c { if let Some(f) = fields.get(&field_name.node) { f.clone() @@ -840,82 +685,16 @@ fn eval_const_expr_partial<'a, 'tcx>(cx: &ConstContext<'a, 'tcx>, _ => signal!(e, MiscCatchAll) }; - match (ety.map(|t| &t.sty), result) { - (Some(ref ty_hint), Integral(i)) => match infer(i, tcx, ty_hint) { - Ok(inferred) => Ok(Integral(inferred)), - Err(err) => signal!(e, err), - }, - (_, result) => Ok(result), - } -} - -fn infer<'a, 'tcx>(i: ConstInt, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty_hint: &ty::TypeVariants<'tcx>) - -> Result<ConstInt, ErrKind> { - use syntax::ast::*; - - match (ty_hint, i) { - (&ty::TyInt(IntTy::I8), result @ I8(_)) => Ok(result), - (&ty::TyInt(IntTy::I16), result @ I16(_)) => Ok(result), - (&ty::TyInt(IntTy::I32), result @ I32(_)) => Ok(result), - (&ty::TyInt(IntTy::I64), result @ I64(_)) => Ok(result), - (&ty::TyInt(IntTy::I128), result @ I128(_)) => Ok(result), - (&ty::TyInt(IntTy::Is), result @ Isize(_)) => Ok(result), - - (&ty::TyUint(UintTy::U8), result @ U8(_)) => Ok(result), - (&ty::TyUint(UintTy::U16), result @ U16(_)) => Ok(result), - (&ty::TyUint(UintTy::U32), result @ U32(_)) => Ok(result), - (&ty::TyUint(UintTy::U64), result @ U64(_)) => Ok(result), - (&ty::TyUint(UintTy::U128), result @ U128(_)) => Ok(result), - (&ty::TyUint(UintTy::Us), result @ Usize(_)) => Ok(result), - - (&ty::TyInt(IntTy::I8), Infer(i)) => Ok(I8(i as i128 as i8)), - (&ty::TyInt(IntTy::I16), Infer(i)) => Ok(I16(i as i128 as i16)), - (&ty::TyInt(IntTy::I32), Infer(i)) => Ok(I32(i as i128 as i32)), - (&ty::TyInt(IntTy::I64), Infer(i)) => Ok(I64(i as i128 as i64)), - (&ty::TyInt(IntTy::I128), Infer(i)) => Ok(I128(i as i128)), - (&ty::TyInt(IntTy::Is), Infer(i)) => { - Ok(Isize(ConstIsize::new_truncating(i as i128, tcx.sess.target.int_type))) - }, - - (&ty::TyInt(IntTy::I8), InferSigned(i)) => Ok(I8(i as i8)), - (&ty::TyInt(IntTy::I16), InferSigned(i)) => Ok(I16(i as i16)), - (&ty::TyInt(IntTy::I32), InferSigned(i)) => Ok(I32(i as i32)), - (&ty::TyInt(IntTy::I64), InferSigned(i)) => Ok(I64(i as i64)), - (&ty::TyInt(IntTy::I128), InferSigned(i)) => Ok(I128(i)), - (&ty::TyInt(IntTy::Is), InferSigned(i)) => { - Ok(Isize(ConstIsize::new_truncating(i, tcx.sess.target.int_type))) - }, - - (&ty::TyUint(UintTy::U8), Infer(i)) => Ok(U8(i as u8)), - (&ty::TyUint(UintTy::U16), Infer(i)) => Ok(U16(i as u16)), - (&ty::TyUint(UintTy::U32), Infer(i)) => Ok(U32(i as u32)), - (&ty::TyUint(UintTy::U64), Infer(i)) => Ok(U64(i as u64)), - (&ty::TyUint(UintTy::U128), Infer(i)) => Ok(U128(i)), - (&ty::TyUint(UintTy::Us), Infer(i)) => { - Ok(Usize(ConstUsize::new_truncating(i, tcx.sess.target.uint_type))) - }, - (&ty::TyUint(_), InferSigned(_)) => Err(IntermediateUnsignedNegative), - - (&ty::TyInt(ity), i) => Err(TypeMismatch(ity.to_string(), i)), - (&ty::TyUint(ity), i) => Err(TypeMismatch(ity.to_string(), i)), - - (&ty::TyAdt(adt, _), i) if adt.is_enum() => { - let int_ty = adt.repr.discr_type(); - infer(i, tcx, &int_ty.to_ty(tcx).sty) - }, - (_, i) => Err(BadType(ConstVal::Integral(i))), - } + Ok(result) } fn resolve_trait_associated_const<'a, 'tcx: 'a>( tcx: TyCtxt<'a, 'tcx, 'tcx>, trait_item_id: DefId, - default_value: Option<(&'tcx Expr, Option<&'a ty::TypeckTables<'tcx>>, Option<ty::Ty<'tcx>>)>, + default_value: Option<(&'tcx Expr, &'a ty::TypeckTables<'tcx>)>, trait_id: DefId, rcvr_substs: &'tcx Substs<'tcx> -) -> Option<(&'tcx Expr, Option<&'a ty::TypeckTables<'tcx>>, Option<ty::Ty<'tcx>>)> +) -> Option<(&'tcx Expr, &'a ty::TypeckTables<'tcx>)> { let trait_ref = ty::Binder(ty::TraitRef::new(trait_id, rcvr_substs)); debug!("resolve_trait_associated_const: trait_ref={:?}", @@ -950,7 +729,7 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>( let ac = tcx.associated_items(impl_data.impl_def_id) .find(|item| item.kind == ty::AssociatedKind::Const && item.name == name); match ac { - Some(ic) => lookup_const_by_id(tcx, ic.def_id, None), + Some(ic) => lookup_const_by_id(tcx, ic.def_id, Substs::empty()), None => default_value, } } @@ -961,7 +740,10 @@ fn resolve_trait_associated_const<'a, 'tcx: 'a>( }) } -fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: ty::Ty) -> CastResult { +fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + val: ConstInt, + ty: Ty<'tcx>) + -> CastResult<'tcx> { let v = val.to_u128_unchecked(); match ty.sty { ty::TyBool if v == 0 => Ok(Bool(false)), @@ -982,42 +764,31 @@ fn cast_const_int<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstInt, ty: ty:: ty::TyUint(ast::UintTy::Us) => { Ok(Integral(Usize(ConstUsize::new_truncating(v, tcx.sess.target.uint_type)))) }, - ty::TyFloat(ast::FloatTy::F64) => match val.erase_type() { - Infer(u) => Ok(Float(F64(u as f64))), - InferSigned(i) => Ok(Float(F64(i as f64))), - _ => bug!("ConstInt::erase_type returned something other than Infer/InferSigned"), - }, - ty::TyFloat(ast::FloatTy::F32) => match val.erase_type() { - Infer(u) => Ok(Float(F32(u as f32))), - InferSigned(i) => Ok(Float(F32(i as f32))), - _ => bug!("ConstInt::erase_type returned something other than Infer/InferSigned"), - }, + ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(val.to_f64()))), + ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(val.to_f32()))), ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting an address to a raw ptr")), - ty::TyChar => match infer(val, tcx, &ty::TyUint(ast::UintTy::U8)) { - Ok(U8(u)) => Ok(Char(u as char)), - // can only occur before typeck, typeck blocks `T as char` for `T` != `u8` - _ => Err(CharCast(val)), + ty::TyChar => match val { + U8(u) => Ok(Char(u as char)), + _ => bug!(), }, - _ => Err(CannotCast), + _ => bug!(), } } fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstFloat, - ty: ty::Ty) -> CastResult { + ty: Ty<'tcx>) -> CastResult<'tcx> { match ty.sty { ty::TyInt(_) | ty::TyUint(_) => { let i = match val { - F32(f) if f >= 0.0 => Infer(f as u128), - FInfer { f64: f, .. } | - F64(f) if f >= 0.0 => Infer(f as u128), + F32(f) if f >= 0.0 => U128(f as u128), + F64(f) if f >= 0.0 => U128(f as u128), - F32(f) => InferSigned(f as i128), - FInfer { f64: f, .. } | - F64(f) => InferSigned(f as i128) + F32(f) => I128(f as i128), + F64(f) => I128(f as i128) }; - if let (InferSigned(_), &ty::TyUint(_)) = (i, &ty.sty) { + if let (I128(_), &ty::TyUint(_)) = (i, &ty.sty) { return Err(CannotCast); } @@ -1025,23 +796,26 @@ fn cast_const_float<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } ty::TyFloat(ast::FloatTy::F64) => Ok(Float(F64(match val { F32(f) => f as f64, - FInfer { f64: f, .. } | F64(f) => f + F64(f) => f }))), ty::TyFloat(ast::FloatTy::F32) => Ok(Float(F32(match val { F64(f) => f as f32, - FInfer { f32: f, .. } | F32(f) => f + F32(f) => f }))), _ => Err(CannotCast), } } -fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstVal, ty: ty::Ty) -> CastResult { +fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + val: ConstVal<'tcx>, + ty: Ty<'tcx>) + -> CastResult<'tcx> { match val { Integral(i) => cast_const_int(tcx, i, ty), - Bool(b) => cast_const_int(tcx, Infer(b as u128), ty), + Bool(b) => cast_const_int(tcx, U8(b as u8), ty), Float(f) => cast_const_float(tcx, f, ty), - Char(c) => cast_const_int(tcx, Infer(c as u128), ty), - Function(_) => Err(UnimplementedConstVal("casting fn pointers")), + Char(c) => cast_const_int(tcx, U32(c as u32), ty), + Function(..) => Err(UnimplementedConstVal("casting fn pointers")), ByteStr(b) => match ty.sty { ty::TyRawPtr(_) => { Err(ErrKind::UnimplementedConstVal("casting a bytestr to a raw ptr")) @@ -1069,66 +843,56 @@ fn cast_const<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, val: ConstVal, ty: ty::Ty) fn lit_to_const<'a, 'tcx>(lit: &ast::LitKind, tcx: TyCtxt<'a, 'tcx, 'tcx>, - ty_hint: Option<Ty<'tcx>>) - -> Result<ConstVal, ErrKind> { + mut ty: Ty<'tcx>) + -> Result<ConstVal<'tcx>, ErrKind<'tcx>> { use syntax::ast::*; use syntax::ast::LitIntType::*; + + if let ty::TyAdt(adt, _) = ty.sty { + if adt.is_enum() { + ty = adt.repr.discr_type().to_ty(tcx) + } + } + match *lit { LitKind::Str(ref s, _) => Ok(Str(s.as_str())), LitKind::ByteStr(ref data) => Ok(ByteStr(data.clone())), LitKind::Byte(n) => Ok(Integral(U8(n))), - LitKind::Int(n, Signed(ity)) => { - infer(InferSigned(n as i128), tcx, &ty::TyInt(ity)).map(Integral) - }, - - // FIXME: this should become u128. - LitKind::Int(n, Unsuffixed) => { - match ty_hint.map(|t| &t.sty) { - Some(&ty::TyInt(ity)) => { - infer(InferSigned(n as i128), tcx, &ty::TyInt(ity)).map(Integral) - }, - Some(&ty::TyUint(uty)) => { - infer(Infer(n as u128), tcx, &ty::TyUint(uty)).map(Integral) - }, - None => Ok(Integral(Infer(n as u128))), - Some(&ty::TyAdt(adt, _)) => { - let int_ty = adt.repr.discr_type(); - infer(Infer(n as u128), tcx, &int_ty.to_ty(tcx).sty).map(Integral) - }, - Some(ty_hint) => bug!("bad ty_hint: {:?}, {:?}", ty_hint, lit), + LitKind::Int(n, hint) => { + match (&ty.sty, hint) { + (&ty::TyInt(ity), _) | + (_, Signed(ity)) => { + Ok(Integral(ConstInt::new_signed_truncating(n as i128, + ity, tcx.sess.target.int_type))) + } + (&ty::TyUint(uty), _) | + (_, Unsigned(uty)) => { + Ok(Integral(ConstInt::new_unsigned_truncating(n as u128, + uty, tcx.sess.target.uint_type))) + } + _ => bug!() } - }, - LitKind::Int(n, Unsigned(ity)) => { - infer(Infer(n as u128), tcx, &ty::TyUint(ity)).map(Integral) - }, - + } LitKind::Float(n, fty) => { - parse_float(&n.as_str(), Some(fty)).map(Float) + parse_float(&n.as_str(), fty).map(Float) } LitKind::FloatUnsuffixed(n) => { - let fty_hint = match ty_hint.map(|t| &t.sty) { - Some(&ty::TyFloat(fty)) => Some(fty), - _ => None + let fty = match ty.sty { + ty::TyFloat(fty) => fty, + _ => bug!() }; - parse_float(&n.as_str(), fty_hint).map(Float) + parse_float(&n.as_str(), fty).map(Float) } LitKind::Bool(b) => Ok(Bool(b)), LitKind::Char(c) => Ok(Char(c)), } } -fn parse_float(num: &str, fty_hint: Option<ast::FloatTy>) - -> Result<ConstFloat, ErrKind> { - let val = match fty_hint { - Some(ast::FloatTy::F32) => num.parse::<f32>().map(F32), - Some(ast::FloatTy::F64) => num.parse::<f64>().map(F64), - None => { - num.parse::<f32>().and_then(|f32| { - num.parse::<f64>().map(|f64| { - FInfer { f32: f32, f64: f64 } - }) - }) - } +fn parse_float<'tcx>(num: &str, fty: ast::FloatTy) + -> Result<ConstFloat, ErrKind<'tcx>> { + let val = match fty { + ast::FloatTy::F32 => num.parse::<f32>().map(F32), + ast::FloatTy::F64 => num.parse::<f64>().map(F64) }; val.map_err(|_| { // FIXME(#31407) this is only necessary because float parsing is buggy @@ -1168,17 +932,17 @@ impl<'a, 'tcx> ConstContext<'a, 'tcx> { a: &Expr, b: &Expr) -> Result<Ordering, ErrorReported> { let tcx = self.tcx; - let a = match self.eval(a, ExprTypeChecked) { + let a = match self.eval(a) { Ok(a) => a, Err(e) => { - report_const_eval_err(tcx, &e, a.span, "expression").emit(); + report_const_eval_err(tcx, &e, a.span, "expression"); return Err(ErrorReported); } }; - let b = match self.eval(b, ExprTypeChecked) { + let b = match self.eval(b) { Ok(b) => b, Err(e) => { - report_const_eval_err(tcx, &e, b.span, "expression").emit(); + report_const_eval_err(tcx, &e, b.span, "expression"); return Err(ErrorReported); } }; @@ -1193,26 +957,17 @@ pub fn eval_length<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, reason: &str) -> Result<usize, ErrorReported> { - let hint = UncheckedExprHint(tcx.types.usize); let count_expr = &tcx.hir.body(count).value; - match ConstContext::new(tcx, count).eval(count_expr, hint) { + match ConstContext::new(tcx, count).eval(count_expr) { Ok(Integral(Usize(count))) => { let val = count.as_u64(tcx.sess.target.uint_type); assert_eq!(val as usize as u64, val); Ok(val as usize) }, - Ok(const_val) => { - struct_span_err!(tcx.sess, count_expr.span, E0306, - "expected `usize` for {}, found {}", - reason, - const_val.description()) - .span_label(count_expr.span, &format!("expected `usize`")) - .emit(); - - Err(ErrorReported) - } + Ok(_) | + Err(ConstEvalErr { kind: TypeckError, .. }) => Err(ErrorReported), Err(err) => { - let mut diag = report_const_eval_err( + let mut diag = build_const_eval_err( tcx, &err, count_expr.span, reason); if let hir::ExprPath(hir::QPath::Resolved(None, ref path)) = count_expr.node { diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs index 198e49daabc..4434a901f94 100644 --- a/src/librustc_const_eval/lib.rs +++ b/src/librustc_const_eval/lib.rs @@ -21,6 +21,7 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] +#![deny(warnings)] #![feature(rustc_private)] #![feature(staged_api)] diff --git a/src/librustc_const_eval/pattern.rs b/src/librustc_const_eval/pattern.rs index 609fb3e39d6..72a47c00281 100644 --- a/src/librustc_const_eval/pattern.rs +++ b/src/librustc_const_eval/pattern.rs @@ -27,9 +27,9 @@ use syntax::ptr::P; use syntax_pos::Span; #[derive(Clone, Debug)] -pub enum PatternError { +pub enum PatternError<'tcx> { StaticInPattern(Span), - ConstEval(eval::ConstEvalErr), + ConstEval(eval::ConstEvalErr<'tcx>), } #[derive(Copy, Clone, Debug)] @@ -84,12 +84,12 @@ pub enum PatternKind<'tcx> { }, Constant { - value: ConstVal, + value: ConstVal<'tcx>, }, Range { - lo: ConstVal, - hi: ConstVal, + lo: ConstVal<'tcx>, + hi: ConstVal<'tcx>, end: RangeEnd, }, @@ -118,7 +118,7 @@ fn print_const_val(value: &ConstVal, f: &mut fmt::Formatter) -> fmt::Result { ConstVal::Char(c) => write!(f, "{:?}", c), ConstVal::Struct(_) | ConstVal::Tuple(_) | - ConstVal::Function(_) | + ConstVal::Function(..) | ConstVal::Array(..) | ConstVal::Repeat(..) => bug!("{:?} not printable in a pattern", value) } @@ -265,7 +265,7 @@ impl<'tcx> fmt::Display for Pattern<'tcx> { pub struct PatternContext<'a, 'gcx: 'tcx, 'tcx: 'a> { pub tcx: TyCtxt<'a, 'gcx, 'tcx>, pub tables: &'a ty::TypeckTables<'gcx>, - pub errors: Vec<PatternError>, + pub errors: Vec<PatternError<'tcx>>, } impl<'a, 'gcx, 'tcx> Pattern<'tcx> { @@ -582,11 +582,11 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { let tcx = self.tcx.global_tcx(); let substs = self.tables.node_id_item_substs(id) .unwrap_or_else(|| tcx.intern_substs(&[])); - match eval::lookup_const_by_id(tcx, def_id, Some(substs)) { - Some((const_expr, const_tables, _const_ty)) => { + match eval::lookup_const_by_id(tcx, def_id, substs) { + Some((const_expr, const_tables)) => { // Enter the inlined constant's tables temporarily. let old_tables = self.tables; - self.tables = const_tables.expect("missing tables after typeck"); + self.tables = const_tables; let pat = self.lower_const_expr(const_expr, pat_id, span); self.tables = old_tables; return pat; @@ -609,7 +609,7 @@ impl<'a, 'gcx, 'tcx> PatternContext<'a, 'gcx, 'tcx> { fn lower_lit(&mut self, expr: &hir::Expr) -> PatternKind<'tcx> { let const_cx = eval::ConstContext::with_tables(self.tcx.global_tcx(), self.tables); - match const_cx.eval(expr, eval::EvalHint::ExprTypeChecked) { + match const_cx.eval(expr) { Ok(value) => { PatternKind::Constant { value: value } } @@ -796,7 +796,7 @@ macro_rules! CloneImpls { } CloneImpls!{ <'tcx> - Span, Field, Mutability, ast::Name, ast::NodeId, usize, ConstVal, Region, + Span, Field, Mutability, ast::Name, ast::NodeId, usize, ConstVal<'tcx>, Region, Ty<'tcx>, BindingMode<'tcx>, &'tcx AdtDef, &'tcx Substs<'tcx>, &'tcx Kind<'tcx> } |
