summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_infer/src/infer/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_infer/src/infer/mod.rs')
-rw-r--r--compiler/rustc_infer/src/infer/mod.rs210
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,