diff options
Diffstat (limited to 'compiler/rustc_infer/src/infer/mod.rs')
-rw-r--r-- | compiler/rustc_infer/src/infer/mod.rs | 210 |
1 files changed, 89 insertions, 121 deletions
diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index ffb020398..706cd4124 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -10,6 +10,7 @@ pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog}; use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine, TraitEngineExt}; +use rustc_data_structures::fx::FxIndexMap; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_data_structures::sync::Lrc; use rustc_data_structures::undo_log::Rollback; @@ -22,7 +23,6 @@ use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKin use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; use rustc_middle::mir::ConstraintCategory; use rustc_middle::traits::select; -use rustc_middle::ty::abstract_const::{AbstractConst, FailureKind}; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::fold::BoundVarReplacerDelegate; use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; @@ -33,7 +33,7 @@ pub use rustc_middle::ty::IntVarValue; use rustc_middle::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt}; use rustc_middle::ty::{ConstVid, FloatVid, IntVid, TyVid}; use rustc_span::symbol::Symbol; -use rustc_span::{Span, DUMMY_SP}; +use rustc_span::Span; use std::cell::{Cell, RefCell}; use std::fmt; @@ -80,7 +80,6 @@ pub struct InferOk<'tcx, T> { } pub type InferResult<'tcx, T> = Result<InferOk<'tcx, T>, TypeError<'tcx>>; -pub type Bound<T> = Option<T>; pub type UnitResult<'tcx> = RelateResult<'tcx, ()>; // "unify result" pub type FixupResult<'tcx, T> = Result<T, FixupError<'tcx>>; // "fixup result" @@ -294,7 +293,7 @@ pub struct InferCtxt<'tcx> { /// the set of predicates on which errors have been reported, to /// avoid reporting the same error twice. - pub reported_trait_errors: RefCell<FxHashMap<Span, Vec<ty::Predicate<'tcx>>>>, + pub reported_trait_errors: RefCell<FxIndexMap<Span, Vec<ty::Predicate<'tcx>>>>, pub reported_closure_mismatch: RefCell<FxHashSet<(Span, Option<Span>)>>, @@ -334,8 +333,25 @@ pub struct InferCtxt<'tcx> { /// bound. universe: Cell<ty::UniverseIndex>, - normalize_fn_sig_for_diagnostic: - Option<Lrc<dyn Fn(&InferCtxt<'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>>, + /// During coherence we have to assume that other crates may add + /// additional impls which we currently don't know about. + /// + /// To deal with this evaluation should be conservative + /// and consider the possibility of impls from outside this crate. + /// This comes up primarily when resolving ambiguity. Imagine + /// there is some trait reference `$0: Bar` where `$0` is an + /// inference variable. If `intercrate` is true, then we can never + /// say for sure that this reference is not implemented, even if + /// there are *no impls at all for `Bar`*, because `$0` could be + /// bound to some type that in a downstream crate that implements + /// `Bar`. + /// + /// Outside of coherence we set this to false because we are only + /// interested in types that the user could actually have written. + /// In other words, we consider `$0: Bar` to be unimplemented if + /// there is no type that the user could *actually name* that + /// would satisfy it. This avoids crippling inference, basically. + pub intercrate: bool, } /// See the `error_reporting` module for more details. @@ -551,8 +567,8 @@ pub struct InferCtxtBuilder<'tcx> { tcx: TyCtxt<'tcx>, defining_use_anchor: DefiningAnchor, considering_regions: bool, - normalize_fn_sig_for_diagnostic: - Option<Lrc<dyn Fn(&InferCtxt<'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>>, + /// Whether we are in coherence mode. + intercrate: bool, } pub trait TyCtxtInferExt<'tcx> { @@ -565,7 +581,7 @@ impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { tcx: self, defining_use_anchor: DefiningAnchor::Error, considering_regions: true, - normalize_fn_sig_for_diagnostic: None, + intercrate: false, } } } @@ -582,16 +598,13 @@ impl<'tcx> InferCtxtBuilder<'tcx> { self } - pub fn ignoring_regions(mut self) -> Self { - self.considering_regions = false; + pub fn intercrate(mut self) -> Self { + self.intercrate = true; self } - pub fn with_normalize_fn_sig_for_diagnostic( - mut self, - fun: Lrc<dyn Fn(&InferCtxt<'tcx>, ty::PolyFnSig<'tcx>) -> ty::PolyFnSig<'tcx>>, - ) -> Self { - self.normalize_fn_sig_for_diagnostic = Some(fun); + pub fn ignoring_regions(mut self) -> Self { + self.considering_regions = false; self } @@ -616,12 +629,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { } pub fn build(&mut self) -> InferCtxt<'tcx> { - let InferCtxtBuilder { - tcx, - defining_use_anchor, - considering_regions, - ref normalize_fn_sig_for_diagnostic, - } = *self; + let InferCtxtBuilder { tcx, defining_use_anchor, considering_regions, intercrate } = *self; InferCtxt { tcx, defining_use_anchor, @@ -637,9 +645,7 @@ impl<'tcx> InferCtxtBuilder<'tcx> { in_snapshot: Cell::new(false), skip_leak_check: Cell::new(false), universe: Cell::new(ty::UniverseIndex::ROOT), - normalize_fn_sig_for_diagnostic: normalize_fn_sig_for_diagnostic - .as_ref() - .map(|f| f.clone()), + intercrate, } } } @@ -677,35 +683,14 @@ pub struct CombinedSnapshot<'tcx> { impl<'tcx> InferCtxt<'tcx> { /// Creates a `TypeErrCtxt` for emitting various inference errors. - /// During typeck, use `FnCtxt::infer_err` instead. + /// During typeck, use `FnCtxt::err_ctxt` instead. pub fn err_ctxt(&self) -> TypeErrCtxt<'_, 'tcx> { - TypeErrCtxt { infcx: self, typeck_results: None } - } - - /// calls `tcx.try_unify_abstract_consts` after - /// canonicalizing the consts. - #[instrument(skip(self), level = "debug")] - pub fn try_unify_abstract_consts( - &self, - a: ty::UnevaluatedConst<'tcx>, - b: ty::UnevaluatedConst<'tcx>, - param_env: ty::ParamEnv<'tcx>, - ) -> bool { - // Reject any attempt to unify two unevaluated constants that contain inference - // variables, since inference variables in queries lead to ICEs. - if a.substs.has_non_region_infer() - || b.substs.has_non_region_infer() - || param_env.has_non_region_infer() - { - debug!("a or b or param_env contain infer vars in its substs -> cannot unify"); - return false; + TypeErrCtxt { + infcx: self, + typeck_results: None, + fallback_has_occurred: false, + normalize_fn_sig: Box::new(|fn_sig| fn_sig), } - - let param_env_and = param_env.and((a, b)); - let erased = self.tcx.erase_regions(param_env_and); - debug!("after erase_regions: {:?}", erased); - - self.tcx.try_unify_abstract_consts(erased) } pub fn is_in_snapshot(&self) -> bool { @@ -777,32 +762,6 @@ impl<'tcx> InferCtxt<'tcx> { } } - /// Clear the "currently in a snapshot" flag, invoke the closure, - /// then restore the flag to its original value. This flag is a - /// debugging measure designed to detect cases where we start a - /// snapshot, create type variables, and register obligations - /// which may involve those type variables in the fulfillment cx, - /// potentially leaving "dangling type variables" behind. - /// In such cases, an assertion will fail when attempting to - /// register obligations, within a snapshot. Very useful, much - /// better than grovelling through megabytes of `RUSTC_LOG` output. - /// - /// HOWEVER, in some cases the flag is unhelpful. In particular, we - /// sometimes create a "mini-fulfilment-cx" in which we enroll - /// obligations. As long as this fulfillment cx is fully drained - /// before we return, this is not a problem, as there won't be any - /// escaping obligations in the main cx. In those cases, you can - /// use this function. - pub fn save_and_restore_in_snapshot_flag<F, R>(&self, func: F) -> R - where - F: FnOnce(&Self) -> R, - { - let flag = self.in_snapshot.replace(false); - let result = func(self); - self.in_snapshot.set(flag); - result - } - fn start_snapshot(&self) -> CombinedSnapshot<'tcx> { debug!("start_snapshot()"); @@ -1087,7 +1046,7 @@ impl<'tcx> InferCtxt<'tcx> { } pub fn next_const_var(&self, ty: Ty<'tcx>, origin: ConstVariableOrigin) -> ty::Const<'tcx> { - self.tcx.mk_const_var(self.next_const_var_id(origin), ty) + self.tcx.mk_const(self.next_const_var_id(origin), ty) } pub fn next_const_var_in_universe( @@ -1101,7 +1060,7 @@ impl<'tcx> InferCtxt<'tcx> { .borrow_mut() .const_unification_table() .new_key(ConstVarValue { origin, val: ConstVariableValue::Unknown { universe } }); - self.tcx.mk_const_var(vid, ty) + self.tcx.mk_const(vid, ty) } pub fn next_const_var_id(&self, origin: ConstVariableOrigin) -> ConstVid<'tcx> { @@ -1217,7 +1176,7 @@ impl<'tcx> InferCtxt<'tcx> { origin, val: ConstVariableValue::Unknown { universe: self.universe() }, }); - self.tcx.mk_const_var(const_var_id, self.tcx.type_of(param.def_id)).into() + self.tcx.mk_const(const_var_id, self.tcx.type_of(param.def_id)).into() } } } @@ -1233,7 +1192,8 @@ impl<'tcx> InferCtxt<'tcx> { /// reporting errors that often occur as a result of earlier /// errors, but where it's hard to be 100% sure (e.g., unresolved /// inference variables, regionck errors). - pub fn is_tainted_by_errors(&self) -> bool { + #[must_use = "this method does not have any side effects"] + pub fn tainted_by_errors(&self) -> Option<ErrorGuaranteed> { debug!( "is_tainted_by_errors(err_count={}, err_count_on_creation={}, \ tainted_by_errors={})", @@ -1242,19 +1202,25 @@ impl<'tcx> InferCtxt<'tcx> { self.tainted_by_errors.get().is_some() ); + if let Some(e) = self.tainted_by_errors.get() { + return Some(e); + } + if self.tcx.sess.err_count() > self.err_count_on_creation { - return true; // errors reported since this infcx was made + // errors reported since this infcx was made + let e = self.tcx.sess.has_errors().unwrap(); + self.set_tainted_by_errors(e); + return Some(e); } - self.tainted_by_errors.get().is_some() + + None } /// Set the "tainted by errors" flag to true. We call this when we /// observe an error from a prior pass. - pub fn set_tainted_by_errors(&self) { - debug!("set_tainted_by_errors()"); - self.tainted_by_errors.set(Some( - self.tcx.sess.delay_span_bug(DUMMY_SP, "`InferCtxt` incorrectly tainted by errors"), - )); + pub fn set_tainted_by_errors(&self, e: ErrorGuaranteed) { + debug!("set_tainted_by_errors(ErrorGuaranteed)"); + self.tainted_by_errors.set(Some(e)); } pub fn skip_region_resolution(&self) { @@ -1295,7 +1261,7 @@ impl<'tcx> InferCtxt<'tcx> { let mut inner = self.inner.borrow_mut(); let inner = &mut *inner; assert!( - self.is_tainted_by_errors() || inner.region_obligations.is_empty(), + self.tainted_by_errors().is_some() || inner.region_obligations.is_empty(), "region_obligations not empty: {:#?}", inner.region_obligations ); @@ -1436,16 +1402,15 @@ impl<'tcx> InferCtxt<'tcx> { value.fold_with(&mut r) } - /// Returns the first unresolved variable contained in `T`. In the - /// process of visiting `T`, this will resolve (where possible) - /// type variables in `T`, but it never constructs the final, - /// resolved type, so it's more efficient than - /// `resolve_vars_if_possible()`. - pub fn unresolved_type_vars<T>(&self, value: &T) -> Option<(Ty<'tcx>, Option<Span>)> + /// Returns the first unresolved type or const variable contained in `T`. + pub fn first_unresolved_const_or_ty_var<T>( + &self, + value: &T, + ) -> Option<(ty::Term<'tcx>, Option<Span>)> where T: TypeVisitable<'tcx>, { - value.visit_with(&mut resolve::UnresolvedTypeFinder::new(self)).break_value() + value.visit_with(&mut resolve::UnresolvedTypeOrConstFinder::new(self)).break_value() } pub fn probe_const_var( @@ -1596,7 +1561,7 @@ impl<'tcx> InferCtxt<'tcx> { span: Option<Span>, ) -> Result<ty::Const<'tcx>, ErrorHandled> { match self.const_eval_resolve(param_env, unevaluated, span) { - Ok(Some(val)) => Ok(ty::Const::from_value(self.tcx, val, ty)), + Ok(Some(val)) => Ok(self.tcx.mk_const(val, ty)), Ok(None) => { let tcx = self.tcx; let def_id = unevaluated.def.did; @@ -1634,26 +1599,25 @@ impl<'tcx> InferCtxt<'tcx> { // Postpone the evaluation of constants whose substs depend on inference // variables + let tcx = self.tcx; if substs.has_non_region_infer() { - let ac = AbstractConst::new(self.tcx, unevaluated); - match ac { - Ok(None) => { - substs = InternalSubsts::identity_for_item(self.tcx, unevaluated.def.did); - param_env = self.tcx.param_env(unevaluated.def.did); - } - Ok(Some(ct)) => { - if ct.unify_failure_kind(self.tcx) == FailureKind::Concrete { - substs = replace_param_and_infer_substs_with_placeholder(self.tcx, substs); - } else { - return Err(ErrorHandled::TooGeneric); - } + if let Some(ct) = tcx.bound_abstract_const(unevaluated.def)? { + let ct = tcx.expand_abstract_consts(ct.subst(tcx, substs)); + if let Err(e) = ct.error_reported() { + return Err(ErrorHandled::Reported(e)); + } else if ct.has_non_region_infer() || ct.has_non_region_param() { + return Err(ErrorHandled::TooGeneric); + } else { + substs = replace_param_and_infer_substs_with_placeholder(tcx, substs); } - Err(guar) => return Err(ErrorHandled::Reported(guar)), + } else { + substs = InternalSubsts::identity_for_item(tcx, unevaluated.def.did); + param_env = tcx.param_env(unevaluated.def.did); } } - let param_env_erased = self.tcx.erase_regions(param_env); - let substs_erased = self.tcx.erase_regions(substs); + let param_env_erased = tcx.erase_regions(param_env); + let substs_erased = tcx.erase_regions(substs); debug!(?param_env_erased); debug!(?substs_erased); @@ -1661,7 +1625,7 @@ impl<'tcx> InferCtxt<'tcx> { // The return value is the evaluated value which doesn't contain any reference to inference // variables, thus we don't need to substitute back the original values. - self.tcx.const_eval_resolve_for_typeck(param_env_erased, unevaluated, span) + tcx.const_eval_resolve_for_typeck(param_env_erased, unevaluated, span) } /// `ty_or_const_infer_var_changed` is equivalent to one of these two: @@ -1729,10 +1693,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { &self, generic_param_scope: LocalDefId, outlives_env: &OutlivesEnvironment<'tcx>, - ) { + ) -> Option<ErrorGuaranteed> { let errors = self.resolve_regions(outlives_env); - if !self.is_tainted_by_errors() { + if let None = self.tainted_by_errors() { // As a heuristic, just skip reporting region errors // altogether if other errors have been reported while // this infcx was in use. This is totally hokey but @@ -1740,6 +1704,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // errors from silly ones. self.report_region_errors(generic_param_scope, &errors); } + + (!errors.is_empty()).then(|| { + self.tcx.sess.delay_span_bug(rustc_span::DUMMY_SP, "error should have been emitted") + }) } // [Note-Type-error-reporting] @@ -2065,13 +2033,13 @@ fn replace_param_and_infer_substs_with_placeholder<'tcx>( if ty.has_non_region_param() || ty.has_non_region_infer() { bug!("const `{ct}`'s type should not reference params or types"); } - tcx.mk_const(ty::ConstS { - ty, - kind: ty::ConstKind::Placeholder(ty::PlaceholderConst { + tcx.mk_const( + ty::PlaceholderConst { universe: ty::UniverseIndex::ROOT, name: ty::BoundVar::from_usize(idx), - }), - }) + }, + ty, + ) .into() } _ => arg, |