diff --git a/src/ci/publish_toolstate.sh b/src/ci/publish_toolstate.sh index d8ff74078220e..4c047069571bd 100755 --- a/src/ci/publish_toolstate.sh +++ b/src/ci/publish_toolstate.sh @@ -14,12 +14,15 @@ printf 'https://%s:x-oauth-basic@github.com\n' "$TOOLSTATE_REPO_ACCESS_TOKEN" \ > "$HOME/.git-credentials" git clone --depth=1 $TOOLSTATE_REPO +GIT_COMMIT="$(git rev-parse HEAD)" +GIT_COMMIT_MSG="$(git log --format=%s -n1 HEAD)" + cd rust-toolstate FAILURE=1 for RETRY_COUNT in 1 2 3 4 5; do # The purpose is to publish the new "current" toolstate in the toolstate repo. - "$BUILD_SOURCESDIRECTORY/src/tools/publish_toolstate.py" "$(git rev-parse HEAD)" \ - "$(git log --format=%s -n1 HEAD)" \ + "$BUILD_SOURCESDIRECTORY/src/tools/publish_toolstate.py" "$GIT_COMMIT" \ + "$GIT_COMMIT_MSG" \ "$MESSAGE_FILE" \ "$TOOLSTATE_REPO_ACCESS_TOKEN" # `git commit` failing means nothing to commit. diff --git a/src/libcore/any.rs b/src/libcore/any.rs index a2aa6b3fea50c..5126fe01a3f00 100644 --- a/src/libcore/any.rs +++ b/src/libcore/any.rs @@ -74,6 +74,16 @@ use crate::intrinsics; /// See the [module-level documentation][mod] for more details. /// /// [mod]: index.html +// This trait is not unsafe, though we rely on the specifics of it's sole impl's +// `type_id` function in unsafe code (e.g., `downcast`). Normally, that would be +// a problem, but because the only impl of `Any` is a blanket implementation, no +// other code can implement `Any`. +// +// We could plausibly make this trait unsafe -- it would not cause breakage, +// since we control all the implementations -- but we choose not to as that's +// both not really necessary and may confuse users about the distinction of +// unsafe traits and unsafe methods (i.e., `type_id` would still be safe to call, +// but we would likely want to indicate as such in documentation). #[stable(feature = "rust1", since = "1.0.0")] pub trait Any: 'static { /// Gets the `TypeId` of `self`. diff --git a/src/libcore/iter_private.rs b/src/libcore/iter_private.rs deleted file mode 100644 index 890db47b19700..0000000000000 --- a/src/libcore/iter_private.rs +++ /dev/null @@ -1,17 +0,0 @@ -/// An iterator whose items are random accessible efficiently -/// -/// # Safety -/// -/// The iterator's .len() and size_hint() must be exact. -/// `.len()` must be cheap to call. -/// -/// .get_unchecked() must return distinct mutable references for distinct -/// indices (if applicable), and must return a valid reference if index is in -/// 0..self.len(). -#[doc(hidden)] -pub unsafe trait TrustedRandomAccess : ExactSizeIterator { - unsafe fn get_unchecked(&mut self, i: usize) -> Self::Item; - /// Returns `true` if getting an iterator element may have - /// side effects. Remember to take inner iterators into account. - fn may_have_side_effect() -> bool; -} diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index fff876752db55..80bac92d00354 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -101,6 +101,7 @@ mod error; mod value; mod allocation; mod pointer; +mod queries; pub use self::error::{ InterpErrorInfo, InterpResult, InterpError, AssertMessage, ConstEvalErr, struct_error, @@ -116,9 +117,10 @@ pub use self::pointer::{Pointer, PointerArithmetic, CheckInAllocMsg}; use crate::mir; use crate::hir::def_id::DefId; -use crate::ty::{self, TyCtxt, Instance, subst::GenericArgKind}; +use crate::ty::{self, TyCtxt, Instance}; use crate::ty::codec::TyDecoder; use crate::ty::layout::{self, Size}; +use crate::ty::subst::GenericArgKind; use std::io; use std::fmt; use std::num::NonZeroU32; diff --git a/src/librustc/mir/interpret/queries.rs b/src/librustc/mir/interpret/queries.rs new file mode 100644 index 0000000000000..5fd49a056d22c --- /dev/null +++ b/src/librustc/mir/interpret/queries.rs @@ -0,0 +1,89 @@ +use super::{ConstEvalResult, ErrorHandled, GlobalId}; + +use crate::mir; +use crate::hir::def_id::DefId; +use crate::ty::{self, TyCtxt}; +use crate::ty::subst::{InternalSubsts, SubstsRef}; +use syntax_pos::Span; + + +impl<'tcx> TyCtxt<'tcx> { + + /// Evaluates a constant without providing any substitutions. This is useful to evaluate consts + /// that can't take any generic arguments like statics, const items or enum discriminants. If a + /// generic parameter is used within the constant `ErrorHandled::ToGeneric` will be returned. + pub fn const_eval_poly(self, def_id: DefId) -> ConstEvalResult<'tcx> { + // In some situations def_id will have substitutions within scope, but they aren't allowed + // to be used. So we can't use `Instance::mono`, instead we feed unresolved substitutions + // into `const_eval` which will return `ErrorHandled::ToGeneric` if any og them are + // encountered. + let substs = InternalSubsts::identity_for_item(self, def_id); + let instance = ty::Instance::new(def_id, substs); + let cid = GlobalId { + instance, + promoted: None, + }; + let param_env = self.param_env(def_id); + self.const_eval_validated(param_env.and(cid)) + } + + /// Resolves and evaluates a constant. + /// + /// The constant can be located on a trait like `::C`, in which case the given + /// substitutions and environment are used to resolve the constant. Alternatively if the + /// constant has generic parameters in scope the substitutions are used to evaluate the value of + /// the constant. For example in `fn foo() { let _ = [0; bar::()]; }` the repeat count + /// constant `bar::()` requires a substitution for `T`, if the substitution for `T` is still + /// too generic for the constant to be evaluated then `Err(ErrorHandled::TooGeneric)` is + /// returned. + pub fn const_eval_resolve( + self, + param_env: ty::ParamEnv<'tcx>, + def_id: DefId, + substs: SubstsRef<'tcx>, + span: Option + ) -> ConstEvalResult<'tcx> { + let instance = ty::Instance::resolve( + self, + param_env, + def_id, + substs, + ); + if let Some(instance) = instance { + self.const_eval_instance(param_env, instance, span) + } else { + Err(ErrorHandled::TooGeneric) + } + } + + pub fn const_eval_instance( + self, + param_env: ty::ParamEnv<'tcx>, + instance: ty::Instance<'tcx>, + span: Option + ) -> ConstEvalResult<'tcx> { + let cid = GlobalId { + instance, + promoted: None, + }; + if let Some(span) = span { + self.at(span).const_eval_validated(param_env.and(cid)) + } else { + self.const_eval_validated(param_env.and(cid)) + } + } + + /// Evaluate a promoted constant. + pub fn const_eval_promoted( + self, + instance: ty::Instance<'tcx>, + promoted: mir::Promoted + ) -> ConstEvalResult<'tcx> { + let cid = GlobalId { + instance, + promoted: Some(promoted), + }; + let param_env = ty::ParamEnv::reveal_all(); + self.const_eval_validated(param_env.and(cid)) + } +} diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index a038ca23ae92d..f48d22291c6a4 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -236,14 +236,22 @@ impl<'tcx, Tag> Scalar { Scalar::Raw { data: c as u128, size: 4 } } + #[inline] + pub fn try_from_uint(i: impl Into, size: Size) -> Option { + let i = i.into(); + if truncate(i, size) == i { + Some(Scalar::Raw { data: i, size: size.bytes() as u8 }) + } else { + None + } + } + #[inline] pub fn from_uint(i: impl Into, size: Size) -> Self { let i = i.into(); - assert_eq!( - truncate(i, size), i, - "Unsigned value {:#x} does not fit in {} bits", i, size.bits() - ); - Scalar::Raw { data: i, size: size.bytes() as u8 } + Self::try_from_uint(i, size).unwrap_or_else(|| { + bug!("Unsigned value {:#x} does not fit in {} bits", i, size.bits()) + }) } #[inline] @@ -267,15 +275,23 @@ impl<'tcx, Tag> Scalar { } #[inline] - pub fn from_int(i: impl Into, size: Size) -> Self { + pub fn try_from_int(i: impl Into, size: Size) -> Option { let i = i.into(); // `into` performed sign extension, we have to truncate let truncated = truncate(i as u128, size); - assert_eq!( - sign_extend(truncated, size) as i128, i, - "Signed value {:#x} does not fit in {} bits", i, size.bits() - ); - Scalar::Raw { data: truncated, size: size.bytes() as u8 } + if sign_extend(truncated, size) as i128 == i { + Some(Scalar::Raw { data: truncated, size: size.bytes() as u8 }) + } else { + None + } + } + + #[inline] + pub fn from_int(i: impl Into, size: Size) -> Self { + let i = i.into(); + Self::try_from_int(i, size).unwrap_or_else(|| { + bug!("Signed value {:#x} does not fit in {} bits", i, size.bits()) + }) } #[inline] diff --git a/src/librustc/query/mod.rs b/src/librustc/query/mod.rs index cc02165f60579..a9dd856e75857 100644 --- a/src/librustc/query/mod.rs +++ b/src/librustc/query/mod.rs @@ -448,7 +448,8 @@ rustc_queries! { /// /// **Do not use this** outside const eval. Const eval uses this to break query cycles /// during validation. Please add a comment to every use site explaining why using - /// `const_eval` isn't sufficient. + /// `const_eval_validated` isn't sufficient. The returned constant also isn't in a suitable + /// form to be used outside of const eval. query const_eval_raw(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) -> ConstEvalRawResult<'tcx> { no_force @@ -460,7 +461,13 @@ rustc_queries! { /// Results of evaluating const items or constants embedded in /// other items (such as enum variant explicit discriminants). - query const_eval(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) + /// + /// In contrast to `const_eval_raw` this performs some validation on the constant, and + /// returns a proper constant that is usable by the rest of the compiler. + /// + /// **Do not use this** directly, use one of the following wrappers: `tcx.const_eval_poly`, + /// `tcx.const_eval_resolve`, `tcx.const_eval_instance`, or `tcx.const_eval_promoted`. + query const_eval_validated(key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>) -> ConstEvalResult<'tcx> { no_force desc { |tcx| diff --git a/src/librustc/traits/fulfill.rs b/src/librustc/traits/fulfill.rs index 27731990d2b62..3fd2415c83c0c 100644 --- a/src/librustc/traits/fulfill.rs +++ b/src/librustc/traits/fulfill.rs @@ -1,5 +1,4 @@ use crate::infer::{InferCtxt, ShallowResolver}; -use crate::mir::interpret::{GlobalId, ErrorHandled}; use crate::ty::{self, Ty, TypeFoldable, ToPolyTraitRef}; use crate::ty::error::ExpectedFound; use rustc_data_structures::obligation_forest::{DoCompleted, Error, ForestObligation}; @@ -501,27 +500,13 @@ impl<'a, 'b, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'b, 'tcx> { ProcessResult::Unchanged } else { if !substs.has_local_value() { - let instance = ty::Instance::resolve( - self.selcx.tcx(), - obligation.param_env, - def_id, - substs, - ); - if let Some(instance) = instance { - let cid = GlobalId { - instance, - promoted: None, - }; - match self.selcx.tcx().at(obligation.cause.span) - .const_eval(obligation.param_env.and(cid)) { - Ok(_) => ProcessResult::Changed(vec![]), - Err(err) => ProcessResult::Error( - CodeSelectionError(ConstEvalFailure(err))) - } - } else { - ProcessResult::Error(CodeSelectionError( - ConstEvalFailure(ErrorHandled::TooGeneric) - )) + match self.selcx.tcx().const_eval_resolve(obligation.param_env, + def_id, + substs, + Some(obligation.cause.span)) { + Ok(_) => ProcessResult::Changed(vec![]), + Err(err) => ProcessResult::Error( + CodeSelectionError(ConstEvalFailure(err))) } } else { pending_obligation.stalled_on = diff --git a/src/librustc/traits/select.rs b/src/librustc/traits/select.rs index f1cd002d19b64..afc8a4d9e1415 100644 --- a/src/librustc/traits/select.rs +++ b/src/librustc/traits/select.rs @@ -33,7 +33,6 @@ use crate::dep_graph::{DepKind, DepNodeIndex}; use crate::hir::def_id::DefId; use crate::infer::{CombinedSnapshot, InferCtxt, InferOk, PlaceholderMap, TypeFreshener}; use crate::middle::lang_items; -use crate::mir::interpret::GlobalId; use crate::ty::fast_reject; use crate::ty::relate::TypeRelation; use crate::ty::subst::{Subst, SubstsRef}; @@ -820,22 +819,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } ty::Predicate::ConstEvaluatable(def_id, substs) => { - let tcx = self.tcx(); if !(obligation.param_env, substs).has_local_value() { - let param_env = obligation.param_env; - let instance = - ty::Instance::resolve(tcx, param_env, def_id, substs); - if let Some(instance) = instance { - let cid = GlobalId { - instance, - promoted: None, - }; - match self.tcx().const_eval(param_env.and(cid)) { - Ok(_) => Ok(EvaluatedToOk), - Err(_) => Ok(EvaluatedToErr), - } - } else { - Ok(EvaluatedToErr) + match self.tcx().const_eval_resolve(obligation.param_env, + def_id, + substs, + None) { + Ok(_) => Ok(EvaluatedToOk), + Err(_) => Ok(EvaluatedToErr), } } else { // Inference variables still left in param_env or substs. diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 37a443a2c7469..30356d59a7ce6 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -19,7 +19,7 @@ use crate::middle::cstore::CrateStoreDyn; use crate::middle::lang_items::{FnTraitLangItem, FnMutTraitLangItem, FnOnceTraitLangItem}; use crate::middle::resolve_lifetime::ObjectLifetimeDefault; use crate::mir::ReadOnlyBodyAndCache; -use crate::mir::interpret::{GlobalId, ErrorHandled}; +use crate::mir::interpret::ErrorHandled; use crate::mir::GeneratorLayout; use crate::session::CrateDisambiguator; use crate::traits::{self, Reveal}; @@ -2344,13 +2344,7 @@ impl<'tcx> AdtDef { pub fn eval_explicit_discr(&self, tcx: TyCtxt<'tcx>, expr_did: DefId) -> Option> { let param_env = tcx.param_env(expr_did); let repr_type = self.repr.discr_type(); - let substs = InternalSubsts::identity_for_item(tcx, expr_did); - let instance = ty::Instance::new(expr_did, substs); - let cid = GlobalId { - instance, - promoted: None - }; - match tcx.const_eval(param_env.and(cid)) { + match tcx.const_eval_poly(expr_did) { Ok(val) => { // FIXME: Find the right type and use it instead of `val.ty` here if let Some(b) = val.try_eval_bits(tcx, param_env, val.ty) { diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 02efe8df03d80..604dc03ea17bc 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -15,7 +15,7 @@ use crate::ty::{self, AdtDef, Discr, DefIdTree, TypeFlags, Ty, TyCtxt, TypeFolda use crate::ty::{List, TyS, ParamEnvAnd, ParamEnv}; use crate::ty::layout::VariantIdx; use crate::util::captures::Captures; -use crate::mir::interpret::{Scalar, GlobalId}; +use crate::mir::interpret::Scalar; use polonius_engine::Atom; use rustc_index::vec::Idx; @@ -2340,13 +2340,9 @@ impl<'tcx> Const<'tcx> { let (param_env, substs) = param_env_and_substs.into_parts(); - // try to resolve e.g. associated constants to their definition on an impl - let instance = ty::Instance::resolve(tcx, param_env, did, substs)?; - let gid = GlobalId { - instance, - promoted: None, - }; - tcx.const_eval(param_env.and(gid)).ok() + // try to resolve e.g. associated constants to their definition on an impl, and then + // evaluate the const. + tcx.const_eval_resolve(param_env, did, substs, None).ok() }; match self.val { diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs index e1750887ac43e..78a86d33a145b 100644 --- a/src/librustc_codegen_llvm/consts.rs +++ b/src/librustc_codegen_llvm/consts.rs @@ -8,7 +8,7 @@ use crate::value::Value; use libc::c_uint; use rustc::hir::def_id::DefId; use rustc::mir::interpret::{ConstValue, Allocation, read_target_uint, - Pointer, ErrorHandled, GlobalId}; + Pointer, ErrorHandled}; use rustc::mir::mono::MonoItem; use rustc::hir::Node; use rustc_target::abi::HasDataLayout; @@ -81,13 +81,7 @@ pub fn codegen_static_initializer( cx: &CodegenCx<'ll, 'tcx>, def_id: DefId, ) -> Result<(&'ll Value, &'tcx Allocation), ErrorHandled> { - let instance = ty::Instance::mono(cx.tcx, def_id); - let cid = GlobalId { - instance, - promoted: None, - }; - let param_env = ty::ParamEnv::reveal_all(); - let static_ = cx.tcx.const_eval(param_env.and(cid))?; + let static_ = cx.tcx.const_eval_poly(def_id)?; let alloc = match static_.val { ty::ConstKind::Value(ConstValue::ByRef { diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs index e178050b36918..e5a7583a8cc70 100644 --- a/src/librustc_codegen_llvm/intrinsic.rs +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -14,7 +14,6 @@ use rustc_codegen_ssa::glue; use rustc_codegen_ssa::base::{to_immediate, wants_msvc_seh, compare_simd_types}; use rustc::ty::{self, Ty}; use rustc::ty::layout::{self, FnAbiExt, LayoutOf, HasTyCtxt, Primitive}; -use rustc::mir::interpret::GlobalId; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; use rustc::hir; use rustc_target::abi::HasDataLayout; @@ -202,11 +201,9 @@ impl IntrinsicCallMethods<'tcx> for Builder<'a, 'll, 'tcx> { "needs_drop" | "type_id" | "type_name" => { - let gid = GlobalId { - instance, - promoted: None, - }; - let ty_name = self.tcx.const_eval(ty::ParamEnv::reveal_all().and(gid)).unwrap(); + let ty_name = self.tcx + .const_eval_instance(ty::ParamEnv::reveal_all(), instance, None) + .unwrap(); OperandRef::from_const(self, ty_name).immediate_or_packed_pair(self) } "init" => { diff --git a/src/librustc_codegen_ssa/mir/block.rs b/src/librustc_codegen_ssa/mir/block.rs index dabd097b000b5..a532c23a6e0a2 100644 --- a/src/librustc_codegen_ssa/mir/block.rs +++ b/src/librustc_codegen_ssa/mir/block.rs @@ -638,12 +638,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { projection: &[], } = place.as_ref() { - let param_env = ty::ParamEnv::reveal_all(); - let cid = mir::interpret::GlobalId { - instance: self.instance, - promoted: Some(promoted), - }; - let c = bx.tcx().const_eval(param_env.and(cid)); + let c = bx.tcx().const_eval_promoted(self.instance, promoted); let (llval, ty) = self.simd_shuffle_indices( &bx, terminator.source_info.span, diff --git a/src/librustc_codegen_ssa/mir/constant.rs b/src/librustc_codegen_ssa/mir/constant.rs index fb8f504d04b10..fc17e2c0c71b7 100644 --- a/src/librustc_codegen_ssa/mir/constant.rs +++ b/src/librustc_codegen_ssa/mir/constant.rs @@ -43,17 +43,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { match constant.literal.val { ty::ConstKind::Unevaluated(def_id, substs) => { let substs = self.monomorphize(&substs); - let instance = ty::Instance::resolve( - self.cx.tcx(), ty::ParamEnv::reveal_all(), def_id, substs, - ).unwrap(); - let cid = mir::interpret::GlobalId { - instance, - promoted: None, - }; - self.cx.tcx().const_eval(ty::ParamEnv::reveal_all().and(cid)).map_err(|err| { - self.cx.tcx().sess.span_err(constant.span, "erroneous constant encountered"); - err - }) + self.cx.tcx() + .const_eval_resolve(ty::ParamEnv::reveal_all(), def_id, substs, None) + .map_err(|err| { + self.cx.tcx().sess.span_err( + constant.span, + "erroneous constant encountered"); + err + }) }, _ => Ok(self.monomorphize(&constant.literal)), } diff --git a/src/librustc_codegen_ssa/mir/place.rs b/src/librustc_codegen_ssa/mir/place.rs index 5b21dfbdf1c69..98d239d353d3a 100644 --- a/src/librustc_codegen_ssa/mir/place.rs +++ b/src/librustc_codegen_ssa/mir/place.rs @@ -473,14 +473,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }), projection: [], } => { - let param_env = ty::ParamEnv::reveal_all(); let instance = Instance::new(*def_id, self.monomorphize(substs)); - let cid = mir::interpret::GlobalId { - instance: instance, - promoted: Some(*promoted), - }; let layout = cx.layout_of(self.monomorphize(&ty)); - match bx.tcx().const_eval(param_env.and(cid)) { + match bx.tcx().const_eval_promoted(instance, *promoted) { Ok(val) => match val.val { ty::ConstKind::Value(mir::interpret::ConstValue::ByRef { alloc, offset diff --git a/src/librustc_lint/builtin.rs b/src/librustc_lint/builtin.rs index de541ef5af3e3..340af64adad56 100644 --- a/src/librustc_lint/builtin.rs +++ b/src/librustc_lint/builtin.rs @@ -1147,19 +1147,9 @@ declare_lint_pass!( fn check_const(cx: &LateContext<'_, '_>, body_id: hir::BodyId) { let def_id = cx.tcx.hir().body_owner_def_id(body_id); - let param_env = if cx.tcx.is_static(def_id) { - // Use the same param_env as `codegen_static_initializer`, to reuse the cache. - ty::ParamEnv::reveal_all() - } else { - cx.tcx.param_env(def_id) - }; - let cid = ::rustc::mir::interpret::GlobalId { - instance: ty::Instance::mono(cx.tcx, def_id), - promoted: None - }; // trigger the query once for all constants since that will already report the errors // FIXME: Use ensure here - let _ = cx.tcx.const_eval(param_env.and(cid)); + let _ = cx.tcx.const_eval_poly(def_id); } impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedBrokenConst { diff --git a/src/librustc_mir/borrow_check/diagnostics/mod.rs b/src/librustc_mir/borrow_check/diagnostics/mod.rs index 1a76265fbdcf0..ede0c2084097c 100644 --- a/src/librustc_mir/borrow_check/diagnostics/mod.rs +++ b/src/librustc_mir/borrow_check/diagnostics/mod.rs @@ -32,7 +32,7 @@ mod explain_borrow; crate use mutability_errors::AccessKind; crate use region_name::{RegionName, RegionNameSource, RegionErrorNamingCtx}; -crate use region_errors::{ErrorReportingCtx, ErrorConstraintInfo}; +crate use region_errors::{ErrorReportingCtx, ErrorConstraintInfo, RegionErrors, RegionErrorKind}; crate use outlives_suggestion::OutlivesSuggestionBuilder; pub(super) struct IncludingDowncast(pub(super) bool); diff --git a/src/librustc_mir/borrow_check/diagnostics/outlives_suggestion.rs b/src/librustc_mir/borrow_check/diagnostics/outlives_suggestion.rs index b61c37b061396..dc5fed4822984 100644 --- a/src/librustc_mir/borrow_check/diagnostics/outlives_suggestion.rs +++ b/src/librustc_mir/borrow_check/diagnostics/outlives_suggestion.rs @@ -250,7 +250,9 @@ impl OutlivesSuggestionBuilder<'a> { // If there is only one constraint to suggest, then we already suggested it in the // intermediate suggestion above. - if self.constraints_to_add.len() == 1 { + if self.constraints_to_add.len() == 1 + && self.constraints_to_add.values().next().unwrap().len() == 1 + { debug!("Only 1 suggestion. Skipping."); return; } diff --git a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs index b78cd6bccf8ca..801f801fc9097 100644 --- a/src/librustc_mir/borrow_check/diagnostics/region_errors.rs +++ b/src/librustc_mir/borrow_check/diagnostics/region_errors.rs @@ -3,12 +3,13 @@ use rustc::hir::def_id::DefId; use rustc::infer::{ error_reporting::nice_region_error::NiceRegionError, + region_constraints::GenericKind, InferCtxt, NLLRegionVariableOrigin, }; use rustc::mir::{ ConstraintCategory, Local, Location, Body, }; -use rustc::ty::{self, RegionVid}; +use rustc::ty::{self, RegionVid, Ty}; use rustc_index::vec::IndexVec; use rustc_errors::DiagnosticBuilder; use std::collections::VecDeque; @@ -53,13 +54,74 @@ impl ConstraintDescription for ConstraintCategory { } } -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] enum Trace { StartRegion, FromOutlivesConstraint(OutlivesConstraint), NotVisited, } +/// A collection of errors encountered during region inference. This is needed to efficiently +/// report errors after borrow checking. +/// +/// Usually we expect this to either be empty or contain a small number of items, so we can avoid +/// allocation most of the time. +crate type RegionErrors<'tcx> = Vec>; + +#[derive(Clone, Debug)] +crate enum RegionErrorKind<'tcx> { + /// An error for a type test: `T: 'a` does not live long enough. + TypeTestDoesNotLiveLongEnough { + /// The span of the type test. + span: Span, + /// The generic type of the type test. + generic: GenericKind<'tcx>, + }, + + /// A generic bound failure for a type test. + TypeTestGenericBoundError { + /// The span of the type test. + span: Span, + /// The generic type of the type test. + generic: GenericKind<'tcx>, + /// The lower bound region. + lower_bound_region: ty::Region<'tcx>, + }, + + /// An unexpected hidden region for an opaque type. + UnexpectedHiddenRegion { + /// The def id of the opaque type. + opaque_type_def_id: DefId, + /// The hidden type. + hidden_ty: Ty<'tcx>, + /// The unexpected region. + member_region: ty::Region<'tcx>, + }, + + /// Higher-ranked subtyping error. + BoundUniversalRegionError { + /// The placeholder free region. + longer_fr: RegionVid, + /// The region that erroneously must be outlived by `longer_fr`. + error_region: RegionVid, + /// The origin of the placeholder region. + fr_origin: NLLRegionVariableOrigin, + }, + + /// Any other lifetime error. + RegionError { + /// The origin of the region. + fr_origin: NLLRegionVariableOrigin, + /// The region that should outlive `shorter_fr`. + longer_fr: RegionVid, + /// The region that should be shorter, but we can't prove it. + shorter_fr: RegionVid, + /// Indicates whether this is a reported error. We currently only report the first error + /// encountered and leave the rest unreported so as not to overwhelm the user. + is_reported: bool, + }, +} + /// Various pieces of state used when reporting borrow checker errors. pub struct ErrorReportingCtx<'a, 'b, 'tcx> { /// The region inference context used for borrow chekcing this MIR body. diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index cd1e72e9d1701..cb0def3a8ec5d 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -3,7 +3,7 @@ use rustc::hir::{self, HirId}; use rustc::hir::Node; use rustc::hir::def_id::DefId; -use rustc::infer::InferCtxt; +use rustc::infer::{opaque_types, InferCtxt}; use rustc::lint::builtin::UNUSED_MUT; use rustc::lint::builtin::{MUTABLE_BORROW_RESERVATION_CONFLICT}; use rustc::mir::{AggregateKind, BasicBlock, BorrowCheckResult, BorrowKind}; @@ -39,12 +39,15 @@ use crate::dataflow::MoveDataParamEnv; use crate::dataflow::{do_dataflow, DebugFormatted}; use crate::dataflow::EverInitializedPlaces; use crate::dataflow::{MaybeInitializedPlaces, MaybeUninitializedPlaces}; +use crate::transform::MirSource; use self::flows::Flows; use self::location::LocationTable; use self::prefixes::PrefixSet; use self::MutateMode::{JustWrite, WriteAndRead}; -use self::diagnostics::AccessKind; +use self::diagnostics::{ + AccessKind, RegionErrors, RegionErrorKind, OutlivesSuggestionBuilder, RegionErrorNamingCtx, +}; use self::path_utils::*; @@ -211,20 +214,40 @@ fn do_mir_borrowck<'a, 'tcx>( let borrow_set = Rc::new(BorrowSet::build( tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data)); - // If we are in non-lexical mode, compute the non-lexical lifetimes. - let (regioncx, polonius_output, opt_closure_req) = nll::compute_regions( + // Compute non-lexical lifetimes. + let nll::NllOutput { + regioncx, polonius_output, opt_closure_req, nll_errors + } = nll::compute_regions( infcx, def_id, free_regions, body, &promoted, - &local_names, - &upvars, location_table, param_env, &mut flow_inits, &mdpe.move_data, &borrow_set, + ); + + // Dump MIR results into a file, if that is enabled. This let us + // write unit-tests, as well as helping with debugging. + nll::dump_mir_results( + infcx, + MirSource::item(def_id), + &body, + ®ioncx, + &opt_closure_req, + ); + + // We also have a `#[rustc_nll]` annotation that causes us to dump + // information. + nll::dump_annotation( + infcx, + &body, + def_id, + ®ioncx, + &opt_closure_req, &mut errors_buffer, ); @@ -297,6 +320,9 @@ fn do_mir_borrowck<'a, 'tcx>( local_names, }; + // Compute and report region errors, if any. + mbcx.report_region_errors(nll_errors); + let mut state = Flows::new( flow_borrows, flow_uninits, @@ -1560,6 +1586,129 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { // initial reservation. } } + + /// Produces nice borrowck error diagnostics for all the errors collected in `nll_errors`. + fn report_region_errors(&mut self, nll_errors: RegionErrors<'tcx>) { + // Iterate through all the errors, producing a diagnostic for each one. The diagnostics are + // buffered in the `MirBorrowckCtxt`. + + // FIXME(mark-i-m): Would be great to get rid of the naming context. + let mut region_naming = RegionErrorNamingCtx::new(); + let mut outlives_suggestion = OutlivesSuggestionBuilder::new( + self.mir_def_id, &self.local_names + ); + + for nll_error in nll_errors.into_iter() { + match nll_error { + RegionErrorKind::TypeTestDoesNotLiveLongEnough { span, generic } => { + // FIXME. We should handle this case better. It + // indicates that we have e.g., some region variable + // whose value is like `'a+'b` where `'a` and `'b` are + // distinct unrelated univesal regions that are not + // known to outlive one another. It'd be nice to have + // some examples where this arises to decide how best + // to report it; we could probably handle it by + // iterating over the universal regions and reporting + // an error that multiple bounds are required. + self.infcx.tcx.sess + .struct_span_err( + span, + &format!("`{}` does not live long enough", generic), + ) + .buffer(&mut self.errors_buffer); + }, + + RegionErrorKind::TypeTestGenericBoundError { + span, generic, lower_bound_region + } => { + let region_scope_tree = &self.infcx.tcx.region_scope_tree(self.mir_def_id); + self.infcx + .construct_generic_bound_failure( + region_scope_tree, + span, + None, + generic, + lower_bound_region, + ) + .buffer(&mut self.errors_buffer); + }, + + RegionErrorKind::UnexpectedHiddenRegion { + opaque_type_def_id, hidden_ty, member_region, + } => { + let region_scope_tree = &self.infcx.tcx.region_scope_tree(self.mir_def_id); + opaque_types::unexpected_hidden_region_diagnostic( + self.infcx.tcx, + Some(region_scope_tree), + opaque_type_def_id, + hidden_ty, + member_region, + ) + .buffer(&mut self.errors_buffer); + } + + RegionErrorKind::BoundUniversalRegionError { + longer_fr, fr_origin, error_region, + } => { + // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. + let (_, span) = self.nonlexical_regioncx.find_outlives_blame_span( + &self.body, + longer_fr, + fr_origin, + error_region, + ); + + // FIXME: improve this error message + self.infcx + .tcx + .sess + .struct_span_err(span, "higher-ranked subtype error") + .buffer(&mut self.errors_buffer); + } + + RegionErrorKind::RegionError { + fr_origin, longer_fr, shorter_fr, is_reported, + } => { + if is_reported { + let db = self.nonlexical_regioncx.report_error( + &self.body, + &self.local_names, + &self.upvars, + self.infcx, + self.mir_def_id, + longer_fr, + fr_origin, + shorter_fr, + &mut outlives_suggestion, + &mut region_naming, + ); + + db.buffer(&mut self.errors_buffer); + } else { + // We only report the first error, so as not to overwhelm the user. See + // `RegRegionErrorKind` docs. + // + // FIXME: currently we do nothing with these, but perhaps we can do better? + // FIXME: try collecting these constraints on the outlives suggestion + // builder. Does it make the suggestions any better? + debug!( + "Unreported region error: can't prove that {:?}: {:?}", + longer_fr, shorter_fr + ); + } + } + } + } + + // Emit one outlives suggestions for each MIR def we borrowck + outlives_suggestion.add_suggestion( + &self.body, + &self.nonlexical_regioncx, + self.infcx, + &mut self.errors_buffer, + &mut region_naming + ); + } } impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { diff --git a/src/librustc_mir/borrow_check/nll.rs b/src/librustc_mir/borrow_check/nll.rs index 6d28a8caa92bd..2bbf6c9c84799 100644 --- a/src/librustc_mir/borrow_check/nll.rs +++ b/src/librustc_mir/borrow_check/nll.rs @@ -3,12 +3,11 @@ use rustc::hir::def_id::DefId; use rustc::infer::InferCtxt; use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, - Local, Location, Body, BodyAndCache, LocalKind, BasicBlock, + Location, Body, BodyAndCache, LocalKind, BasicBlock, Promoted, ReadOnlyBodyAndCache}; use rustc::ty::{self, RegionKind, RegionVid}; use rustc_index::vec::IndexVec; use rustc_errors::Diagnostic; -use syntax_pos::symbol::Symbol; use std::fmt::Debug; use std::env; use std::io; @@ -29,19 +28,28 @@ use crate::transform::MirSource; use crate::borrow_check::{ borrow_set::BorrowSet, + diagnostics::RegionErrors, location::LocationTable, facts::{AllFacts, AllFactsExt, RustcFacts}, region_infer::{RegionInferenceContext, values::RegionValueElements}, universal_regions::UniversalRegions, type_check::{self, MirTypeckResults, MirTypeckRegionConstraints}, - Upvar, renumber, constraint_generation, invalidation, + renumber, constraint_generation, invalidation, }; crate type PoloniusOutput = Output; -/// Rewrites the regions in the MIR to use NLL variables, also -/// scraping out the set of universal regions (e.g., region parameters) -/// declared on the function. That set will need to be given to +/// The output of `nll::compute_regions`. This includes the computed `RegionInferenceContext`, any +/// closure requirements to propagate, and any generated errors. +crate struct NllOutput<'tcx> { + pub regioncx: RegionInferenceContext<'tcx>, + pub polonius_output: Option>, + pub opt_closure_req: Option>, + pub nll_errors: RegionErrors<'tcx>, +} + +/// Rewrites the regions in the MIR to use NLL variables, also scraping out the set of universal +/// regions (e.g., region parameters) declared on the function. That set will need to be given to /// `compute_regions`. pub(in crate::borrow_check) fn replace_regions_in_mir<'cx, 'tcx>( infcx: &InferCtxt<'cx, 'tcx>, @@ -152,19 +160,12 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( universal_regions: UniversalRegions<'tcx>, body: ReadOnlyBodyAndCache<'_, 'tcx>, promoted: &IndexVec>, - local_names: &IndexVec>, - upvars: &[Upvar], location_table: &LocationTable, param_env: ty::ParamEnv<'tcx>, flow_inits: &mut FlowAtLocation<'tcx, MaybeInitializedPlaces<'cx, 'tcx>>, move_data: &MoveData<'tcx>, borrow_set: &BorrowSet<'tcx>, - errors_buffer: &mut Vec, -) -> ( - RegionInferenceContext<'tcx>, - Option>, - Option>, -) { +) -> NllOutput<'tcx> { let mut all_facts = AllFacts::enabled(infcx.tcx).then_some(AllFacts::default()); let universal_regions = Rc::new(universal_regions); @@ -306,40 +307,22 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( }); // Solve the region constraints. - let closure_region_requirements = regioncx.solve( + let (closure_region_requirements, nll_errors) = regioncx.solve( infcx, &body, - local_names, - upvars, def_id, - errors_buffer, polonius_output.clone(), ); - // Dump MIR results into a file, if that is enabled. This let us - // write unit-tests, as well as helping with debugging. - dump_mir_results( - infcx, - MirSource::item(def_id), - &body, - ®ioncx, - &closure_region_requirements, - ); - - // We also have a `#[rustc_nll]` annotation that causes us to dump - // information - dump_annotation( - infcx, - &body, - def_id, - ®ioncx, - &closure_region_requirements, - errors_buffer); - - (regioncx, polonius_output, closure_region_requirements) + NllOutput { + regioncx, + polonius_output, + opt_closure_req: closure_region_requirements, + nll_errors, + } } -fn dump_mir_results<'a, 'tcx>( +pub(super) fn dump_mir_results<'a, 'tcx>( infcx: &InferCtxt<'a, 'tcx>, source: MirSource<'tcx>, body: &Body<'tcx>, @@ -400,7 +383,7 @@ fn dump_mir_results<'a, 'tcx>( }; } -fn dump_annotation<'a, 'tcx>( +pub(super) fn dump_annotation<'a, 'tcx>( infcx: &InferCtxt<'a, 'tcx>, body: &Body<'tcx>, mir_def_id: DefId, diff --git a/src/librustc_mir/borrow_check/region_infer/mod.rs b/src/librustc_mir/borrow_check/region_infer/mod.rs index dedc6b9b09af2..5310ac6c48faa 100644 --- a/src/librustc_mir/borrow_check/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/region_infer/mod.rs @@ -2,7 +2,6 @@ use std::rc::Rc; use rustc::hir::def_id::DefId; use rustc::infer::canonical::QueryOutlivesConstraint; -use rustc::infer::opaque_types; use rustc::infer::region_constraints::{GenericKind, VarInfos, VerifyBound}; use rustc::infer::{InferCtxt, NLLRegionVariableOrigin, RegionVariableOrigin}; use rustc::mir::{ @@ -10,7 +9,6 @@ use rustc::mir::{ ConstraintCategory, Local, Location, }; use rustc::ty::{self, subst::SubstsRef, RegionVid, Ty, TyCtxt, TypeFoldable}; -use rustc::util::common::ErrorReported; use rustc_data_structures::binary_search_util; use rustc_index::bit_set::BitSet; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -18,9 +16,7 @@ use rustc_data_structures::graph::WithSuccessors; use rustc_data_structures::graph::scc::Sccs; use rustc_data_structures::graph::vec_graph::VecGraph; use rustc_index::vec::IndexVec; -use rustc_errors::{Diagnostic, DiagnosticBuilder}; use syntax_pos::Span; -use syntax_pos::symbol::Symbol; use crate::borrow_check::{ constraints::{ @@ -35,12 +31,9 @@ use crate::borrow_check::{ RegionValues, }, type_check::{free_region_relations::UniversalRegionRelations, Locations}, - diagnostics::{ - OutlivesSuggestionBuilder, RegionErrorNamingCtx, - }, + diagnostics::{RegionErrors, RegionErrorKind}, nll::{ToRegionVid, PoloniusOutput}, universal_regions::UniversalRegions, - Upvar, }; mod dump_mir; @@ -230,6 +223,15 @@ pub struct TypeTest<'tcx> { pub verify_bound: VerifyBound<'tcx>, } +/// When we have an unmet lifetime constraint, we try to propagate it outward (e.g. to a closure +/// environment). If we can't, it is an error. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum RegionRelationCheckResult { + Ok, + Propagated, + Error, +} + impl<'tcx> RegionInferenceContext<'tcx> { /// Creates a new region inference context with a total of /// `num_region_variables` valid inference variables; the first N @@ -432,7 +434,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } /// Adds annotations for `#[rustc_regions]`; see `UniversalRegions::annotate`. - crate fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut DiagnosticBuilder<'_>) { + crate fn annotate(&self, tcx: TyCtxt<'tcx>, err: &mut rustc_errors::DiagnosticBuilder<'_>) { self.universal_regions.annotate(tcx, err) } @@ -477,14 +479,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { &mut self, infcx: &InferCtxt<'_, 'tcx>, body: &Body<'tcx>, - local_names: &IndexVec>, - upvars: &[Upvar], mir_def_id: DefId, - errors_buffer: &mut Vec, polonius_output: Option>, - ) -> Option> { + ) -> (Option>, RegionErrors<'tcx>) { self.propagate_constraints(body); + let mut errors_buffer = RegionErrors::new(); + // If this is a closure, we can propagate unsatisfied // `outlives_requirements` to our creator, so create a vector // to store those. Otherwise, we'll pass in `None` to the @@ -496,54 +497,40 @@ impl<'tcx> RegionInferenceContext<'tcx> { self.check_type_tests( infcx, body, - mir_def_id, outlives_requirements.as_mut(), - errors_buffer, + &mut errors_buffer, ); - // If we produce any errors, we keep track of the names of all regions, so that we can use - // the same error names in any suggestions we produce. Note that we need names to be unique - // across different errors for the same MIR def so that we can make suggestions that fix - // multiple problems. - let mut region_naming = RegionErrorNamingCtx::new(); - // In Polonius mode, the errors about missing universal region relations are in the output // and need to be emitted or propagated. Otherwise, we need to check whether the // constraints were too strong, and if so, emit or propagate those errors. if infcx.tcx.sess.opts.debugging_opts.polonius { self.check_polonius_subset_errors( - infcx, body, - local_names, - upvars, - mir_def_id, outlives_requirements.as_mut(), - errors_buffer, - &mut region_naming, + &mut errors_buffer, polonius_output.expect("Polonius output is unavailable despite `-Z polonius`"), ); } else { self.check_universal_regions( - infcx, body, - local_names, - upvars, - mir_def_id, outlives_requirements.as_mut(), - errors_buffer, - &mut region_naming, + &mut errors_buffer, ); } - self.check_member_constraints(infcx, mir_def_id, errors_buffer); + self.check_member_constraints(infcx, &mut errors_buffer); let outlives_requirements = outlives_requirements.unwrap_or(vec![]); if outlives_requirements.is_empty() { - None + (None, errors_buffer) } else { let num_external_vids = self.universal_regions.num_global_and_external_regions(); - Some(ClosureRegionRequirements { num_external_vids, outlives_requirements }) + ( + Some(ClosureRegionRequirements { num_external_vids, outlives_requirements }), + errors_buffer, + ) } } @@ -838,9 +825,8 @@ impl<'tcx> RegionInferenceContext<'tcx> { &self, infcx: &InferCtxt<'_, 'tcx>, body: &Body<'tcx>, - mir_def_id: DefId, mut propagated_outlives_requirements: Option<&mut Vec>>, - errors_buffer: &mut Vec, + errors_buffer: &mut RegionErrors<'tcx>, ) { let tcx = infcx.tcx; @@ -898,32 +884,16 @@ impl<'tcx> RegionInferenceContext<'tcx> { } if let Some(lower_bound_region) = lower_bound_region { - let region_scope_tree = &tcx.region_scope_tree(mir_def_id); - infcx - .construct_generic_bound_failure( - region_scope_tree, - type_test_span, - None, - type_test.generic_kind, - lower_bound_region, - ) - .buffer(errors_buffer); + errors_buffer.push(RegionErrorKind::TypeTestGenericBoundError { + span: type_test_span, + generic: type_test.generic_kind, + lower_bound_region, + }); } else { - // FIXME. We should handle this case better. It - // indicates that we have e.g., some region variable - // whose value is like `'a+'b` where `'a` and `'b` are - // distinct unrelated univesal regions that are not - // known to outlive one another. It'd be nice to have - // some examples where this arises to decide how best - // to report it; we could probably handle it by - // iterating over the universal regions and reporting - // an error that multiple bounds are required. - tcx.sess - .struct_span_err( - type_test_span, - &format!("`{}` does not live long enough", type_test.generic_kind,), - ) - .buffer(errors_buffer); + errors_buffer.push(RegionErrorKind::TypeTestDoesNotLiveLongEnough { + span: type_test_span, + generic: type_test.generic_kind, + }); } } } @@ -1138,7 +1108,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// include the CFG anyhow. /// - For each `end('x)` element in `'r`, compute the mutual LUB, yielding /// a result `'y`. - pub (in crate::borrow_check) fn universal_upper_bound(&self, r: RegionVid) -> RegionVid { + pub(in crate::borrow_check) fn universal_upper_bound(&self, r: RegionVid) -> RegionVid { debug!("universal_upper_bound(r={:?}={})", r, self.region_value_str(r)); // Find the smallest universal region that contains all other @@ -1316,17 +1286,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// report them as errors. fn check_universal_regions( &self, - infcx: &InferCtxt<'_, 'tcx>, body: &Body<'tcx>, - local_names: &IndexVec>, - upvars: &[Upvar], - mir_def_id: DefId, mut propagated_outlives_requirements: Option<&mut Vec>>, - errors_buffer: &mut Vec, - region_naming: &mut RegionErrorNamingCtx, + errors_buffer: &mut RegionErrors<'tcx>, ) { - let mut outlives_suggestion = OutlivesSuggestionBuilder::new(mir_def_id, local_names); - for (fr, fr_definition) in self.definitions.iter_enumerated() { match fr_definition.origin { NLLRegionVariableOrigin::FreeRegion => { @@ -1334,21 +1297,15 @@ impl<'tcx> RegionInferenceContext<'tcx> { // they did not grow too large, accumulating any requirements // for our caller into the `outlives_requirements` vector. self.check_universal_region( - infcx, body, - local_names, - upvars, - mir_def_id, fr, &mut propagated_outlives_requirements, - &mut outlives_suggestion, errors_buffer, - region_naming, ); } NLLRegionVariableOrigin::Placeholder(placeholder) => { - self.check_bound_universal_region(infcx, body, mir_def_id, fr, placeholder); + self.check_bound_universal_region(fr, placeholder, errors_buffer); } NLLRegionVariableOrigin::Existential { .. } => { @@ -1356,9 +1313,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } } - - // Emit outlives suggestions - outlives_suggestion.add_suggestion(body, self, infcx, errors_buffer, region_naming); } /// Checks if Polonius has found any unexpected free region relations. @@ -1384,14 +1338,9 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// report them as errors. fn check_polonius_subset_errors( &self, - infcx: &InferCtxt<'_, 'tcx>, body: &Body<'tcx>, - local_names: &IndexVec>, - upvars: &[Upvar], - mir_def_id: DefId, mut propagated_outlives_requirements: Option<&mut Vec>>, - errors_buffer: &mut Vec, - region_naming: &mut RegionErrorNamingCtx, + errors_buffer: &mut RegionErrors<'tcx>, polonius_output: Rc, ) { debug!( @@ -1399,8 +1348,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { polonius_output.subset_errors.len() ); - let mut outlives_suggestion = OutlivesSuggestionBuilder::new(mir_def_id, local_names); - // Similarly to `check_universal_regions`: a free region relation, which was not explicitly // declared ("known") was found by Polonius, so emit an error, or propagate the // requirements for our caller into the `propagated_outlives_requirements` vector. @@ -1438,27 +1385,13 @@ impl<'tcx> RegionInferenceContext<'tcx> { body, &mut propagated_outlives_requirements, ); - if !propagated { - // If we are not in a context where we can't propagate errors, or we - // could not shrink `fr` to something smaller, then just report an - // error. - // - // Note: in this case, we use the unapproximated regions to report the - // error. This gives better error messages in some cases. - let db = self.report_error( - body, - local_names, - upvars, - infcx, - mir_def_id, - *longer_fr, - NLLRegionVariableOrigin::FreeRegion, - *shorter_fr, - &mut outlives_suggestion, - region_naming, - ); - - db.buffer(errors_buffer); + if propagated == RegionRelationCheckResult::Error { + errors_buffer.push(RegionErrorKind::RegionError { + longer_fr: *longer_fr, + shorter_fr: *shorter_fr, + fr_origin: NLLRegionVariableOrigin::FreeRegion, + is_reported: true, + }); } } @@ -1471,7 +1404,7 @@ impl<'tcx> RegionInferenceContext<'tcx> { } NLLRegionVariableOrigin::Placeholder(placeholder) => { - self.check_bound_universal_region(infcx, body, mir_def_id, fr, placeholder); + self.check_bound_universal_region(fr, placeholder, errors_buffer); } NLLRegionVariableOrigin::Existential { .. } => { @@ -1479,9 +1412,6 @@ impl<'tcx> RegionInferenceContext<'tcx> { } } } - - // Emit outlives suggestions - outlives_suggestion.add_suggestion(body, self, infcx, errors_buffer, region_naming); } /// Checks the final value for the free region `fr` to see if it @@ -1494,16 +1424,10 @@ impl<'tcx> RegionInferenceContext<'tcx> { /// `outlives_requirements` vector. fn check_universal_region( &self, - infcx: &InferCtxt<'_, 'tcx>, body: &Body<'tcx>, - local_names: &IndexVec>, - upvars: &[Upvar], - mir_def_id: DefId, longer_fr: RegionVid, propagated_outlives_requirements: &mut Option<&mut Vec>>, - outlives_suggestion: &mut OutlivesSuggestionBuilder<'_>, - errors_buffer: &mut Vec, - region_naming: &mut RegionErrorNamingCtx, + errors_buffer: &mut RegionErrors<'tcx>, ) { debug!("check_universal_region(fr={:?})", longer_fr); @@ -1522,76 +1446,59 @@ impl<'tcx> RegionInferenceContext<'tcx> { // one in this SCC, so we will always check the representative here. let representative = self.scc_representatives[longer_fr_scc]; if representative != longer_fr { - self.check_universal_region_relation( + if let RegionRelationCheckResult::Error = self.check_universal_region_relation( longer_fr, representative, - infcx, body, - local_names, - upvars, - mir_def_id, propagated_outlives_requirements, - outlives_suggestion, - errors_buffer, - region_naming, - ); + ) { + errors_buffer.push(RegionErrorKind::RegionError { + longer_fr, + shorter_fr: representative, + fr_origin: NLLRegionVariableOrigin::FreeRegion, + is_reported: true, + }); + } return; } // Find every region `o` such that `fr: o` // (because `fr` includes `end(o)`). + let mut error_reported = false; for shorter_fr in self.scc_values.universal_regions_outlived_by(longer_fr_scc) { - if let Some(ErrorReported) = self.check_universal_region_relation( + if let RegionRelationCheckResult::Error = self.check_universal_region_relation( longer_fr, shorter_fr, - infcx, body, - local_names, - upvars, - mir_def_id, propagated_outlives_requirements, - outlives_suggestion, - errors_buffer, - region_naming, ) { - // continuing to iterate just reports more errors than necessary - // - // FIXME It would also allow us to report more Outlives Suggestions, though, so - // it's not clear that that's a bad thing. Somebody should try commenting out this - // line and see it is actually a regression. - return; + // We only report the first region error. Subsequent errors are hidden so as + // not to overwhelm the user, but we do record them so as to potentially print + // better diagnostics elsewhere... + errors_buffer.push(RegionErrorKind::RegionError { + longer_fr, shorter_fr, + fr_origin: NLLRegionVariableOrigin::FreeRegion, + is_reported: !error_reported, + }); + + error_reported = true; } } } + /// Checks that we can prove that `longer_fr: shorter_fr`. If we can't we attempt to propagate + /// the constraint outward (e.g. to a closure environment), but if that fails, there is an + /// error. fn check_universal_region_relation( &self, longer_fr: RegionVid, shorter_fr: RegionVid, - infcx: &InferCtxt<'_, 'tcx>, body: &Body<'tcx>, - local_names: &IndexVec>, - upvars: &[Upvar], - mir_def_id: DefId, propagated_outlives_requirements: &mut Option<&mut Vec>>, - outlives_suggestion: &mut OutlivesSuggestionBuilder<'_>, - errors_buffer: &mut Vec, - region_naming: &mut RegionErrorNamingCtx, - ) -> Option { + ) -> RegionRelationCheckResult { // If it is known that `fr: o`, carry on. if self.universal_region_relations.outlives(longer_fr, shorter_fr) { - return None; - } - - let propagated = self.try_propagate_universal_region_error( - longer_fr, - shorter_fr, - body, - propagated_outlives_requirements, - ); - - if propagated { - None + RegionRelationCheckResult::Ok } else { // If we are not in a context where we can't propagate errors, or we // could not shrink `fr` to something smaller, then just report an @@ -1599,36 +1506,24 @@ impl<'tcx> RegionInferenceContext<'tcx> { // // Note: in this case, we use the unapproximated regions to report the // error. This gives better error messages in some cases. - let db = self.report_error( - body, - local_names, - upvars, - infcx, - mir_def_id, + self.try_propagate_universal_region_error( longer_fr, - NLLRegionVariableOrigin::FreeRegion, shorter_fr, - outlives_suggestion, - region_naming, - ); - - db.buffer(errors_buffer); - - Some(ErrorReported) + body, + propagated_outlives_requirements, + ) } } /// Attempt to propagate a region error (e.g. `'a: 'b`) that is not met to a closure's /// creator. If we cannot, then the caller should report an error to the user. - /// - /// Returns `true` if the error was propagated, and `false` otherwise. fn try_propagate_universal_region_error( &self, longer_fr: RegionVid, shorter_fr: RegionVid, body: &Body<'tcx>, propagated_outlives_requirements: &mut Option<&mut Vec>>, - ) -> bool { + ) -> RegionRelationCheckResult { if let Some(propagated_outlives_requirements) = propagated_outlives_requirements { // Shrink `longer_fr` until we find a non-local region (if we do). // We'll call it `fr-` -- it's ever so slightly smaller than @@ -1660,20 +1555,18 @@ impl<'tcx> RegionInferenceContext<'tcx> { category: blame_span_category.0, }); } - return true; + return RegionRelationCheckResult::Propagated; } } - false + RegionRelationCheckResult::Error } fn check_bound_universal_region( &self, - infcx: &InferCtxt<'_, 'tcx>, - body: &Body<'tcx>, - _mir_def_id: DefId, longer_fr: RegionVid, placeholder: ty::PlaceholderRegion, + errors_buffer: &mut RegionErrors<'tcx>, ) { debug!("check_bound_universal_region(fr={:?}, placeholder={:?})", longer_fr, placeholder,); @@ -1710,25 +1603,17 @@ impl<'tcx> RegionInferenceContext<'tcx> { .unwrap(), }; - // Find the code to blame for the fact that `longer_fr` outlives `error_fr`. - let (_, span) = self.find_outlives_blame_span( - body, longer_fr, NLLRegionVariableOrigin::Placeholder(placeholder), error_region - ); - - // Obviously, this error message is far from satisfactory. - // At present, though, it only appears in unit tests -- - // the AST-based checker uses a more conservative check, - // so to even see this error, one must pass in a special - // flag. - let mut diag = infcx.tcx.sess.struct_span_err(span, "higher-ranked subtype error"); - diag.emit(); + errors_buffer.push(RegionErrorKind::BoundUniversalRegionError { + longer_fr, + error_region, + fr_origin: NLLRegionVariableOrigin::Placeholder(placeholder), + }); } fn check_member_constraints( &self, infcx: &InferCtxt<'_, 'tcx>, - mir_def_id: DefId, - errors_buffer: &mut Vec, + errors_buffer: &mut RegionErrors<'tcx>, ) { let member_constraints = self.member_constraints.clone(); for m_c_i in member_constraints.all_indices() { @@ -1752,16 +1637,12 @@ impl<'tcx> RegionInferenceContext<'tcx> { } // If not, report an error. - let region_scope_tree = &infcx.tcx.region_scope_tree(mir_def_id); let member_region = infcx.tcx.mk_region(ty::ReVar(member_region_vid)); - opaque_types::unexpected_hidden_region_diagnostic( - infcx.tcx, - Some(region_scope_tree), - m_c.opaque_type_def_id, - m_c.hidden_ty, + errors_buffer.push(RegionErrorKind::UnexpectedHiddenRegion { + opaque_type_def_id: m_c.opaque_type_def_id, + hidden_ty: m_c.hidden_ty, member_region, - ) - .buffer(errors_buffer); + }); } } } diff --git a/src/librustc_mir/build/block.rs b/src/librustc_mir/build/block.rs index 7353ca9285ddb..aceed09757e7e 100644 --- a/src/librustc_mir/build/block.rs +++ b/src/librustc_mir/build/block.rs @@ -33,8 +33,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { this.ast_block_stmts(destination, block, span, stmts, expr, safety_mode) }); - this.cfg.terminate(unpack!(block_exit), source_info, - TerminatorKind::Goto { target: exit_block }); + this.cfg.goto(unpack!(block_exit), source_info, exit_block); exit_block.unit() } else { this.ast_block_stmts(destination, block, span, stmts, expr, diff --git a/src/librustc_mir/build/cfg.rs b/src/librustc_mir/build/cfg.rs index 6bd8d2f7c0792..0e685486c3f99 100644 --- a/src/librustc_mir/build/cfg.rs +++ b/src/librustc_mir/build/cfg.rs @@ -85,4 +85,9 @@ impl<'tcx> CFG<'tcx> { kind, }); } + + /// In the `origin` block, push a `goto -> target` terminator. + pub fn goto(&mut self, origin: BasicBlock, source_info: SourceInfo, target: BasicBlock) { + self.terminate(origin, source_info, TerminatorKind::Goto { target }) + } } diff --git a/src/librustc_mir/build/expr/into.rs b/src/librustc_mir/build/expr/into.rs index 608415408e35c..a9fa7cfa04a89 100644 --- a/src/librustc_mir/build/expr/into.rs +++ b/src/librustc_mir/build/expr/into.rs @@ -140,17 +140,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }, ); - this.cfg.terminate( - true_block, - source_info, - TerminatorKind::Goto { target: join_block }, - ); - this.cfg.terminate( - false_block, - source_info, - TerminatorKind::Goto { target: join_block }, - ); - + // Link up both branches: + this.cfg.goto(true_block, source_info, join_block); + this.cfg.goto(false_block, source_info, join_block); join_block.unit() } ExprKind::Loop { body } => { @@ -167,12 +159,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let loop_block = this.cfg.start_new_block(); let exit_block = this.cfg.start_new_block(); - // start the loop - this.cfg.terminate( - block, - source_info, - TerminatorKind::Goto { target: loop_block }, - ); + // Start the loop. + this.cfg.goto(block, source_info, loop_block); this.in_breakable_scope( Some(loop_block), @@ -196,11 +184,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let tmp = this.get_unit_temp(); // Execute the body, branching back to the test. let body_block_end = unpack!(this.into(&tmp, body_block, body)); - this.cfg.terminate( - body_block_end, - source_info, - TerminatorKind::Goto { target: loop_block }, - ); + this.cfg.goto(body_block_end, source_info, loop_block); }, ); exit_block.unit() diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index bf0b2439c00b5..6869930509cbe 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -26,6 +26,7 @@ mod simplify; mod test; mod util; +use itertools::Itertools; use std::convert::TryFrom; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -258,11 +259,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scrutinee_span, match_scope, ); - this.cfg.terminate( - binding_end, - source_info, - TerminatorKind::Goto { target: arm_block }, - ); + this.cfg.goto(binding_end, source_info, arm_block); } } @@ -278,11 +275,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let end_block = self.cfg.start_new_block(); for arm_block in arm_end_blocks { - self.cfg.terminate( - unpack!(arm_block), - outer_source_info, - TerminatorKind::Goto { target: end_block }, - ); + self.cfg.goto(unpack!(arm_block), outer_source_info, end_block); } self.source_scope = outer_source_info.scope; @@ -822,9 +815,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); let (matched_candidates, unmatched_candidates) = candidates.split_at_mut(fully_matched); - let block: BasicBlock; - - if !matched_candidates.is_empty() { + let block: BasicBlock = if !matched_candidates.is_empty() { let otherwise_block = self.select_matched_candidates( matched_candidates, start_block, @@ -832,35 +823,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { ); if let Some(last_otherwise_block) = otherwise_block { - block = last_otherwise_block + last_otherwise_block } else { // Any remaining candidates are unreachable. if unmatched_candidates.is_empty() { return; } - block = self.cfg.start_new_block(); - }; + self.cfg.start_new_block() + } } else { - block = *start_block.get_or_insert_with(|| self.cfg.start_new_block()); - } + *start_block.get_or_insert_with(|| self.cfg.start_new_block()) + }; // If there are no candidates that still need testing, we're // done. Since all matches are exhaustive, execution should // never reach this point. if unmatched_candidates.is_empty() { let source_info = self.source_info(span); - if let Some(otherwise) = otherwise_block { - self.cfg.terminate( - block, - source_info, - TerminatorKind::Goto { target: otherwise }, - ); - } else { - self.cfg.terminate( - block, - source_info, - TerminatorKind::Unreachable, - ) + match otherwise_block { + Some(otherwise) => self.cfg.goto(block, source_info, otherwise), + None => self.cfg.terminate(block, source_info, TerminatorKind::Unreachable), } return; } @@ -885,7 +867,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// ... /// /// We generate real edges from: - /// * `block` to the prebinding_block of the first pattern, + /// * `start_block` to the `prebinding_block` of the first pattern, /// * the otherwise block of the first pattern to the second pattern, /// * the otherwise block of the third pattern to the a block with an /// Unreachable terminator. @@ -948,32 +930,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let first_candidate = &reachable_candidates[0]; let first_prebinding_block = first_candidate.pre_binding_block; + // `goto -> first_prebinding_block` from the `start_block` if there is one. if let Some(start_block) = *start_block { let source_info = self.source_info(first_candidate.span); - self.cfg.terminate( - start_block, - source_info, - TerminatorKind::Goto { target: first_prebinding_block }, - ); + self.cfg.goto(start_block, source_info, first_prebinding_block); } else { *start_block = Some(first_prebinding_block); } - for window in reachable_candidates.windows(2) { - if let [first_candidate, second_candidate] = window { - let source_info = self.source_info(first_candidate.span); - if let Some(otherwise_block) = first_candidate.otherwise_block { - self.false_edges( - otherwise_block, - second_candidate.pre_binding_block, - first_candidate.next_candidate_pre_binding_block, - source_info, - ); - } else { - bug!("candidate other than the last has no guard"); - } + for (first_candidate, second_candidate) in reachable_candidates.iter().tuple_windows() { + let source_info = self.source_info(first_candidate.span); + if let Some(otherwise_block) = first_candidate.otherwise_block { + self.false_edges( + otherwise_block, + second_candidate.pre_binding_block, + first_candidate.next_candidate_pre_binding_block, + source_info, + ); } else { - bug!("<[_]>::windows returned incorrectly sized window"); + bug!("candidate other than the last has no guard"); } } @@ -992,8 +967,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { } } - let last_candidate = reachable_candidates.last().unwrap(); + let last_candidate = reachable_candidates.last().unwrap(); if let Some(otherwise) = last_candidate.otherwise_block { let source_info = self.source_info(last_candidate.span); let block = self.cfg.start_new_block(); diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index e320811ca0556..bdc1bdd5b9855 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -30,7 +30,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Test { span: match_pair.pattern.span, kind: TestKind::Switch { - adt_def: adt_def.clone(), + adt_def, variants: BitSet::new_empty(adt_def.variants.len()), }, } diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs index ec8b3c5e24bf2..87481d1d69bc7 100644 --- a/src/librustc_mir/build/matches/util.rs +++ b/src/librustc_mir/build/matches/util.rs @@ -109,15 +109,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }, ); } - _ => { - self.cfg.terminate( - from_block, - source_info, - TerminatorKind::Goto { - target: real_target - } - ); - } + _ => self.cfg.goto(from_block, source_info, real_target), } } } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 6b6a58102fa82..3b85a5d3c911b 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -606,14 +606,11 @@ where let fn_end = span.shrink_to_hi(); let source_info = builder.source_info(fn_end); let return_block = builder.return_block(); - builder.cfg.terminate(block, source_info, - TerminatorKind::Goto { target: return_block }); - builder.cfg.terminate(return_block, source_info, - TerminatorKind::Return); + builder.cfg.goto(block, source_info, return_block); + builder.cfg.terminate(return_block, source_info, TerminatorKind::Return); // Attribute any unreachable codepaths to the function's closing brace if let Some(unreachable_block) = builder.cached_unreachable_block { - builder.cfg.terminate(unreachable_block, source_info, - TerminatorKind::Unreachable); + builder.cfg.terminate(unreachable_block, source_info, TerminatorKind::Unreachable); } return_block.unit() })); diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 00a30af806a89..9c5966263dfc0 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -564,14 +564,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let source_info = scope.source_info(span); block = match scope.cached_exits.entry((target, region_scope)) { Entry::Occupied(e) => { - self.cfg.terminate(block, source_info, - TerminatorKind::Goto { target: *e.get() }); + self.cfg.goto(block, source_info, *e.get()); return; } Entry::Vacant(v) => { let b = self.cfg.start_new_block(); - self.cfg.terminate(block, source_info, - TerminatorKind::Goto { target: b }); + self.cfg.goto(block, source_info, b); v.insert(b); b } @@ -596,8 +594,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { scope = next_scope; } - let source_info = self.scopes.source_info(scope_count, span); - self.cfg.terminate(block, source_info, TerminatorKind::Goto { target }); + self.cfg.goto(block, self.scopes.source_info(scope_count, span), target); } /// Creates a path that performs all required cleanup for dropping a generator. @@ -616,14 +613,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { while let Some(scope) = scopes.next() { block = if let Some(b) = scope.cached_generator_drop { - self.cfg.terminate(block, src_info, - TerminatorKind::Goto { target: b }); + self.cfg.goto(block, src_info, b); return Some(result); } else { let b = self.cfg.start_new_block(); scope.cached_generator_drop = Some(b); - self.cfg.terminate(block, src_info, - TerminatorKind::Goto { target: b }); + self.cfg.goto(block, src_info, b); b }; @@ -1243,8 +1238,7 @@ fn build_diverge_scope<'tcx>(cfg: &mut CFG<'tcx>, // block for our StorageDead statements. let block = cfg.start_new_cleanup_block(); let source_info = SourceInfo { span: DUMMY_SP, scope: source_scope }; - cfg.terminate(block, source_info, - TerminatorKind::Goto { target: target }); + cfg.goto(block, source_info, target); target = block; target_built_by_us = true; } diff --git a/src/librustc_mir/const_eval.rs b/src/librustc_mir/const_eval.rs index b219fec31dc59..a2f066bee08d1 100644 --- a/src/librustc_mir/const_eval.rs +++ b/src/librustc_mir/const_eval.rs @@ -652,7 +652,7 @@ fn validate_and_turn_into_const<'tcx>( }) } -pub fn const_eval_provider<'tcx>( +pub fn const_eval_validated_provider<'tcx>( tcx: TyCtxt<'tcx>, key: ty::ParamEnvAnd<'tcx, GlobalId<'tcx>>, ) -> ::rustc::mir::interpret::ConstEvalResult<'tcx> { @@ -660,7 +660,7 @@ pub fn const_eval_provider<'tcx>( if key.param_env.reveal == Reveal::All { let mut key = key.clone(); key.param_env.reveal = Reveal::UserFacing; - match tcx.const_eval(key) { + match tcx.const_eval_validated(key) { // try again with reveal all as requested Err(ErrorHandled::TooGeneric) => { // Promoteds should never be "too generic" when getting evaluated. diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index c05641fe8bf0a..589016a2ca253 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -5,7 +5,7 @@ use crate::hair::cx::to_ref::ToRef; use crate::hair::util::UserAnnotatedTyHelpers; use rustc_index::vec::Idx; use rustc::hir::def::{CtorOf, Res, DefKind, CtorKind}; -use rustc::mir::interpret::{GlobalId, ErrorHandled, Scalar}; +use rustc::mir::interpret::{ErrorHandled, Scalar}; use rustc::ty::{self, AdtKind, Ty}; use rustc::ty::adjustment::{Adjustment, Adjust, AutoBorrow, AutoBorrowMutability, PointerCast}; use rustc::ty::subst::{InternalSubsts, SubstsRef}; @@ -511,21 +511,15 @@ fn make_mirror_unadjusted<'a, 'tcx>( hir::ExprKind::Repeat(ref v, ref count) => { let def_id = cx.tcx.hir().local_def_id(count.hir_id); let substs = InternalSubsts::identity_for_item(cx.tcx, def_id); - let instance = ty::Instance::resolve( - cx.tcx, - cx.param_env, - def_id, - substs, - ).unwrap(); - let global_id = GlobalId { - instance, - promoted: None - }; let span = cx.tcx.def_span(def_id); - let count = match cx.tcx.at(span).const_eval(cx.param_env.and(global_id)) { + let count = match cx.tcx.const_eval_resolve(cx.param_env, + def_id, + substs, + Some(span)) { Ok(cv) => cv.eval_usize(cx.tcx, cx.param_env), Err(ErrorHandled::Reported) => 0, Err(ErrorHandled::TooGeneric) => { + let span = cx.tcx.def_span(def_id); cx.tcx.sess.span_err(span, "array lengths can't depend on generic parameters"); 0 }, diff --git a/src/librustc_mir/hair/pattern/mod.rs b/src/librustc_mir/hair/pattern/mod.rs index 9d0a67151fb60..fdef6633925da 100644 --- a/src/librustc_mir/hair/pattern/mod.rs +++ b/src/librustc_mir/hair/pattern/mod.rs @@ -11,7 +11,7 @@ use crate::hair::constant::*; use rustc::mir::{Field, BorrowKind, Mutability}; use rustc::mir::{UserTypeProjection}; -use rustc::mir::interpret::{GlobalId, ConstValue, get_slice_bytes, sign_extend}; +use rustc::mir::interpret::{ConstValue, ErrorHandled, get_slice_bytes, sign_extend}; use rustc::ty::{self, Region, TyCtxt, AdtDef, Ty, UserType, DefIdTree}; use rustc::ty::{CanonicalUserType, CanonicalUserTypeAnnotation, CanonicalUserTypeAnnotations}; use rustc::ty::subst::{SubstsRef, GenericArg}; @@ -771,57 +771,37 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let kind = match res { Res::Def(DefKind::Const, def_id) | Res::Def(DefKind::AssocConst, def_id) => { let substs = self.tables.node_substs(id); - match ty::Instance::resolve( - self.tcx, - self.param_env, - def_id, - substs, - ) { - Some(instance) => { - let cid = GlobalId { - instance, - promoted: None, - }; - match self.tcx.at(span).const_eval(self.param_env.and(cid)) { - Ok(value) => { - let pattern = self.const_to_pat(value, id, span); - if !is_associated_const { - return pattern; - } + match self.tcx.const_eval_resolve(self.param_env, def_id, substs, Some(span)) { + Ok(value) => { + let pattern = self.const_to_pat(value, id, span); + if !is_associated_const { + return pattern; + } - let user_provided_types = self.tables().user_provided_types(); - return if let Some(u_ty) = user_provided_types.get(id) { - let user_ty = PatTyProj::from_user_type(*u_ty); - Pat { - span, - kind: Box::new( - PatKind::AscribeUserType { - subpattern: pattern, - ascription: Ascription { - /// Note that use `Contravariant` here. See the - /// `variance` field documentation for details. - variance: ty::Variance::Contravariant, - user_ty, - user_ty_span: span, - }, - } - ), - ty: value.ty, + let user_provided_types = self.tables().user_provided_types(); + return if let Some(u_ty) = user_provided_types.get(id) { + let user_ty = PatTyProj::from_user_type(*u_ty); + Pat { + span, + kind: Box::new( + PatKind::AscribeUserType { + subpattern: pattern, + ascription: Ascription { + /// Note that use `Contravariant` here. See the + /// `variance` field documentation for details. + variance: ty::Variance::Contravariant, + user_ty, + user_ty_span: span, + }, } - } else { - pattern - } - }, - Err(_) => { - self.tcx.sess.span_err( - span, - "could not evaluate constant pattern", - ); - PatKind::Wild + ), + ty: value.ty, } + } else { + pattern } }, - None => { + Err(ErrorHandled::TooGeneric) => { self.errors.push(if is_associated_const { PatternError::AssocConstInPattern(span) } else { @@ -829,6 +809,13 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { }); PatKind::Wild }, + Err(_) => { + self.tcx.sess.span_err( + span, + "could not evaluate constant pattern", + ); + PatKind::Wild + } } } _ => self.lower_variant_or_leaf(res, id, span, ty, vec![]), diff --git a/src/librustc_mir/interpret/intrinsics.rs b/src/librustc_mir/interpret/intrinsics.rs index 67f0aed243da1..46782ef0a806b 100644 --- a/src/librustc_mir/interpret/intrinsics.rs +++ b/src/librustc_mir/interpret/intrinsics.rs @@ -11,7 +11,7 @@ use rustc::hir::def_id::DefId; use rustc::ty::TyCtxt; use rustc::mir::{ self, BinOp, - interpret::{InterpResult, Scalar, GlobalId, ConstValue} + interpret::{InterpResult, Scalar, ConstValue} }; use super::{ @@ -123,11 +123,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { sym::size_of | sym::type_id | sym::type_name => { - let gid = GlobalId { - instance, - promoted: None, - }; - let val = self.tcx.const_eval(self.param_env.and(gid))?; + let val = self.tcx.const_eval_instance(self.param_env, + instance, + Some(self.tcx.span))?; let val = self.eval_const_to_op(val, None)?; self.copy_op(val, dest)?; } diff --git a/src/librustc_mir/interpret/operand.rs b/src/librustc_mir/interpret/operand.rs index 93ab7b9aab7fb..0af203079b153 100644 --- a/src/librustc_mir/interpret/operand.rs +++ b/src/librustc_mir/interpret/operand.rs @@ -218,11 +218,20 @@ impl<'tcx, Tag: Copy> ImmTy<'tcx, Tag> { ImmTy { imm: val.into(), layout } } + #[inline] + pub fn try_from_uint(i: impl Into, layout: TyLayout<'tcx>) -> Option { + Some(Self::from_scalar(Scalar::try_from_uint(i, layout.size)?, layout)) + } #[inline] pub fn from_uint(i: impl Into, layout: TyLayout<'tcx>) -> Self { Self::from_scalar(Scalar::from_uint(i, layout.size), layout) } + #[inline] + pub fn try_from_int(i: impl Into, layout: TyLayout<'tcx>) -> Option { + Some(Self::from_scalar(Scalar::try_from_int(i, layout.size)?, layout)) + } + #[inline] pub fn from_int(i: impl Into, layout: TyLayout<'tcx>) -> Self { Self::from_scalar(Scalar::from_int(i, layout.size), layout) diff --git a/src/librustc_mir/interpret/place.rs b/src/librustc_mir/interpret/place.rs index 8923b167fdee8..a1e6eb69b9d0d 100644 --- a/src/librustc_mir/interpret/place.rs +++ b/src/librustc_mir/interpret/place.rs @@ -626,11 +626,6 @@ where let ty = place_static.ty; assert!(!ty.needs_subst()); let layout = self.layout_of(ty)?; - let instance = ty::Instance::mono(*self.tcx, place_static.def_id); - let cid = GlobalId { - instance, - promoted: None - }; // Just create a lazy reference, so we can support recursive statics. // tcx takes care of assigning every static one and only one unique AllocId. // When the data here is ever actually used, memory will notice, @@ -646,7 +641,7 @@ where // Notice that statics have *two* AllocIds: the lazy one, and the resolved // one. Here we make sure that the interpreted program never sees the // resolved ID. Also see the doc comment of `Memory::get_static_alloc`. - let alloc_id = self.tcx.alloc_map.lock().create_static_alloc(cid.instance.def_id()); + let alloc_id = self.tcx.alloc_map.lock().create_static_alloc(place_static.def_id); let ptr = self.tag_static_base_pointer(Pointer::from(alloc_id)); MPlaceTy::from_aligned_ptr(ptr, layout) } diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index f6b3c5b8e5e82..cf54530317c43 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -56,7 +56,7 @@ pub fn provide(providers: &mut Providers<'_>) { shim::provide(providers); transform::provide(providers); monomorphize::partitioning::provide(providers); - providers.const_eval = const_eval::const_eval_provider; + providers.const_eval_validated = const_eval::const_eval_validated_provider; providers.const_eval_raw = const_eval::const_eval_raw_provider; providers.check_match = hair::pattern::check_match; providers.const_caller_location = const_eval::const_caller_location; diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index 83a4f224aa257..ac627d32392b8 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -189,7 +189,7 @@ use rustc::session::config::EntryFnType; use rustc::mir::{self, Location, PlaceBase, Static, StaticKind}; use rustc::mir::visit::Visitor as MirVisitor; use rustc::mir::mono::{MonoItem, InstantiationMode}; -use rustc::mir::interpret::{Scalar, GlobalId, GlobalAlloc, ErrorHandled}; +use rustc::mir::interpret::{Scalar, GlobalAlloc, ErrorHandled}; use rustc::util::nodemap::{FxHashSet, FxHashMap, DefIdMap}; use rustc::util::common::time; @@ -379,13 +379,7 @@ fn collect_items_rec<'tcx>( recursion_depth_reset = None; - let cid = GlobalId { - instance, - promoted: None, - }; - let param_env = ty::ParamEnv::reveal_all(); - - if let Ok(val) = tcx.const_eval(param_env.and(cid)) { + if let Ok(val) = tcx.const_eval_poly(def_id) { collect_const(tcx, val, InternalSubsts::empty(), &mut neighbors); } } @@ -681,12 +675,8 @@ impl<'a, 'tcx> MirVisitor<'tcx> for MirNeighborCollector<'a, 'tcx> { def_id, .. }) => { - let param_env = ty::ParamEnv::reveal_all(); - let cid = GlobalId { - instance: Instance::new(*def_id, substs.subst(self.tcx, self.param_substs)), - promoted: Some(*promoted), - }; - match self.tcx.const_eval(param_env.and(cid)) { + let instance = Instance::new(*def_id, substs.subst(self.tcx, self.param_substs)); + match self.tcx.const_eval_promoted(instance, *promoted) { Ok(val) => collect_const(self.tcx, val, substs, self.output), Err(ErrorHandled::Reported) => {}, Err(ErrorHandled::TooGeneric) => { @@ -1041,14 +1031,7 @@ impl ItemLikeVisitor<'v> for RootCollector<'_, 'v> { // but even just declaring them must collect the items they refer to let def_id = self.tcx.hir().local_def_id(item.hir_id); - let instance = Instance::mono(self.tcx, def_id); - let cid = GlobalId { - instance, - promoted: None, - }; - let param_env = ty::ParamEnv::reveal_all(); - - if let Ok(val) = self.tcx.const_eval(param_env.and(cid)) { + if let Ok(val) = self.tcx.const_eval_poly(def_id) { collect_const(self.tcx, val, InternalSubsts::empty(), &mut self.output); } } @@ -1288,16 +1271,7 @@ fn collect_const<'tcx>( } } ty::ConstKind::Unevaluated(def_id, substs) => { - let instance = ty::Instance::resolve(tcx, - param_env, - def_id, - substs).unwrap(); - - let cid = GlobalId { - instance, - promoted: None, - }; - match tcx.const_eval(param_env.and(cid)) { + match tcx.const_eval_resolve(param_env, def_id, substs, None) { Ok(val) => collect_const(tcx, val, param_substs, output), Err(ErrorHandled::Reported) => {}, Err(ErrorHandled::TooGeneric) => span_bug!( diff --git a/src/librustc_typeck/check/expr.rs b/src/librustc_typeck/check/expr.rs index 17b168cfab05a..a6ae10b3dea39 100644 --- a/src/librustc_typeck/check/expr.rs +++ b/src/librustc_typeck/check/expr.rs @@ -31,7 +31,6 @@ use rustc::hir::ptr::P; use rustc::infer; use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc::middle::lang_items; -use rustc::mir::interpret::GlobalId; use rustc::ty; use rustc::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, @@ -39,7 +38,6 @@ use rustc::ty::adjustment::{ use rustc::ty::{AdtKind, Visibility}; use rustc::ty::Ty; use rustc::ty::TypeFoldable; -use rustc::ty::subst::InternalSubsts; use rustc::traits::{self, ObligationCauseCode}; use rustc_error_codes::*; @@ -1023,20 +1021,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let count = if self.const_param_def_id(count).is_some() { Ok(self.to_const(count, tcx.type_of(count_def_id))) } else { - let param_env = ty::ParamEnv::empty(); - let substs = InternalSubsts::identity_for_item(tcx, count_def_id); - let instance = ty::Instance::resolve( - tcx, - param_env, - count_def_id, - substs, - ).unwrap(); - let global_id = GlobalId { - instance, - promoted: None - }; - - tcx.const_eval(param_env.and(global_id)) + tcx.const_eval_poly(count_def_id) }; let uty = match expected { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 5895b8d25d3d6..ea295103982b0 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -106,7 +106,7 @@ use rustc::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use rustc::infer::error_reporting::TypeAnnotationNeeded::E0282; use rustc::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; use rustc::middle::region; -use rustc::mir::interpret::{ConstValue, GlobalId}; +use rustc::mir::interpret::ConstValue; use rustc::traits::{self, ObligationCause, ObligationCauseCode, TraitEngine}; use rustc::ty::{ self, AdtKind, CanonicalUserType, Ty, TyCtxt, Const, GenericParamDefKind, @@ -1836,13 +1836,7 @@ fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: DefId, span: Span) // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is // the consumer's responsibility to ensure all bytes that have been read // have defined values. - let instance = ty::Instance::mono(tcx, id); - let cid = GlobalId { - instance, - promoted: None - }; - let param_env = ty::ParamEnv::reveal_all(); - if let Ok(static_) = tcx.const_eval(param_env.and(cid)) { + if let Ok(static_) = tcx.const_eval_poly(id) { let alloc = if let ty::ConstKind::Value(ConstValue::ByRef { alloc, .. }) = static_.val { alloc } else { diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 03fb0d9997822..93a1d5845ad28 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -15,7 +15,6 @@ use rustc::infer::region_constraints::{RegionConstraintData, Constraint}; use rustc::middle::resolve_lifetime as rl; use rustc::middle::lang_items; use rustc::middle::stability; -use rustc::mir::interpret::GlobalId; use rustc::hir; use rustc::hir::def::{CtorKind, DefKind, Res}; use rustc::hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; @@ -1341,13 +1340,7 @@ impl Clean for hir::Ty { TyKind::Slice(ref ty) => Slice(box ty.clean(cx)), TyKind::Array(ref ty, ref length) => { let def_id = cx.tcx.hir().local_def_id(length.hir_id); - let param_env = cx.tcx.param_env(def_id); - let substs = InternalSubsts::identity_for_item(cx.tcx, def_id); - let cid = GlobalId { - instance: ty::Instance::new(def_id, substs), - promoted: None - }; - let length = match cx.tcx.const_eval(param_env.and(cid)) { + let length = match cx.tcx.const_eval_poly(def_id) { Ok(length) => print_const(cx, length), Err(_) => cx.sess() .source_map() @@ -1541,16 +1534,7 @@ impl<'tcx> Clean for Ty<'tcx> { ty::Slice(ty) => Slice(box ty.clean(cx)), ty::Array(ty, n) => { let mut n = cx.tcx.lift(&n).expect("array lift failed"); - if let ty::ConstKind::Unevaluated(def_id, substs) = n.val { - let param_env = cx.tcx.param_env(def_id); - let cid = GlobalId { - instance: ty::Instance::new(def_id, substs), - promoted: None - }; - if let Ok(new_n) = cx.tcx.const_eval(param_env.and(cid)) { - n = new_n; - } - }; + n = n.eval(cx.tcx, ty::ParamEnv::reveal_all()); let n = print_const(cx, n); Array(box ty.clean(cx), n) }