diff options
Diffstat (limited to '')
47 files changed, 20058 insertions, 0 deletions
diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs new file mode 100644 index 000000000..130214a65 --- /dev/null +++ b/compiler/rustc_infer/src/infer/at.rs @@ -0,0 +1,429 @@ +//! A nice interface for working with the infcx. The basic idea is to +//! do `infcx.at(cause, param_env)`, which sets the "cause" of the +//! operation as well as the surrounding parameter environment. Then +//! you can do something like `.sub(a, b)` or `.eq(a, b)` to create a +//! subtype or equality relationship respectively. The first argument +//! is always the "expected" output from the POV of diagnostics. +//! +//! Examples: +//! ```ignore (fragment) +//! infcx.at(cause, param_env).sub(a, b) +//! // requires that `a <: b`, with `a` considered the "expected" type +//! +//! infcx.at(cause, param_env).sup(a, b) +//! // requires that `b <: a`, with `a` considered the "expected" type +//! +//! infcx.at(cause, param_env).eq(a, b) +//! // requires that `a == b`, with `a` considered the "expected" type +//! ``` +//! For finer-grained control, you can also do use `trace`: +//! ```ignore (fragment) +//! infcx.at(...).trace(a, b).sub(&c, &d) +//! ``` +//! This will set `a` and `b` as the "root" values for +//! error-reporting, but actually operate on `c` and `d`. This is +//! sometimes useful when the types of `c` and `d` are not traceable +//! things. (That system should probably be refactored.) + +use super::*; + +use rustc_middle::ty::relate::{Relate, TypeRelation}; +use rustc_middle::ty::{Const, ImplSubject}; + +pub struct At<'a, 'tcx> { + pub infcx: &'a InferCtxt<'a, 'tcx>, + pub cause: &'a ObligationCause<'tcx>, + pub param_env: ty::ParamEnv<'tcx>, + /// Whether we should define opaque types + /// or just treat them opaquely. + /// Currently only used to prevent predicate + /// matching from matching anything against opaque + /// types. + pub define_opaque_types: bool, +} + +pub struct Trace<'a, 'tcx> { + at: At<'a, 'tcx>, + a_is_expected: bool, + trace: TypeTrace<'tcx>, +} + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + #[inline] + pub fn at( + &'a self, + cause: &'a ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> At<'a, 'tcx> { + At { infcx: self, cause, param_env, define_opaque_types: true } + } + + /// Forks the inference context, creating a new inference context with the same inference + /// variables in the same state. This can be used to "branch off" many tests from the same + /// common state. Used in coherence. + pub fn fork(&self) -> Self { + Self { + tcx: self.tcx, + defining_use_anchor: self.defining_use_anchor, + considering_regions: self.considering_regions, + in_progress_typeck_results: self.in_progress_typeck_results, + inner: self.inner.clone(), + skip_leak_check: self.skip_leak_check.clone(), + lexical_region_resolutions: self.lexical_region_resolutions.clone(), + selection_cache: self.selection_cache.clone(), + evaluation_cache: self.evaluation_cache.clone(), + reported_trait_errors: self.reported_trait_errors.clone(), + reported_closure_mismatch: self.reported_closure_mismatch.clone(), + tainted_by_errors_flag: self.tainted_by_errors_flag.clone(), + err_count_on_creation: self.err_count_on_creation, + in_snapshot: self.in_snapshot.clone(), + universe: self.universe.clone(), + } + } +} + +pub trait ToTrace<'tcx>: Relate<'tcx> + Copy { + fn to_trace( + tcx: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx>; +} + +impl<'a, 'tcx> At<'a, 'tcx> { + pub fn define_opaque_types(self, define_opaque_types: bool) -> Self { + Self { define_opaque_types, ..self } + } + + /// Hacky routine for equating two impl headers in coherence. + pub fn eq_impl_headers( + self, + expected: &ty::ImplHeader<'tcx>, + actual: &ty::ImplHeader<'tcx>, + ) -> InferResult<'tcx, ()> { + debug!("eq_impl_header({:?} = {:?})", expected, actual); + match (expected.trait_ref, actual.trait_ref) { + (Some(a_ref), Some(b_ref)) => self.eq(a_ref, b_ref), + (None, None) => self.eq(expected.self_ty, actual.self_ty), + _ => bug!("mk_eq_impl_headers given mismatched impl kinds"), + } + } + + /// Makes `a <: b`, where `a` may or may not be expected. + /// + /// See [`At::trace_exp`] and [`Trace::sub`] for a version of + /// this method that only requires `T: Relate<'tcx>` + pub fn sub_exp<T>(self, a_is_expected: bool, a: T, b: T) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + self.trace_exp(a_is_expected, a, b).sub(a, b) + } + + /// Makes `actual <: expected`. For example, if type-checking a + /// call like `foo(x)`, where `foo: fn(i32)`, you might have + /// `sup(i32, x)`, since the "expected" type is the type that + /// appears in the signature. + /// + /// See [`At::trace`] and [`Trace::sub`] for a version of + /// this method that only requires `T: Relate<'tcx>` + pub fn sup<T>(self, expected: T, actual: T) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + self.sub_exp(false, actual, expected) + } + + /// Makes `expected <: actual`. + /// + /// See [`At::trace`] and [`Trace::sub`] for a version of + /// this method that only requires `T: Relate<'tcx>` + pub fn sub<T>(self, expected: T, actual: T) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + self.sub_exp(true, expected, actual) + } + + /// Makes `expected <: actual`. + /// + /// See [`At::trace_exp`] and [`Trace::eq`] for a version of + /// this method that only requires `T: Relate<'tcx>` + pub fn eq_exp<T>(self, a_is_expected: bool, a: T, b: T) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + self.trace_exp(a_is_expected, a, b).eq(a, b) + } + + /// Makes `expected <: actual`. + /// + /// See [`At::trace`] and [`Trace::eq`] for a version of + /// this method that only requires `T: Relate<'tcx>` + pub fn eq<T>(self, expected: T, actual: T) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + self.trace(expected, actual).eq(expected, actual) + } + + pub fn relate<T>(self, expected: T, variance: ty::Variance, actual: T) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + match variance { + ty::Variance::Covariant => self.sub(expected, actual), + ty::Variance::Invariant => self.eq(expected, actual), + ty::Variance::Contravariant => self.sup(expected, actual), + + // We could make this make sense but it's not readily + // exposed and I don't feel like dealing with it. Note + // that bivariance in general does a bit more than just + // *nothing*, it checks that the types are the same + // "modulo variance" basically. + ty::Variance::Bivariant => panic!("Bivariant given to `relate()`"), + } + } + + /// Computes the least-upper-bound, or mutual supertype, of two + /// values. The order of the arguments doesn't matter, but since + /// this can result in an error (e.g., if asked to compute LUB of + /// u32 and i32), it is meaningful to call one of them the + /// "expected type". + /// + /// See [`At::trace`] and [`Trace::lub`] for a version of + /// this method that only requires `T: Relate<'tcx>` + pub fn lub<T>(self, expected: T, actual: T) -> InferResult<'tcx, T> + where + T: ToTrace<'tcx>, + { + self.trace(expected, actual).lub(expected, actual) + } + + /// Computes the greatest-lower-bound, or mutual subtype, of two + /// values. As with `lub` order doesn't matter, except for error + /// cases. + /// + /// See [`At::trace`] and [`Trace::glb`] for a version of + /// this method that only requires `T: Relate<'tcx>` + pub fn glb<T>(self, expected: T, actual: T) -> InferResult<'tcx, T> + where + T: ToTrace<'tcx>, + { + self.trace(expected, actual).glb(expected, actual) + } + + /// Sets the "trace" values that will be used for + /// error-reporting, but doesn't actually perform any operation + /// yet (this is useful when you want to set the trace using + /// distinct values from those you wish to operate upon). + pub fn trace<T>(self, expected: T, actual: T) -> Trace<'a, 'tcx> + where + T: ToTrace<'tcx>, + { + self.trace_exp(true, expected, actual) + } + + /// Like `trace`, but the expected value is determined by the + /// boolean argument (if true, then the first argument `a` is the + /// "expected" value). + pub fn trace_exp<T>(self, a_is_expected: bool, a: T, b: T) -> Trace<'a, 'tcx> + where + T: ToTrace<'tcx>, + { + let trace = ToTrace::to_trace(self.infcx.tcx, self.cause, a_is_expected, a, b); + Trace { at: self, trace, a_is_expected } + } +} + +impl<'a, 'tcx> Trace<'a, 'tcx> { + /// Makes `a <: b` where `a` may or may not be expected (if + /// `a_is_expected` is true, then `a` is expected). + #[instrument(skip(self), level = "debug")] + pub fn sub<T>(self, a: T, b: T) -> InferResult<'tcx, ()> + where + T: Relate<'tcx>, + { + let Trace { at, trace, a_is_expected } = self; + at.infcx.commit_if_ok(|_| { + let mut fields = at.infcx.combine_fields(trace, at.param_env, at.define_opaque_types); + fields + .sub(a_is_expected) + .relate(a, b) + .map(move |_| InferOk { value: (), obligations: fields.obligations }) + }) + } + + /// Makes `a == b`; the expectation is set by the call to + /// `trace()`. + #[instrument(skip(self), level = "debug")] + pub fn eq<T>(self, a: T, b: T) -> InferResult<'tcx, ()> + where + T: Relate<'tcx>, + { + let Trace { at, trace, a_is_expected } = self; + at.infcx.commit_if_ok(|_| { + let mut fields = at.infcx.combine_fields(trace, at.param_env, at.define_opaque_types); + fields + .equate(a_is_expected) + .relate(a, b) + .map(move |_| InferOk { value: (), obligations: fields.obligations }) + }) + } + + #[instrument(skip(self), level = "debug")] + pub fn lub<T>(self, a: T, b: T) -> InferResult<'tcx, T> + where + T: Relate<'tcx>, + { + let Trace { at, trace, a_is_expected } = self; + at.infcx.commit_if_ok(|_| { + let mut fields = at.infcx.combine_fields(trace, at.param_env, at.define_opaque_types); + fields + .lub(a_is_expected) + .relate(a, b) + .map(move |t| InferOk { value: t, obligations: fields.obligations }) + }) + } + + #[instrument(skip(self), level = "debug")] + pub fn glb<T>(self, a: T, b: T) -> InferResult<'tcx, T> + where + T: Relate<'tcx>, + { + let Trace { at, trace, a_is_expected } = self; + at.infcx.commit_if_ok(|_| { + let mut fields = at.infcx.combine_fields(trace, at.param_env, at.define_opaque_types); + fields + .glb(a_is_expected) + .relate(a, b) + .map(move |t| InferOk { value: t, obligations: fields.obligations }) + }) + } +} + +impl<'tcx> ToTrace<'tcx> for ImplSubject<'tcx> { + fn to_trace( + tcx: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + match (a, b) { + (ImplSubject::Trait(trait_ref_a), ImplSubject::Trait(trait_ref_b)) => { + ToTrace::to_trace(tcx, cause, a_is_expected, trait_ref_a, trait_ref_b) + } + (ImplSubject::Inherent(ty_a), ImplSubject::Inherent(ty_b)) => { + ToTrace::to_trace(tcx, cause, a_is_expected, ty_a, ty_b) + } + (ImplSubject::Trait(_), ImplSubject::Inherent(_)) + | (ImplSubject::Inherent(_), ImplSubject::Trait(_)) => { + bug!("can not trace TraitRef and Ty"); + } + } + } +} + +impl<'tcx> ToTrace<'tcx> for Ty<'tcx> { + fn to_trace( + _: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + TypeTrace { + cause: cause.clone(), + values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + } + } +} + +impl<'tcx> ToTrace<'tcx> for ty::Region<'tcx> { + fn to_trace( + _: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + TypeTrace { cause: cause.clone(), values: Regions(ExpectedFound::new(a_is_expected, a, b)) } + } +} + +impl<'tcx> ToTrace<'tcx> for Const<'tcx> { + fn to_trace( + _: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + TypeTrace { + cause: cause.clone(), + values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + } + } +} + +impl<'tcx> ToTrace<'tcx> for ty::Term<'tcx> { + fn to_trace( + _: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + TypeTrace { cause: cause.clone(), values: Terms(ExpectedFound::new(a_is_expected, a, b)) } + } +} + +impl<'tcx> ToTrace<'tcx> for ty::TraitRef<'tcx> { + fn to_trace( + _: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + TypeTrace { + cause: cause.clone(), + values: TraitRefs(ExpectedFound::new(a_is_expected, a, b)), + } + } +} + +impl<'tcx> ToTrace<'tcx> for ty::PolyTraitRef<'tcx> { + fn to_trace( + _: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + TypeTrace { + cause: cause.clone(), + values: PolyTraitRefs(ExpectedFound::new(a_is_expected, a, b)), + } + } +} + +impl<'tcx> ToTrace<'tcx> for ty::ProjectionTy<'tcx> { + fn to_trace( + tcx: TyCtxt<'tcx>, + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Self, + b: Self, + ) -> TypeTrace<'tcx> { + let a_ty = tcx.mk_projection(a.item_def_id, a.substs); + let b_ty = tcx.mk_projection(b.item_def_id, b.substs); + TypeTrace { + cause: cause.clone(), + values: Terms(ExpectedFound::new(a_is_expected, a_ty.into(), b_ty.into())), + } + } +} diff --git a/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs new file mode 100644 index 000000000..ca7862c9d --- /dev/null +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -0,0 +1,791 @@ +//! This module contains the "canonicalizer" itself. +//! +//! For an overview of what canonicalization is and how it fits into +//! rustc, check out the [chapter in the rustc dev guide][c]. +//! +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html + +use crate::infer::canonical::{ + Canonical, CanonicalTyVarKind, CanonicalVarInfo, CanonicalVarKind, Canonicalized, + OriginalQueryValues, +}; +use crate::infer::InferCtxt; +use rustc_middle::ty::flags::FlagComputation; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, BoundVar, InferConst, List, Ty, TyCtxt, TypeFlags}; +use std::sync::atomic::Ordering; + +use rustc_data_structures::fx::FxHashMap; +use rustc_index::vec::Idx; +use smallvec::SmallVec; + +impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { + /// Canonicalizes a query value `V`. When we canonicalize a query, + /// we not only canonicalize unbound inference variables, but we + /// *also* replace all free regions whatsoever. So for example a + /// query like `T: Trait<'static>` would be canonicalized to + /// + /// ```text + /// T: Trait<'?0> + /// ``` + /// + /// with a mapping M that maps `'?0` to `'static`. + /// + /// To get a good understanding of what is happening here, check + /// out the [chapter in the rustc dev guide][c]. + /// + /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query + pub fn canonicalize_query<V>( + &self, + value: V, + query_state: &mut OriginalQueryValues<'tcx>, + ) -> Canonicalized<'tcx, V> + where + V: TypeFoldable<'tcx>, + { + self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed); + + Canonicalizer::canonicalize(value, self, self.tcx, &CanonicalizeAllFreeRegions, query_state) + } + + /// Like [Self::canonicalize_query], but preserves distinct universes. For + /// example, canonicalizing `&'?0: Trait<'?1>`, where `'?0` is in `U1` and + /// `'?1` is in `U3` would be canonicalized to have ?0` in `U1` and `'?1` + /// in `U2`. + /// + /// This is used for Chalk integration. + pub fn canonicalize_query_preserving_universes<V>( + &self, + value: V, + query_state: &mut OriginalQueryValues<'tcx>, + ) -> Canonicalized<'tcx, V> + where + V: TypeFoldable<'tcx>, + { + self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed); + + Canonicalizer::canonicalize( + value, + self, + self.tcx, + &CanonicalizeAllFreeRegionsPreservingUniverses, + query_state, + ) + } + + /// Canonicalizes a query *response* `V`. When we canonicalize a + /// query response, we only canonicalize unbound inference + /// variables, and we leave other free regions alone. So, + /// continuing with the example from `canonicalize_query`, if + /// there was an input query `T: Trait<'static>`, it would have + /// been canonicalized to + /// + /// ```text + /// T: Trait<'?0> + /// ``` + /// + /// with a mapping M that maps `'?0` to `'static`. But if we found that there + /// exists only one possible impl of `Trait`, and it looks like + /// ```ignore (illustrative) + /// impl<T> Trait<'static> for T { .. } + /// ``` + /// then we would prepare a query result R that (among other + /// things) includes a mapping to `'?0 := 'static`. When + /// canonicalizing this query result R, we would leave this + /// reference to `'static` alone. + /// + /// To get a good understanding of what is happening here, check + /// out the [chapter in the rustc dev guide][c]. + /// + /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#canonicalizing-the-query-result + pub fn canonicalize_response<V>(&self, value: V) -> Canonicalized<'tcx, V> + where + V: TypeFoldable<'tcx>, + { + let mut query_state = OriginalQueryValues::default(); + Canonicalizer::canonicalize( + value, + self, + self.tcx, + &CanonicalizeQueryResponse, + &mut query_state, + ) + } + + pub fn canonicalize_user_type_annotation<V>(&self, value: V) -> Canonicalized<'tcx, V> + where + V: TypeFoldable<'tcx>, + { + let mut query_state = OriginalQueryValues::default(); + Canonicalizer::canonicalize( + value, + self, + self.tcx, + &CanonicalizeUserTypeAnnotation, + &mut query_state, + ) + } + + /// A variant of `canonicalize_query` that does not + /// canonicalize `'static`. This is useful when + /// the query implementation can perform more efficient + /// handling of `'static` regions (e.g. trait evaluation). + pub fn canonicalize_query_keep_static<V>( + &self, + value: V, + query_state: &mut OriginalQueryValues<'tcx>, + ) -> Canonicalized<'tcx, V> + where + V: TypeFoldable<'tcx>, + { + self.tcx.sess.perf_stats.queries_canonicalized.fetch_add(1, Ordering::Relaxed); + + Canonicalizer::canonicalize( + value, + self, + self.tcx, + &CanonicalizeFreeRegionsOtherThanStatic, + query_state, + ) + } +} + +/// Controls how we canonicalize "free regions" that are not inference +/// variables. This depends on what we are canonicalizing *for* -- +/// e.g., if we are canonicalizing to create a query, we want to +/// replace those with inference variables, since we want to make a +/// maximally general query. But if we are canonicalizing a *query +/// response*, then we don't typically replace free regions, as they +/// must have been introduced from other parts of the system. +trait CanonicalizeMode { + fn canonicalize_free_region<'tcx>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx>; + + fn any(&self) -> bool; + + // Do we preserve universe of variables. + fn preserve_universes(&self) -> bool; +} + +struct CanonicalizeQueryResponse; + +impl CanonicalizeMode for CanonicalizeQueryResponse { + fn canonicalize_free_region<'tcx>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + match *r { + ty::ReFree(_) + | ty::ReErased + | ty::ReStatic + | ty::ReEmpty(ty::UniverseIndex::ROOT) + | ty::ReEarlyBound(..) => r, + + ty::RePlaceholder(placeholder) => canonicalizer.canonical_var_for_region( + CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderRegion(placeholder) }, + r, + ), + + ty::ReVar(vid) => { + let universe = canonicalizer.region_var_universe(vid); + canonicalizer.canonical_var_for_region( + CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) }, + r, + ) + } + + ty::ReEmpty(ui) => { + bug!("canonicalizing 'empty in universe {:?}", ui) // FIXME + } + + _ => { + // Other than `'static` or `'empty`, the query + // response should be executing in a fully + // canonicalized environment, so there shouldn't be + // any other region names it can come up. + // + // rust-lang/rust#57464: `impl Trait` can leak local + // scopes (in manner violating typeck). Therefore, use + // `delay_span_bug` to allow type error over an ICE. + ty::tls::with(|tcx| { + tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + &format!("unexpected region in query response: `{:?}`", r), + ); + }); + r + } + } + } + + fn any(&self) -> bool { + false + } + + fn preserve_universes(&self) -> bool { + true + } +} + +struct CanonicalizeUserTypeAnnotation; + +impl CanonicalizeMode for CanonicalizeUserTypeAnnotation { + fn canonicalize_free_region<'tcx>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + match *r { + ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReErased | ty::ReStatic => r, + ty::ReVar(_) => canonicalizer.canonical_var_for_region_in_root_universe(r), + _ => { + // We only expect region names that the user can type. + bug!("unexpected region in query response: `{:?}`", r) + } + } + } + + fn any(&self) -> bool { + false + } + + fn preserve_universes(&self) -> bool { + false + } +} + +struct CanonicalizeAllFreeRegions; + +impl CanonicalizeMode for CanonicalizeAllFreeRegions { + fn canonicalize_free_region<'tcx>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + canonicalizer.canonical_var_for_region_in_root_universe(r) + } + + fn any(&self) -> bool { + true + } + + fn preserve_universes(&self) -> bool { + false + } +} + +struct CanonicalizeAllFreeRegionsPreservingUniverses; + +impl CanonicalizeMode for CanonicalizeAllFreeRegionsPreservingUniverses { + fn canonicalize_free_region<'tcx>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + let universe = canonicalizer.infcx.universe_of_region(r); + canonicalizer.canonical_var_for_region( + CanonicalVarInfo { kind: CanonicalVarKind::Region(universe) }, + r, + ) + } + + fn any(&self) -> bool { + true + } + + fn preserve_universes(&self) -> bool { + true + } +} + +struct CanonicalizeFreeRegionsOtherThanStatic; + +impl CanonicalizeMode for CanonicalizeFreeRegionsOtherThanStatic { + fn canonicalize_free_region<'tcx>( + &self, + canonicalizer: &mut Canonicalizer<'_, 'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + if r.is_static() { r } else { canonicalizer.canonical_var_for_region_in_root_universe(r) } + } + + fn any(&self) -> bool { + true + } + + fn preserve_universes(&self) -> bool { + false + } +} + +struct Canonicalizer<'cx, 'tcx> { + infcx: &'cx InferCtxt<'cx, 'tcx>, + tcx: TyCtxt<'tcx>, + variables: SmallVec<[CanonicalVarInfo<'tcx>; 8]>, + query_state: &'cx mut OriginalQueryValues<'tcx>, + // Note that indices is only used once `var_values` is big enough to be + // heap-allocated. + indices: FxHashMap<GenericArg<'tcx>, BoundVar>, + canonicalize_mode: &'cx dyn CanonicalizeMode, + needs_canonical_flags: TypeFlags, + + binder_index: ty::DebruijnIndex, +} + +impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_binder<T>(&mut self, t: ty::Binder<'tcx, T>) -> ty::Binder<'tcx, T> + where + T: TypeFoldable<'tcx>, + { + self.binder_index.shift_in(1); + let t = t.super_fold_with(self); + self.binder_index.shift_out(1); + t + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + ty::ReLateBound(index, ..) => { + if index >= self.binder_index { + bug!("escaping late-bound region during canonicalization"); + } else { + r + } + } + + ty::ReVar(vid) => { + let resolved_vid = self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(vid); + debug!( + "canonical: region var found with vid {:?}, \ + opportunistically resolved to {:?}", + vid, r + ); + let r = self.tcx.reuse_or_mk_region(r, ty::ReVar(resolved_vid)); + self.canonicalize_mode.canonicalize_free_region(self, r) + } + + ty::ReStatic + | ty::ReEarlyBound(..) + | ty::ReFree(_) + | ty::ReEmpty(_) + | ty::RePlaceholder(..) + | ty::ReErased => self.canonicalize_mode.canonicalize_free_region(self, r), + } + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match *t.kind() { + ty::Infer(ty::TyVar(vid)) => { + debug!("canonical: type var found with vid {:?}", vid); + match self.infcx.probe_ty_var(vid) { + // `t` could be a float / int variable; canonicalize that instead. + Ok(t) => { + debug!("(resolved to {:?})", t); + self.fold_ty(t) + } + + // `TyVar(vid)` is unresolved, track its universe index in the canonicalized + // result. + Err(mut ui) => { + if !self.canonicalize_mode.preserve_universes() { + // FIXME: perf problem described in #55921. + ui = ty::UniverseIndex::ROOT; + } + self.canonicalize_ty_var( + CanonicalVarInfo { + kind: CanonicalVarKind::Ty(CanonicalTyVarKind::General(ui)), + }, + t, + ) + } + } + } + + ty::Infer(ty::IntVar(_)) => self.canonicalize_ty_var( + CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Int) }, + t, + ), + + ty::Infer(ty::FloatVar(_)) => self.canonicalize_ty_var( + CanonicalVarInfo { kind: CanonicalVarKind::Ty(CanonicalTyVarKind::Float) }, + t, + ), + + ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { + bug!("encountered a fresh type during canonicalization") + } + + ty::Placeholder(placeholder) => self.canonicalize_ty_var( + CanonicalVarInfo { kind: CanonicalVarKind::PlaceholderTy(placeholder) }, + t, + ), + + ty::Bound(debruijn, _) => { + if debruijn >= self.binder_index { + bug!("escaping bound type during canonicalization") + } else { + t + } + } + + ty::Closure(..) + | ty::Generator(..) + | ty::GeneratorWitness(..) + | ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Adt(..) + | ty::Str + | ty::Error(_) + | ty::Array(..) + | ty::Slice(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Dynamic(..) + | ty::Never + | ty::Tuple(..) + | ty::Projection(..) + | ty::Foreign(..) + | ty::Param(..) + | ty::Opaque(..) => { + if t.flags().intersects(self.needs_canonical_flags) { + t.super_fold_with(self) + } else { + t + } + } + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + match ct.kind() { + ty::ConstKind::Infer(InferConst::Var(vid)) => { + debug!("canonical: const var found with vid {:?}", vid); + match self.infcx.probe_const_var(vid) { + Ok(c) => { + debug!("(resolved to {:?})", c); + return self.fold_const(c); + } + + // `ConstVar(vid)` is unresolved, track its universe index in the + // canonicalized result + Err(mut ui) => { + if !self.canonicalize_mode.preserve_universes() { + // FIXME: perf problem described in #55921. + ui = ty::UniverseIndex::ROOT; + } + return self.canonicalize_const_var( + CanonicalVarInfo { kind: CanonicalVarKind::Const(ui, ct.ty()) }, + ct, + ); + } + } + } + ty::ConstKind::Infer(InferConst::Fresh(_)) => { + bug!("encountered a fresh const during canonicalization") + } + ty::ConstKind::Bound(debruijn, _) => { + if debruijn >= self.binder_index { + bug!("escaping bound type during canonicalization") + } else { + return ct; + } + } + ty::ConstKind::Placeholder(placeholder) => { + return self.canonicalize_const_var( + CanonicalVarInfo { + kind: CanonicalVarKind::PlaceholderConst(placeholder, ct.ty()), + }, + ct, + ); + } + _ => {} + } + + let flags = FlagComputation::for_const(ct); + if flags.intersects(self.needs_canonical_flags) { ct.super_fold_with(self) } else { ct } + } +} + +impl<'cx, 'tcx> Canonicalizer<'cx, 'tcx> { + /// The main `canonicalize` method, shared impl of + /// `canonicalize_query` and `canonicalize_response`. + fn canonicalize<V>( + value: V, + infcx: &InferCtxt<'_, 'tcx>, + tcx: TyCtxt<'tcx>, + canonicalize_region_mode: &dyn CanonicalizeMode, + query_state: &mut OriginalQueryValues<'tcx>, + ) -> Canonicalized<'tcx, V> + where + V: TypeFoldable<'tcx>, + { + let needs_canonical_flags = if canonicalize_region_mode.any() { + TypeFlags::NEEDS_INFER | + TypeFlags::HAS_FREE_REGIONS | // `HAS_RE_PLACEHOLDER` implies `HAS_FREE_REGIONS` + TypeFlags::HAS_TY_PLACEHOLDER | + TypeFlags::HAS_CT_PLACEHOLDER + } else { + TypeFlags::NEEDS_INFER + | TypeFlags::HAS_RE_PLACEHOLDER + | TypeFlags::HAS_TY_PLACEHOLDER + | TypeFlags::HAS_CT_PLACEHOLDER + }; + + // Fast path: nothing that needs to be canonicalized. + if !value.has_type_flags(needs_canonical_flags) { + let canon_value = Canonical { + max_universe: ty::UniverseIndex::ROOT, + variables: List::empty(), + value, + }; + return canon_value; + } + + let mut canonicalizer = Canonicalizer { + infcx, + tcx, + canonicalize_mode: canonicalize_region_mode, + needs_canonical_flags, + variables: SmallVec::new(), + query_state, + indices: FxHashMap::default(), + binder_index: ty::INNERMOST, + }; + let out_value = value.fold_with(&mut canonicalizer); + + // Once we have canonicalized `out_value`, it should not + // contain anything that ties it to this inference context + // anymore. + debug_assert!(!out_value.needs_infer() && !out_value.has_placeholders()); + + let canonical_variables = + tcx.intern_canonical_var_infos(&canonicalizer.universe_canonicalized_variables()); + + let max_universe = canonical_variables + .iter() + .map(|cvar| cvar.universe()) + .max() + .unwrap_or(ty::UniverseIndex::ROOT); + + Canonical { max_universe, variables: canonical_variables, value: out_value } + } + + /// Creates a canonical variable replacing `kind` from the input, + /// or returns an existing variable if `kind` has already been + /// seen. `kind` is expected to be an unbound variable (or + /// potentially a free region). + fn canonical_var(&mut self, info: CanonicalVarInfo<'tcx>, kind: GenericArg<'tcx>) -> BoundVar { + let Canonicalizer { variables, query_state, indices, .. } = self; + + let var_values = &mut query_state.var_values; + + let universe = info.universe(); + if universe != ty::UniverseIndex::ROOT { + assert!(self.canonicalize_mode.preserve_universes()); + + // Insert universe into the universe map. To preserve the order of the + // universes in the value being canonicalized, we don't update the + // universe in `info` until we have finished canonicalizing. + match query_state.universe_map.binary_search(&universe) { + Err(idx) => query_state.universe_map.insert(idx, universe), + Ok(_) => {} + } + } + + // This code is hot. `variables` and `var_values` are usually small + // (fewer than 8 elements ~95% of the time). They are SmallVec's to + // avoid allocations in those cases. We also don't use `indices` to + // determine if a kind has been seen before until the limit of 8 has + // been exceeded, to also avoid allocations for `indices`. + if !var_values.spilled() { + // `var_values` is stack-allocated. `indices` isn't used yet. Do a + // direct linear search of `var_values`. + if let Some(idx) = var_values.iter().position(|&k| k == kind) { + // `kind` is already present in `var_values`. + BoundVar::new(idx) + } else { + // `kind` isn't present in `var_values`. Append it. Likewise + // for `info` and `variables`. + variables.push(info); + var_values.push(kind); + assert_eq!(variables.len(), var_values.len()); + + // If `var_values` has become big enough to be heap-allocated, + // fill up `indices` to facilitate subsequent lookups. + if var_values.spilled() { + assert!(indices.is_empty()); + *indices = var_values + .iter() + .enumerate() + .map(|(i, &kind)| (kind, BoundVar::new(i))) + .collect(); + } + // The cv is the index of the appended element. + BoundVar::new(var_values.len() - 1) + } + } else { + // `var_values` is large. Do a hashmap search via `indices`. + *indices.entry(kind).or_insert_with(|| { + variables.push(info); + var_values.push(kind); + assert_eq!(variables.len(), var_values.len()); + BoundVar::new(variables.len() - 1) + }) + } + } + + /// Replaces the universe indexes used in `var_values` with their index in + /// `query_state.universe_map`. This minimizes the maximum universe used in + /// the canonicalized value. + fn universe_canonicalized_variables(self) -> SmallVec<[CanonicalVarInfo<'tcx>; 8]> { + if self.query_state.universe_map.len() == 1 { + return self.variables; + } + + let reverse_universe_map: FxHashMap<ty::UniverseIndex, ty::UniverseIndex> = self + .query_state + .universe_map + .iter() + .enumerate() + .map(|(idx, universe)| (*universe, ty::UniverseIndex::from_usize(idx))) + .collect(); + + self.variables + .iter() + .map(|v| CanonicalVarInfo { + kind: match v.kind { + CanonicalVarKind::Ty(CanonicalTyVarKind::Int | CanonicalTyVarKind::Float) => { + return *v; + } + CanonicalVarKind::Ty(CanonicalTyVarKind::General(u)) => { + CanonicalVarKind::Ty(CanonicalTyVarKind::General(reverse_universe_map[&u])) + } + CanonicalVarKind::Region(u) => { + CanonicalVarKind::Region(reverse_universe_map[&u]) + } + CanonicalVarKind::Const(u, t) => { + CanonicalVarKind::Const(reverse_universe_map[&u], t) + } + CanonicalVarKind::PlaceholderTy(placeholder) => { + CanonicalVarKind::PlaceholderTy(ty::Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + CanonicalVarKind::PlaceholderRegion(placeholder) => { + CanonicalVarKind::PlaceholderRegion(ty::Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }) + } + CanonicalVarKind::PlaceholderConst(placeholder, t) => { + CanonicalVarKind::PlaceholderConst( + ty::Placeholder { + universe: reverse_universe_map[&placeholder.universe], + ..placeholder + }, + t, + ) + } + }, + }) + .collect() + } + + /// Shorthand helper that creates a canonical region variable for + /// `r` (always in the root universe). The reason that we always + /// put these variables into the root universe is because this + /// method is used during **query construction:** in that case, we + /// are taking all the regions and just putting them into the most + /// generic context we can. This may generate solutions that don't + /// fit (e.g., that equate some region variable with a placeholder + /// it can't name) on the caller side, but that's ok, the caller + /// can figure that out. In the meantime, it maximizes our + /// caching. + /// + /// (This works because unification never fails -- and hence trait + /// selection is never affected -- due to a universe mismatch.) + fn canonical_var_for_region_in_root_universe( + &mut self, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + self.canonical_var_for_region( + CanonicalVarInfo { kind: CanonicalVarKind::Region(ty::UniverseIndex::ROOT) }, + r, + ) + } + + /// Returns the universe in which `vid` is defined. + fn region_var_universe(&self, vid: ty::RegionVid) -> ty::UniverseIndex { + self.infcx.inner.borrow_mut().unwrap_region_constraints().var_universe(vid) + } + + /// Creates a canonical variable (with the given `info`) + /// representing the region `r`; return a region referencing it. + fn canonical_var_for_region( + &mut self, + info: CanonicalVarInfo<'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + let var = self.canonical_var(info, r.into()); + let br = ty::BoundRegion { var, kind: ty::BrAnon(var.as_u32()) }; + let region = ty::ReLateBound(self.binder_index, br); + self.tcx().mk_region(region) + } + + /// Given a type variable `ty_var` of the given kind, first check + /// if `ty_var` is bound to anything; if so, canonicalize + /// *that*. Otherwise, create a new canonical variable for + /// `ty_var`. + fn canonicalize_ty_var(&mut self, info: CanonicalVarInfo<'tcx>, ty_var: Ty<'tcx>) -> Ty<'tcx> { + let infcx = self.infcx; + let bound_to = infcx.shallow_resolve(ty_var); + if bound_to != ty_var { + self.fold_ty(bound_to) + } else { + let var = self.canonical_var(info, ty_var.into()); + self.tcx().mk_ty(ty::Bound(self.binder_index, var.into())) + } + } + + /// Given a type variable `const_var` of the given kind, first check + /// if `const_var` is bound to anything; if so, canonicalize + /// *that*. Otherwise, create a new canonical variable for + /// `const_var`. + fn canonicalize_const_var( + &mut self, + info: CanonicalVarInfo<'tcx>, + const_var: ty::Const<'tcx>, + ) -> ty::Const<'tcx> { + let infcx = self.infcx; + let bound_to = infcx.shallow_resolve(const_var); + if bound_to != const_var { + self.fold_const(bound_to) + } else { + let var = self.canonical_var(info, const_var.into()); + self.tcx().mk_const(ty::ConstS { + kind: ty::ConstKind::Bound(self.binder_index, var), + ty: self.fold_ty(const_var.ty()), + }) + } + } +} diff --git a/compiler/rustc_infer/src/infer/canonical/mod.rs b/compiler/rustc_infer/src/infer/canonical/mod.rs new file mode 100644 index 000000000..a9294a85e --- /dev/null +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -0,0 +1,159 @@ +//! **Canonicalization** is the key to constructing a query in the +//! middle of type inference. Ordinarily, it is not possible to store +//! types from type inference in query keys, because they contain +//! references to inference variables whose lifetimes are too short +//! and so forth. Canonicalizing a value T1 using `canonicalize_query` +//! produces two things: +//! +//! - a value T2 where each unbound inference variable has been +//! replaced with a **canonical variable**; +//! - a map M (of type `CanonicalVarValues`) from those canonical +//! variables back to the original. +//! +//! We can then do queries using T2. These will give back constraints +//! on the canonical variables which can be translated, using the map +//! M, into constraints in our source context. This process of +//! translating the results back is done by the +//! `instantiate_query_result` method. +//! +//! For a more detailed look at what is happening here, check +//! out the [chapter in the rustc dev guide][c]. +//! +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html + +use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind}; +use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_index::vec::IndexVec; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::subst::GenericArg; +use rustc_middle::ty::{self, BoundVar, List}; +use rustc_span::source_map::Span; + +pub use rustc_middle::infer::canonical::*; +use substitute::CanonicalExt; + +mod canonicalizer; +pub mod query_response; +mod substitute; + +impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { + /// Creates a substitution S for the canonical value with fresh + /// inference variables and applies it to the canonical value. + /// Returns both the instantiated result *and* the substitution S. + /// + /// This is only meant to be invoked as part of constructing an + /// inference context at the start of a query (see + /// `InferCtxtBuilder::enter_with_canonical`). It basically + /// brings the canonical value "into scope" within your new infcx. + /// + /// At the end of processing, the substitution S (once + /// canonicalized) then represents the values that you computed + /// for each of the canonical inputs to your query. + pub fn instantiate_canonical_with_fresh_inference_vars<T>( + &self, + span: Span, + canonical: &Canonical<'tcx, T>, + ) -> (T, CanonicalVarValues<'tcx>) + where + T: TypeFoldable<'tcx>, + { + // For each universe that is referred to in the incoming + // query, create a universe in our local inference context. In + // practice, as of this writing, all queries have no universes + // in them, so this code has no effect, but it is looking + // forward to the day when we *do* want to carry universes + // through into queries. + let universes: IndexVec<ty::UniverseIndex, _> = std::iter::once(ty::UniverseIndex::ROOT) + .chain((0..canonical.max_universe.as_u32()).map(|_| self.create_next_universe())) + .collect(); + + let canonical_inference_vars = + self.instantiate_canonical_vars(span, canonical.variables, |ui| universes[ui]); + let result = canonical.substitute(self.tcx, &canonical_inference_vars); + (result, canonical_inference_vars) + } + + /// Given the "infos" about the canonical variables from some + /// canonical, creates fresh variables with the same + /// characteristics (see `instantiate_canonical_var` for + /// details). You can then use `substitute` to instantiate the + /// canonical variable with these inference variables. + fn instantiate_canonical_vars( + &self, + span: Span, + variables: &List<CanonicalVarInfo<'tcx>>, + universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, + ) -> CanonicalVarValues<'tcx> { + let var_values: IndexVec<BoundVar, GenericArg<'tcx>> = variables + .iter() + .map(|info| self.instantiate_canonical_var(span, info, &universe_map)) + .collect(); + + CanonicalVarValues { var_values } + } + + /// Given the "info" about a canonical variable, creates a fresh + /// variable for it. If this is an existentially quantified + /// variable, then you'll get a new inference variable; if it is a + /// universally quantified variable, you get a placeholder. + fn instantiate_canonical_var( + &self, + span: Span, + cv_info: CanonicalVarInfo<'tcx>, + universe_map: impl Fn(ty::UniverseIndex) -> ty::UniverseIndex, + ) -> GenericArg<'tcx> { + match cv_info.kind { + CanonicalVarKind::Ty(ty_kind) => { + let ty = match ty_kind { + CanonicalTyVarKind::General(ui) => self.next_ty_var_in_universe( + TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }, + universe_map(ui), + ), + + CanonicalTyVarKind::Int => self.next_int_var(), + + CanonicalTyVarKind::Float => self.next_float_var(), + }; + ty.into() + } + + CanonicalVarKind::PlaceholderTy(ty::PlaceholderType { universe, name }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped = ty::PlaceholderType { universe: universe_mapped, name }; + self.tcx.mk_ty(ty::Placeholder(placeholder_mapped)).into() + } + + CanonicalVarKind::Region(ui) => self + .next_region_var_in_universe( + RegionVariableOrigin::MiscVariable(span), + universe_map(ui), + ) + .into(), + + CanonicalVarKind::PlaceholderRegion(ty::PlaceholderRegion { universe, name }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped = ty::PlaceholderRegion { universe: universe_mapped, name }; + self.tcx.mk_region(ty::RePlaceholder(placeholder_mapped)).into() + } + + CanonicalVarKind::Const(ui, ty) => self + .next_const_var_in_universe( + ty, + ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span }, + universe_map(ui), + ) + .into(), + + CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst { universe, name }, ty) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, name }; + self.tcx + .mk_const(ty::ConstS { + kind: ty::ConstKind::Placeholder(placeholder_mapped), + ty, + }) + .into() + } + } + } +} diff --git a/compiler/rustc_infer/src/infer/canonical/query_response.rs b/compiler/rustc_infer/src/infer/canonical/query_response.rs new file mode 100644 index 000000000..8dc20544f --- /dev/null +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -0,0 +1,741 @@ +//! This module contains the code to instantiate a "query result", and +//! in particular to extract out the resulting region obligations and +//! encode them therein. +//! +//! For an overview of what canonicalization is and how it fits into +//! rustc, check out the [chapter in the rustc dev guide][c]. +//! +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html + +use crate::infer::canonical::substitute::{substitute_value, CanonicalExt}; +use crate::infer::canonical::{ + Canonical, CanonicalVarValues, CanonicalizedQueryResponse, Certainty, OriginalQueryValues, + QueryOutlivesConstraint, QueryRegionConstraints, QueryResponse, +}; +use crate::infer::nll_relate::{NormalizationStrategy, TypeRelating, TypeRelatingDelegate}; +use crate::infer::region_constraints::{Constraint, RegionConstraintData}; +use crate::infer::{InferCtxt, InferOk, InferResult, NllRegionVariableOrigin}; +use crate::traits::query::{Fallible, NoSolution}; +use crate::traits::TraitEngine; +use crate::traits::{Obligation, ObligationCause, PredicateObligation}; +use rustc_data_structures::captures::Captures; +use rustc_index::vec::Idx; +use rustc_index::vec::IndexVec; +use rustc_middle::arena::ArenaAllocatable; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::relate::TypeRelation; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, BoundVar, Const, ToPredicate, Ty, TyCtxt}; +use rustc_span::Span; +use std::fmt::Debug; +use std::iter; + +impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { + /// This method is meant to be invoked as the final step of a canonical query + /// implementation. It is given: + /// + /// - the instantiated variables `inference_vars` created from the query key + /// - the result `answer` of the query + /// - a fulfillment context `fulfill_cx` that may contain various obligations which + /// have yet to be proven. + /// + /// Given this, the function will process the obligations pending + /// in `fulfill_cx`: + /// + /// - If all the obligations can be proven successfully, it will + /// package up any resulting region obligations (extracted from + /// `infcx`) along with the fully resolved value `answer` into a + /// query result (which is then itself canonicalized). + /// - If some obligations can be neither proven nor disproven, then + /// the same thing happens, but the resulting query is marked as ambiguous. + /// - Finally, if any of the obligations result in a hard error, + /// then `Err(NoSolution)` is returned. + #[instrument(skip(self, inference_vars, answer, fulfill_cx), level = "trace")] + pub fn make_canonicalized_query_response<T>( + &self, + inference_vars: CanonicalVarValues<'tcx>, + answer: T, + fulfill_cx: &mut dyn TraitEngine<'tcx>, + ) -> Fallible<CanonicalizedQueryResponse<'tcx, T>> + where + T: Debug + TypeFoldable<'tcx>, + Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>, + { + let query_response = self.make_query_response(inference_vars, answer, fulfill_cx)?; + let canonical_result = self.canonicalize_response(query_response); + + debug!("canonical_result = {:#?}", canonical_result); + + Ok(self.tcx.arena.alloc(canonical_result)) + } + + /// A version of `make_canonicalized_query_response` that does + /// not pack in obligations, for contexts that want to drop + /// pending obligations instead of treating them as an ambiguity (e.g. + /// typeck "probing" contexts). + /// + /// If you DO want to keep track of pending obligations (which + /// include all region obligations, so this includes all cases + /// that care about regions) with this function, you have to + /// do it yourself, by e.g., having them be a part of the answer. + pub fn make_query_response_ignoring_pending_obligations<T>( + &self, + inference_vars: CanonicalVarValues<'tcx>, + answer: T, + ) -> Canonical<'tcx, QueryResponse<'tcx, T>> + where + T: Debug + TypeFoldable<'tcx>, + { + self.canonicalize_response(QueryResponse { + var_values: inference_vars, + region_constraints: QueryRegionConstraints::default(), + certainty: Certainty::Proven, // Ambiguities are OK! + opaque_types: vec![], + value: answer, + }) + } + + /// Helper for `make_canonicalized_query_response` that does + /// everything up until the final canonicalization. + #[instrument(skip(self, fulfill_cx), level = "debug")] + fn make_query_response<T>( + &self, + inference_vars: CanonicalVarValues<'tcx>, + answer: T, + fulfill_cx: &mut dyn TraitEngine<'tcx>, + ) -> Result<QueryResponse<'tcx, T>, NoSolution> + where + T: Debug + TypeFoldable<'tcx>, + { + let tcx = self.tcx; + + // Select everything, returning errors. + let true_errors = fulfill_cx.select_where_possible(self); + debug!("true_errors = {:#?}", true_errors); + + if !true_errors.is_empty() { + // FIXME -- we don't indicate *why* we failed to solve + debug!("make_query_response: true_errors={:#?}", true_errors); + return Err(NoSolution); + } + + // Anything left unselected *now* must be an ambiguity. + let ambig_errors = fulfill_cx.select_all_or_error(self); + debug!("ambig_errors = {:#?}", ambig_errors); + + let region_obligations = self.take_registered_region_obligations(); + let region_constraints = self.with_region_constraints(|region_constraints| { + make_query_region_constraints( + tcx, + region_obligations.iter().map(|r_o| (r_o.sup_type, r_o.sub_region)), + region_constraints, + ) + }); + + let certainty = + if ambig_errors.is_empty() { Certainty::Proven } else { Certainty::Ambiguous }; + + let opaque_types = self.take_opaque_types_for_query_response(); + + Ok(QueryResponse { + var_values: inference_vars, + region_constraints, + certainty, + value: answer, + opaque_types, + }) + } + + fn take_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> { + self.inner + .borrow_mut() + .opaque_type_storage + .take_opaque_types() + .into_iter() + .map(|(k, v)| (self.tcx.mk_opaque(k.def_id.to_def_id(), k.substs), v.hidden_type.ty)) + .collect() + } + + /// Given the (canonicalized) result to a canonical query, + /// instantiates the result so it can be used, plugging in the + /// values from the canonical query. (Note that the result may + /// have been ambiguous; you should check the certainty level of + /// the query before applying this function.) + /// + /// To get a good understanding of what is happening here, check + /// out the [chapter in the rustc dev guide][c]. + /// + /// [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html#processing-the-canonicalized-query-result + pub fn instantiate_query_response_and_region_obligations<R>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + original_values: &OriginalQueryValues<'tcx>, + query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>, + ) -> InferResult<'tcx, R> + where + R: Debug + TypeFoldable<'tcx>, + { + let InferOk { value: result_subst, mut obligations } = + self.query_response_substitution(cause, param_env, original_values, query_response)?; + + obligations.extend(self.query_outlives_constraints_into_obligations( + cause, + param_env, + &query_response.value.region_constraints.outlives, + &result_subst, + )); + + let user_result: R = + query_response.substitute_projected(self.tcx, &result_subst, |q_r| q_r.value.clone()); + + Ok(InferOk { value: user_result, obligations }) + } + + /// An alternative to + /// `instantiate_query_response_and_region_obligations` that is more + /// efficient for NLL. NLL is a bit more advanced in the + /// "transition to chalk" than the rest of the compiler. During + /// the NLL type check, all of the "processing" of types and + /// things happens in queries -- the NLL checker itself is only + /// interested in the region obligations (`'a: 'b` or `T: 'b`) + /// that come out of these queries, which it wants to convert into + /// MIR-based constraints and solve. Therefore, it is most + /// convenient for the NLL Type Checker to **directly consume** + /// the `QueryOutlivesConstraint` values that arise from doing a + /// query. This is contrast to other parts of the compiler, which + /// would prefer for those `QueryOutlivesConstraint` to be converted + /// into the older infcx-style constraints (e.g., calls to + /// `sub_regions` or `register_region_obligation`). + /// + /// Therefore, `instantiate_nll_query_response_and_region_obligations` performs the same + /// basic operations as `instantiate_query_response_and_region_obligations` but + /// it returns its result differently: + /// + /// - It creates a substitution `S` that maps from the original + /// query variables to the values computed in the query + /// result. If any errors arise, they are propagated back as an + /// `Err` result. + /// - In the case of a successful substitution, we will append + /// `QueryOutlivesConstraint` values onto the + /// `output_query_region_constraints` vector for the solver to + /// use (if an error arises, some values may also be pushed, but + /// they should be ignored). + /// - It **can happen** (though it rarely does currently) that + /// equating types and things will give rise to subobligations + /// that must be processed. In this case, those subobligations + /// are propagated back in the return value. + /// - Finally, the query result (of type `R`) is propagated back, + /// after applying the substitution `S`. + pub fn instantiate_nll_query_response_and_region_obligations<R>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + original_values: &OriginalQueryValues<'tcx>, + query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>, + output_query_region_constraints: &mut QueryRegionConstraints<'tcx>, + ) -> InferResult<'tcx, R> + where + R: Debug + TypeFoldable<'tcx>, + { + let InferOk { value: result_subst, mut obligations } = self + .query_response_substitution_guess(cause, param_env, original_values, query_response)?; + + // Compute `QueryOutlivesConstraint` values that unify each of + // the original values `v_o` that was canonicalized into a + // variable... + + for (index, original_value) in original_values.var_values.iter().enumerate() { + // ...with the value `v_r` of that variable from the query. + let result_value = query_response.substitute_projected(self.tcx, &result_subst, |v| { + v.var_values[BoundVar::new(index)] + }); + match (original_value.unpack(), result_value.unpack()) { + (GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2)) + if re1.is_erased() && re2.is_erased() => + { + // No action needed. + } + + (GenericArgKind::Lifetime(v_o), GenericArgKind::Lifetime(v_r)) => { + // To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`. + if v_o != v_r { + output_query_region_constraints + .outlives + .push(ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r))); + output_query_region_constraints + .outlives + .push(ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o))); + } + } + + (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => { + TypeRelating::new( + self, + QueryTypeRelatingDelegate { + infcx: self, + param_env, + cause, + obligations: &mut obligations, + }, + ty::Variance::Invariant, + ) + .relate(v1, v2)?; + } + + (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => { + TypeRelating::new( + self, + QueryTypeRelatingDelegate { + infcx: self, + param_env, + cause, + obligations: &mut obligations, + }, + ty::Variance::Invariant, + ) + .relate(v1, v2)?; + } + + _ => { + bug!("kind mismatch, cannot unify {:?} and {:?}", original_value, result_value); + } + } + } + + // ...also include the other query region constraints from the query. + output_query_region_constraints.outlives.extend( + query_response.value.region_constraints.outlives.iter().filter_map(|&r_c| { + let r_c = substitute_value(self.tcx, &result_subst, r_c); + + // Screen out `'a: 'a` cases -- we skip the binder here but + // only compare the inner values to one another, so they are still at + // consistent binding levels. + let ty::OutlivesPredicate(k1, r2) = r_c.skip_binder(); + if k1 != r2.into() { Some(r_c) } else { None } + }), + ); + + // ...also include the query member constraints. + output_query_region_constraints.member_constraints.extend( + query_response + .value + .region_constraints + .member_constraints + .iter() + .map(|p_c| substitute_value(self.tcx, &result_subst, p_c.clone())), + ); + + let user_result: R = + query_response.substitute_projected(self.tcx, &result_subst, |q_r| q_r.value.clone()); + + Ok(InferOk { value: user_result, obligations }) + } + + /// Given the original values and the (canonicalized) result from + /// computing a query, returns a substitution that can be applied + /// to the query result to convert the result back into the + /// original namespace. + /// + /// The substitution also comes accompanied with subobligations + /// that arose from unification; these might occur if (for + /// example) we are doing lazy normalization and the value + /// assigned to a type variable is unified with an unnormalized + /// projection. + fn query_response_substitution<R>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + original_values: &OriginalQueryValues<'tcx>, + query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>, + ) -> InferResult<'tcx, CanonicalVarValues<'tcx>> + where + R: Debug + TypeFoldable<'tcx>, + { + debug!( + "query_response_substitution(original_values={:#?}, query_response={:#?})", + original_values, query_response, + ); + + let mut value = self.query_response_substitution_guess( + cause, + param_env, + original_values, + query_response, + )?; + + value.obligations.extend( + self.unify_query_response_substitution_guess( + cause, + param_env, + original_values, + &value.value, + query_response, + )? + .into_obligations(), + ); + + Ok(value) + } + + /// Given the original values and the (canonicalized) result from + /// computing a query, returns a **guess** at a substitution that + /// can be applied to the query result to convert the result back + /// into the original namespace. This is called a **guess** + /// because it uses a quick heuristic to find the values for each + /// canonical variable; if that quick heuristic fails, then we + /// will instantiate fresh inference variables for each canonical + /// variable instead. Therefore, the result of this method must be + /// properly unified + fn query_response_substitution_guess<R>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + original_values: &OriginalQueryValues<'tcx>, + query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>, + ) -> InferResult<'tcx, CanonicalVarValues<'tcx>> + where + R: Debug + TypeFoldable<'tcx>, + { + debug!( + "query_response_substitution_guess(original_values={:#?}, query_response={:#?})", + original_values, query_response, + ); + + // For each new universe created in the query result that did + // not appear in the original query, create a local + // superuniverse. + let mut universe_map = original_values.universe_map.clone(); + let num_universes_in_query = original_values.universe_map.len(); + let num_universes_in_response = query_response.max_universe.as_usize() + 1; + for _ in num_universes_in_query..num_universes_in_response { + universe_map.push(self.create_next_universe()); + } + assert!(!universe_map.is_empty()); // always have the root universe + assert_eq!(universe_map[ty::UniverseIndex::ROOT.as_usize()], ty::UniverseIndex::ROOT); + + // Every canonical query result includes values for each of + // the inputs to the query. Therefore, we begin by unifying + // these values with the original inputs that were + // canonicalized. + let result_values = &query_response.value.var_values; + assert_eq!(original_values.var_values.len(), result_values.len()); + + // Quickly try to find initial values for the canonical + // variables in the result in terms of the query. We do this + // by iterating down the values that the query gave to each of + // the canonical inputs. If we find that one of those values + // is directly equal to one of the canonical variables in the + // result, then we can type the corresponding value from the + // input. See the example above. + let mut opt_values: IndexVec<BoundVar, Option<GenericArg<'tcx>>> = + IndexVec::from_elem_n(None, query_response.variables.len()); + + // In terms of our example above, we are iterating over pairs like: + // [(?A, Vec<?0>), ('static, '?1), (?B, ?0)] + for (original_value, result_value) in iter::zip(&original_values.var_values, result_values) + { + match result_value.unpack() { + GenericArgKind::Type(result_value) => { + // e.g., here `result_value` might be `?0` in the example above... + if let ty::Bound(debruijn, b) = *result_value.kind() { + // ...in which case we would set `canonical_vars[0]` to `Some(?U)`. + + // We only allow a `ty::INNERMOST` index in substitutions. + assert_eq!(debruijn, ty::INNERMOST); + opt_values[b.var] = Some(*original_value); + } + } + GenericArgKind::Lifetime(result_value) => { + // e.g., here `result_value` might be `'?1` in the example above... + if let ty::ReLateBound(debruijn, br) = *result_value { + // ... in which case we would set `canonical_vars[0]` to `Some('static)`. + + // We only allow a `ty::INNERMOST` index in substitutions. + assert_eq!(debruijn, ty::INNERMOST); + opt_values[br.var] = Some(*original_value); + } + } + GenericArgKind::Const(result_value) => { + if let ty::ConstKind::Bound(debrujin, b) = result_value.kind() { + // ...in which case we would set `canonical_vars[0]` to `Some(const X)`. + + // We only allow a `ty::INNERMOST` index in substitutions. + assert_eq!(debrujin, ty::INNERMOST); + opt_values[b] = Some(*original_value); + } + } + } + } + + // Create a result substitution: if we found a value for a + // given variable in the loop above, use that. Otherwise, use + // a fresh inference variable. + let result_subst = CanonicalVarValues { + var_values: query_response + .variables + .iter() + .enumerate() + .map(|(index, info)| { + if info.is_existential() { + match opt_values[BoundVar::new(index)] { + Some(k) => k, + None => self.instantiate_canonical_var(cause.span, info, |u| { + universe_map[u.as_usize()] + }), + } + } else { + self.instantiate_canonical_var(cause.span, info, |u| { + universe_map[u.as_usize()] + }) + } + }) + .collect(), + }; + + let mut obligations = vec![]; + + // Carry all newly resolved opaque types to the caller's scope + for &(a, b) in &query_response.value.opaque_types { + let a = substitute_value(self.tcx, &result_subst, a); + let b = substitute_value(self.tcx, &result_subst, b); + obligations.extend(self.handle_opaque_type(a, b, true, cause, param_env)?.obligations); + } + + Ok(InferOk { value: result_subst, obligations }) + } + + /// Given a "guess" at the values for the canonical variables in + /// the input, try to unify with the *actual* values found in the + /// query result. Often, but not always, this is a no-op, because + /// we already found the mapping in the "guessing" step. + /// + /// See also: `query_response_substitution_guess` + fn unify_query_response_substitution_guess<R>( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + original_values: &OriginalQueryValues<'tcx>, + result_subst: &CanonicalVarValues<'tcx>, + query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>, + ) -> InferResult<'tcx, ()> + where + R: Debug + TypeFoldable<'tcx>, + { + // A closure that yields the result value for the given + // canonical variable; this is taken from + // `query_response.var_values` after applying the substitution + // `result_subst`. + let substituted_query_response = |index: BoundVar| -> GenericArg<'tcx> { + query_response.substitute_projected(self.tcx, &result_subst, |v| v.var_values[index]) + }; + + // Unify the original value for each variable with the value + // taken from `query_response` (after applying `result_subst`). + self.unify_canonical_vars(cause, param_env, original_values, substituted_query_response) + } + + /// Converts the region constraints resulting from a query into an + /// iterator of obligations. + fn query_outlives_constraints_into_obligations<'a>( + &'a self, + cause: &'a ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + unsubstituted_region_constraints: &'a [QueryOutlivesConstraint<'tcx>], + result_subst: &'a CanonicalVarValues<'tcx>, + ) -> impl Iterator<Item = PredicateObligation<'tcx>> + 'a + Captures<'tcx> { + unsubstituted_region_constraints.iter().map(move |&constraint| { + let predicate = substitute_value(self.tcx, result_subst, constraint); + self.query_outlives_constraint_to_obligation(predicate, cause.clone(), param_env) + }) + } + + pub fn query_outlives_constraint_to_obligation( + &self, + predicate: QueryOutlivesConstraint<'tcx>, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> Obligation<'tcx, ty::Predicate<'tcx>> { + let ty::OutlivesPredicate(k1, r2) = predicate.skip_binder(); + + let atom = match k1.unpack() { + GenericArgKind::Lifetime(r1) => { + ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(r1, r2)) + } + GenericArgKind::Type(t1) => { + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(t1, r2)) + } + GenericArgKind::Const(..) => { + // Consts cannot outlive one another, so we don't expect to + // encounter this branch. + span_bug!(cause.span, "unexpected const outlives {:?}", predicate); + } + }; + let predicate = predicate.rebind(atom).to_predicate(self.tcx); + + Obligation::new(cause, param_env, predicate) + } + + /// Given two sets of values for the same set of canonical variables, unify them. + /// The second set is produced lazily by supplying indices from the first set. + fn unify_canonical_vars( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + variables1: &OriginalQueryValues<'tcx>, + variables2: impl Fn(BoundVar) -> GenericArg<'tcx>, + ) -> InferResult<'tcx, ()> { + self.commit_if_ok(|_| { + let mut obligations = vec![]; + for (index, value1) in variables1.var_values.iter().enumerate() { + let value2 = variables2(BoundVar::new(index)); + + match (value1.unpack(), value2.unpack()) { + (GenericArgKind::Type(v1), GenericArgKind::Type(v2)) => { + obligations + .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations()); + } + (GenericArgKind::Lifetime(re1), GenericArgKind::Lifetime(re2)) + if re1.is_erased() && re2.is_erased() => + { + // no action needed + } + (GenericArgKind::Lifetime(v1), GenericArgKind::Lifetime(v2)) => { + obligations + .extend(self.at(cause, param_env).eq(v1, v2)?.into_obligations()); + } + (GenericArgKind::Const(v1), GenericArgKind::Const(v2)) => { + let ok = self.at(cause, param_env).eq(v1, v2)?; + obligations.extend(ok.into_obligations()); + } + _ => { + bug!("kind mismatch, cannot unify {:?} and {:?}", value1, value2,); + } + } + } + Ok(InferOk { value: (), obligations }) + }) + } +} + +/// Given the region obligations and constraints scraped from the infcx, +/// creates query region constraints. +pub fn make_query_region_constraints<'tcx>( + tcx: TyCtxt<'tcx>, + outlives_obligations: impl Iterator<Item = (Ty<'tcx>, ty::Region<'tcx>)>, + region_constraints: &RegionConstraintData<'tcx>, +) -> QueryRegionConstraints<'tcx> { + let RegionConstraintData { constraints, verifys, givens, member_constraints } = + region_constraints; + + assert!(verifys.is_empty()); + assert!(givens.is_empty()); + + let outlives: Vec<_> = constraints + .iter() + .map(|(k, _)| match *k { + // Swap regions because we are going from sub (<=) to outlives + // (>=). + Constraint::VarSubVar(v1, v2) => ty::OutlivesPredicate( + tcx.mk_region(ty::ReVar(v2)).into(), + tcx.mk_region(ty::ReVar(v1)), + ), + Constraint::VarSubReg(v1, r2) => { + ty::OutlivesPredicate(r2.into(), tcx.mk_region(ty::ReVar(v1))) + } + Constraint::RegSubVar(r1, v2) => { + ty::OutlivesPredicate(tcx.mk_region(ty::ReVar(v2)).into(), r1) + } + Constraint::RegSubReg(r1, r2) => ty::OutlivesPredicate(r2.into(), r1), + }) + .map(ty::Binder::dummy) // no bound vars in the code above + .chain( + outlives_obligations + .map(|(ty, r)| ty::OutlivesPredicate(ty.into(), r)) + .map(ty::Binder::dummy), // no bound vars in the code above + ) + .collect(); + + QueryRegionConstraints { outlives, member_constraints: member_constraints.clone() } +} + +struct QueryTypeRelatingDelegate<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + obligations: &'a mut Vec<PredicateObligation<'tcx>>, + param_env: ty::ParamEnv<'tcx>, + cause: &'a ObligationCause<'tcx>, +} + +impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> { + fn span(&self) -> Span { + self.cause.span + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } + + fn create_next_universe(&mut self) -> ty::UniverseIndex { + self.infcx.create_next_universe() + } + + fn next_existential_region_var(&mut self, from_forall: bool) -> ty::Region<'tcx> { + let origin = NllRegionVariableOrigin::Existential { from_forall }; + self.infcx.next_nll_region_var(origin) + } + + fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx> { + self.infcx.tcx.mk_region(ty::RePlaceholder(placeholder)) + } + + fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx> { + self.infcx.next_nll_region_var_in_universe( + NllRegionVariableOrigin::Existential { from_forall: false }, + universe, + ) + } + + fn push_outlives( + &mut self, + sup: ty::Region<'tcx>, + sub: ty::Region<'tcx>, + _info: ty::VarianceDiagInfo<'tcx>, + ) { + self.obligations.push(Obligation { + cause: self.cause.clone(), + param_env: self.param_env, + predicate: ty::Binder::dummy(ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate( + sup, sub, + ))) + .to_predicate(self.infcx.tcx), + recursion_depth: 0, + }); + } + + fn const_equate(&mut self, _a: Const<'tcx>, _b: Const<'tcx>) { + span_bug!(self.cause.span(), "generic_const_exprs: unreachable `const_equate`"); + } + + fn normalization() -> NormalizationStrategy { + NormalizationStrategy::Eager + } + + fn forbid_inference_vars() -> bool { + true + } + + fn register_opaque_type( + &mut self, + a: Ty<'tcx>, + b: Ty<'tcx>, + a_is_expected: bool, + ) -> Result<(), TypeError<'tcx>> { + self.obligations.extend( + self.infcx + .handle_opaque_type(a, b, a_is_expected, &self.cause, self.param_env)? + .obligations, + ); + Ok(()) + } +} diff --git a/compiler/rustc_infer/src/infer/canonical/substitute.rs b/compiler/rustc_infer/src/infer/canonical/substitute.rs new file mode 100644 index 000000000..34b611342 --- /dev/null +++ b/compiler/rustc_infer/src/infer/canonical/substitute.rs @@ -0,0 +1,91 @@ +//! This module contains code to substitute new values into a +//! `Canonical<'tcx, T>`. +//! +//! For an overview of what canonicalization is and how it fits into +//! rustc, check out the [chapter in the rustc dev guide][c]. +//! +//! [c]: https://rust-lang.github.io/chalk/book/canonical_queries/canonicalization.html + +use crate::infer::canonical::{Canonical, CanonicalVarValues}; +use rustc_middle::ty::fold::{FnMutDelegate, TypeFoldable}; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, TyCtxt}; + +pub(super) trait CanonicalExt<'tcx, V> { + /// Instantiate the wrapped value, replacing each canonical value + /// with the value given in `var_values`. + fn substitute(&self, tcx: TyCtxt<'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V + where + V: TypeFoldable<'tcx>; + + /// Allows one to apply a substitute to some subset of + /// `self.value`. Invoke `projection_fn` with `self.value` to get + /// a value V that is expressed in terms of the same canonical + /// variables bound in `self` (usually this extracts from subset + /// of `self`). Apply the substitution `var_values` to this value + /// V, replacing each of the canonical variables. + fn substitute_projected<T>( + &self, + tcx: TyCtxt<'tcx>, + var_values: &CanonicalVarValues<'tcx>, + projection_fn: impl FnOnce(&V) -> T, + ) -> T + where + T: TypeFoldable<'tcx>; +} + +impl<'tcx, V> CanonicalExt<'tcx, V> for Canonical<'tcx, V> { + fn substitute(&self, tcx: TyCtxt<'tcx>, var_values: &CanonicalVarValues<'tcx>) -> V + where + V: TypeFoldable<'tcx>, + { + self.substitute_projected(tcx, var_values, |value| value.clone()) + } + + fn substitute_projected<T>( + &self, + tcx: TyCtxt<'tcx>, + var_values: &CanonicalVarValues<'tcx>, + projection_fn: impl FnOnce(&V) -> T, + ) -> T + where + T: TypeFoldable<'tcx>, + { + assert_eq!(self.variables.len(), var_values.len()); + let value = projection_fn(&self.value); + substitute_value(tcx, var_values, value) + } +} + +/// Substitute the values from `var_values` into `value`. `var_values` +/// must be values for the set of canonical variables that appear in +/// `value`. +pub(super) fn substitute_value<'tcx, T>( + tcx: TyCtxt<'tcx>, + var_values: &CanonicalVarValues<'tcx>, + value: T, +) -> T +where + T: TypeFoldable<'tcx>, +{ + if var_values.var_values.is_empty() { + value + } else { + let delegate = FnMutDelegate { + regions: |br: ty::BoundRegion| match var_values.var_values[br.var].unpack() { + GenericArgKind::Lifetime(l) => l, + r => bug!("{:?} is a region but value is {:?}", br, r), + }, + types: |bound_ty: ty::BoundTy| match var_values.var_values[bound_ty.var].unpack() { + GenericArgKind::Type(ty) => ty, + r => bug!("{:?} is a type but value is {:?}", bound_ty, r), + }, + consts: |bound_ct: ty::BoundVar, _| match var_values.var_values[bound_ct].unpack() { + GenericArgKind::Const(ct) => ct, + c => bug!("{:?} is a const but value is {:?}", bound_ct, c), + }, + }; + + tcx.replace_escaping_bound_vars_uncached(value, delegate) + } +} diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs new file mode 100644 index 000000000..8bf1de34a --- /dev/null +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -0,0 +1,1001 @@ +//! There are four type combiners: [Equate], [Sub], [Lub], and [Glb]. +//! Each implements the trait [TypeRelation] and contains methods for +//! combining two instances of various things and yielding a new instance. +//! These combiner methods always yield a `Result<T>`. To relate two +//! types, you can use `infcx.at(cause, param_env)` which then allows +//! you to use the relevant methods of [At](super::at::At). +//! +//! Combiners mostly do their specific behavior and then hand off the +//! bulk of the work to [InferCtxt::super_combine_tys] and +//! [InferCtxt::super_combine_consts]. +//! +//! Combining two types may have side-effects on the inference contexts +//! which can be undone by using snapshots. You probably want to use +//! either [InferCtxt::commit_if_ok] or [InferCtxt::probe]. +//! +//! On success, the LUB/GLB operations return the appropriate bound. The +//! return value of `Equate` or `Sub` shouldn't really be used. +//! +//! ## Contravariance +//! +//! We explicitly track which argument is expected using +//! [TypeRelation::a_is_expected], so when dealing with contravariance +//! this should be correctly updated. + +use super::equate::Equate; +use super::glb::Glb; +use super::lub::Lub; +use super::sub::Sub; +use super::type_variable::TypeVariableValue; +use super::{InferCtxt, MiscVariable, TypeTrace}; +use crate::traits::{Obligation, PredicateObligations}; +use rustc_data_structures::sso::SsoHashMap; +use rustc_hir::def_id::DefId; +use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, InferConst, ToPredicate, Ty, TyCtxt, TypeVisitable}; +use rustc_middle::ty::{IntType, UintType}; +use rustc_span::{Span, DUMMY_SP}; + +#[derive(Clone)] +pub struct CombineFields<'infcx, 'tcx> { + pub infcx: &'infcx InferCtxt<'infcx, 'tcx>, + pub trace: TypeTrace<'tcx>, + pub cause: Option<ty::relate::Cause>, + pub param_env: ty::ParamEnv<'tcx>, + pub obligations: PredicateObligations<'tcx>, + /// Whether we should define opaque types + /// or just treat them opaquely. + /// Currently only used to prevent predicate + /// matching from matching anything against opaque + /// types. + pub define_opaque_types: bool, +} + +#[derive(Copy, Clone, Debug)] +pub enum RelationDir { + SubtypeOf, + SupertypeOf, + EqTo, +} + +impl<'infcx, 'tcx> InferCtxt<'infcx, 'tcx> { + pub fn super_combine_tys<R>( + &self, + relation: &mut R, + a: Ty<'tcx>, + b: Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> + where + R: TypeRelation<'tcx>, + { + let a_is_expected = relation.a_is_expected(); + + match (a.kind(), b.kind()) { + // Relate integral variables to other types + (&ty::Infer(ty::IntVar(a_id)), &ty::Infer(ty::IntVar(b_id))) => { + self.inner + .borrow_mut() + .int_unification_table() + .unify_var_var(a_id, b_id) + .map_err(|e| int_unification_error(a_is_expected, e))?; + Ok(a) + } + (&ty::Infer(ty::IntVar(v_id)), &ty::Int(v)) => { + self.unify_integral_variable(a_is_expected, v_id, IntType(v)) + } + (&ty::Int(v), &ty::Infer(ty::IntVar(v_id))) => { + self.unify_integral_variable(!a_is_expected, v_id, IntType(v)) + } + (&ty::Infer(ty::IntVar(v_id)), &ty::Uint(v)) => { + self.unify_integral_variable(a_is_expected, v_id, UintType(v)) + } + (&ty::Uint(v), &ty::Infer(ty::IntVar(v_id))) => { + self.unify_integral_variable(!a_is_expected, v_id, UintType(v)) + } + + // Relate floating-point variables to other types + (&ty::Infer(ty::FloatVar(a_id)), &ty::Infer(ty::FloatVar(b_id))) => { + self.inner + .borrow_mut() + .float_unification_table() + .unify_var_var(a_id, b_id) + .map_err(|e| float_unification_error(relation.a_is_expected(), e))?; + Ok(a) + } + (&ty::Infer(ty::FloatVar(v_id)), &ty::Float(v)) => { + self.unify_float_variable(a_is_expected, v_id, v) + } + (&ty::Float(v), &ty::Infer(ty::FloatVar(v_id))) => { + self.unify_float_variable(!a_is_expected, v_id, v) + } + + // All other cases of inference are errors + (&ty::Infer(_), _) | (_, &ty::Infer(_)) => { + Err(TypeError::Sorts(ty::relate::expected_found(relation, a, b))) + } + + _ => ty::relate::super_relate_tys(relation, a, b), + } + } + + pub fn super_combine_consts<R>( + &self, + relation: &mut R, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> + where + R: ConstEquateRelation<'tcx>, + { + debug!("{}.consts({:?}, {:?})", relation.tag(), a, b); + if a == b { + return Ok(a); + } + + let a = self.shallow_resolve(a); + let b = self.shallow_resolve(b); + + let a_is_expected = relation.a_is_expected(); + + match (a.kind(), b.kind()) { + ( + ty::ConstKind::Infer(InferConst::Var(a_vid)), + ty::ConstKind::Infer(InferConst::Var(b_vid)), + ) => { + self.inner + .borrow_mut() + .const_unification_table() + .unify_var_var(a_vid, b_vid) + .map_err(|e| const_unification_error(a_is_expected, e))?; + return Ok(a); + } + + // All other cases of inference with other variables are errors. + (ty::ConstKind::Infer(InferConst::Var(_)), ty::ConstKind::Infer(_)) + | (ty::ConstKind::Infer(_), ty::ConstKind::Infer(InferConst::Var(_))) => { + bug!("tried to combine ConstKind::Infer/ConstKind::Infer(InferConst::Var)") + } + + (ty::ConstKind::Infer(InferConst::Var(vid)), _) => { + return self.unify_const_variable(relation.param_env(), vid, b, a_is_expected); + } + + (_, ty::ConstKind::Infer(InferConst::Var(vid))) => { + return self.unify_const_variable(relation.param_env(), vid, a, !a_is_expected); + } + (ty::ConstKind::Unevaluated(..), _) if self.tcx.lazy_normalization() => { + // FIXME(#59490): Need to remove the leak check to accommodate + // escaping bound variables here. + if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() { + relation.const_equate_obligation(a, b); + } + return Ok(b); + } + (_, ty::ConstKind::Unevaluated(..)) if self.tcx.lazy_normalization() => { + // FIXME(#59490): Need to remove the leak check to accommodate + // escaping bound variables here. + if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() { + relation.const_equate_obligation(a, b); + } + return Ok(a); + } + _ => {} + } + + ty::relate::super_relate_consts(relation, a, b) + } + + /// Unifies the const variable `target_vid` with the given constant. + /// + /// This also tests if the given const `ct` contains an inference variable which was previously + /// unioned with `target_vid`. If this is the case, inferring `target_vid` to `ct` + /// would result in an infinite type as we continuously replace an inference variable + /// in `ct` with `ct` itself. + /// + /// This is especially important as unevaluated consts use their parents generics. + /// They therefore often contain unused substs, making these errors far more likely. + /// + /// A good example of this is the following: + /// + /// ```compile_fail,E0308 + /// #![feature(generic_const_exprs)] + /// + /// fn bind<const N: usize>(value: [u8; N]) -> [u8; 3 + 4] { + /// todo!() + /// } + /// + /// fn main() { + /// let mut arr = Default::default(); + /// arr = bind(arr); + /// } + /// ``` + /// + /// Here `3 + 4` ends up as `ConstKind::Unevaluated` which uses the generics + /// of `fn bind` (meaning that its substs contain `N`). + /// + /// `bind(arr)` now infers that the type of `arr` must be `[u8; N]`. + /// The assignment `arr = bind(arr)` now tries to equate `N` with `3 + 4`. + /// + /// As `3 + 4` contains `N` in its substs, this must not succeed. + /// + /// See `src/test/ui/const-generics/occurs-check/` for more examples where this is relevant. + #[instrument(level = "debug", skip(self))] + fn unify_const_variable( + &self, + param_env: ty::ParamEnv<'tcx>, + target_vid: ty::ConstVid<'tcx>, + ct: ty::Const<'tcx>, + vid_is_expected: bool, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + let (for_universe, span) = { + let mut inner = self.inner.borrow_mut(); + let variable_table = &mut inner.const_unification_table(); + let var_value = variable_table.probe_value(target_vid); + match var_value.val { + ConstVariableValue::Known { value } => { + bug!("instantiating {:?} which has a known value {:?}", target_vid, value) + } + ConstVariableValue::Unknown { universe } => (universe, var_value.origin.span), + } + }; + let value = ConstInferUnifier { infcx: self, span, param_env, for_universe, target_vid } + .relate(ct, ct)?; + + self.inner + .borrow_mut() + .const_unification_table() + .unify_var_value( + target_vid, + ConstVarValue { + origin: ConstVariableOrigin { + kind: ConstVariableOriginKind::ConstInference, + span: DUMMY_SP, + }, + val: ConstVariableValue::Known { value }, + }, + ) + .map(|()| value) + .map_err(|e| const_unification_error(vid_is_expected, e)) + } + + fn unify_integral_variable( + &self, + vid_is_expected: bool, + vid: ty::IntVid, + val: ty::IntVarValue, + ) -> RelateResult<'tcx, Ty<'tcx>> { + self.inner + .borrow_mut() + .int_unification_table() + .unify_var_value(vid, Some(val)) + .map_err(|e| int_unification_error(vid_is_expected, e))?; + match val { + IntType(v) => Ok(self.tcx.mk_mach_int(v)), + UintType(v) => Ok(self.tcx.mk_mach_uint(v)), + } + } + + fn unify_float_variable( + &self, + vid_is_expected: bool, + vid: ty::FloatVid, + val: ty::FloatTy, + ) -> RelateResult<'tcx, Ty<'tcx>> { + self.inner + .borrow_mut() + .float_unification_table() + .unify_var_value(vid, Some(ty::FloatVarValue(val))) + .map_err(|e| float_unification_error(vid_is_expected, e))?; + Ok(self.tcx.mk_mach_float(val)) + } +} + +impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> { + pub fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + pub fn equate<'a>(&'a mut self, a_is_expected: bool) -> Equate<'a, 'infcx, 'tcx> { + Equate::new(self, a_is_expected) + } + + pub fn sub<'a>(&'a mut self, a_is_expected: bool) -> Sub<'a, 'infcx, 'tcx> { + Sub::new(self, a_is_expected) + } + + pub fn lub<'a>(&'a mut self, a_is_expected: bool) -> Lub<'a, 'infcx, 'tcx> { + Lub::new(self, a_is_expected) + } + + pub fn glb<'a>(&'a mut self, a_is_expected: bool) -> Glb<'a, 'infcx, 'tcx> { + Glb::new(self, a_is_expected) + } + + /// Here, `dir` is either `EqTo`, `SubtypeOf`, or `SupertypeOf`. + /// The idea is that we should ensure that the type `a_ty` is equal + /// to, a subtype of, or a supertype of (respectively) the type + /// to which `b_vid` is bound. + /// + /// Since `b_vid` has not yet been instantiated with a type, we + /// will first instantiate `b_vid` with a *generalized* version + /// of `a_ty`. Generalization introduces other inference + /// variables wherever subtyping could occur. + #[instrument(skip(self), level = "debug")] + pub fn instantiate( + &mut self, + a_ty: Ty<'tcx>, + dir: RelationDir, + b_vid: ty::TyVid, + a_is_expected: bool, + ) -> RelateResult<'tcx, ()> { + use self::RelationDir::*; + + // Get the actual variable that b_vid has been inferred to + debug_assert!(self.infcx.inner.borrow_mut().type_variables().probe(b_vid).is_unknown()); + + // Generalize type of `a_ty` appropriately depending on the + // direction. As an example, assume: + // + // - `a_ty == &'x ?1`, where `'x` is some free region and `?1` is an + // inference variable, + // - and `dir` == `SubtypeOf`. + // + // Then the generalized form `b_ty` would be `&'?2 ?3`, where + // `'?2` and `?3` are fresh region/type inference + // variables. (Down below, we will relate `a_ty <: b_ty`, + // adding constraints like `'x: '?2` and `?1 <: ?3`.) + let Generalization { ty: b_ty, needs_wf } = self.generalize(a_ty, b_vid, dir)?; + debug!(?b_ty); + self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty); + + if needs_wf { + self.obligations.push(Obligation::new( + self.trace.cause.clone(), + self.param_env, + ty::Binder::dummy(ty::PredicateKind::WellFormed(b_ty.into())) + .to_predicate(self.infcx.tcx), + )); + } + + // Finally, relate `b_ty` to `a_ty`, as described in previous comment. + // + // FIXME(#16847): This code is non-ideal because all these subtype + // relations wind up attributed to the same spans. We need + // to associate causes/spans with each of the relations in + // the stack to get this right. + match dir { + EqTo => self.equate(a_is_expected).relate(a_ty, b_ty), + SubtypeOf => self.sub(a_is_expected).relate(a_ty, b_ty), + SupertypeOf => self.sub(a_is_expected).relate_with_variance( + ty::Contravariant, + ty::VarianceDiagInfo::default(), + a_ty, + b_ty, + ), + }?; + + Ok(()) + } + + /// Attempts to generalize `ty` for the type variable `for_vid`. + /// This checks for cycle -- that is, whether the type `ty` + /// references `for_vid`. The `dir` is the "direction" for which we + /// a performing the generalization (i.e., are we producing a type + /// that can be used as a supertype etc). + /// + /// Preconditions: + /// + /// - `for_vid` is a "root vid" + #[instrument(skip(self), level = "trace")] + fn generalize( + &self, + ty: Ty<'tcx>, + for_vid: ty::TyVid, + dir: RelationDir, + ) -> RelateResult<'tcx, Generalization<'tcx>> { + // Determine the ambient variance within which `ty` appears. + // The surrounding equation is: + // + // ty [op] ty2 + // + // where `op` is either `==`, `<:`, or `:>`. This maps quite + // naturally. + let ambient_variance = match dir { + RelationDir::EqTo => ty::Invariant, + RelationDir::SubtypeOf => ty::Covariant, + RelationDir::SupertypeOf => ty::Contravariant, + }; + + trace!(?ambient_variance); + + let for_universe = match self.infcx.inner.borrow_mut().type_variables().probe(for_vid) { + v @ TypeVariableValue::Known { .. } => { + bug!("instantiating {:?} which has a known value {:?}", for_vid, v,) + } + TypeVariableValue::Unknown { universe } => universe, + }; + + trace!(?for_universe); + trace!(?self.trace); + + let mut generalize = Generalizer { + infcx: self.infcx, + cause: &self.trace.cause, + for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables().sub_root_var(for_vid), + for_universe, + ambient_variance, + needs_wf: false, + root_ty: ty, + param_env: self.param_env, + cache: SsoHashMap::new(), + }; + + let ty = match generalize.relate(ty, ty) { + Ok(ty) => ty, + Err(e) => { + debug!(?e, "failure"); + return Err(e); + } + }; + let needs_wf = generalize.needs_wf; + trace!(?ty, ?needs_wf, "success"); + Ok(Generalization { ty, needs_wf }) + } + + pub fn add_const_equate_obligation( + &mut self, + a_is_expected: bool, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) { + let predicate = if a_is_expected { + ty::PredicateKind::ConstEquate(a, b) + } else { + ty::PredicateKind::ConstEquate(b, a) + }; + self.obligations.push(Obligation::new( + self.trace.cause.clone(), + self.param_env, + ty::Binder::dummy(predicate).to_predicate(self.tcx()), + )); + } +} + +struct Generalizer<'cx, 'tcx> { + infcx: &'cx InferCtxt<'cx, 'tcx>, + + /// The span, used when creating new type variables and things. + cause: &'cx ObligationCause<'tcx>, + + /// The vid of the type variable that is in the process of being + /// instantiated; if we find this within the type we are folding, + /// that means we would have created a cyclic type. + for_vid_sub_root: ty::TyVid, + + /// The universe of the type variable that is in the process of + /// being instantiated. Any fresh variables that we create in this + /// process should be in that same universe. + for_universe: ty::UniverseIndex, + + /// Track the variance as we descend into the type. + ambient_variance: ty::Variance, + + /// See the field `needs_wf` in `Generalization`. + needs_wf: bool, + + /// The root type that we are generalizing. Used when reporting cycles. + root_ty: Ty<'tcx>, + + param_env: ty::ParamEnv<'tcx>, + + cache: SsoHashMap<Ty<'tcx>, RelateResult<'tcx, Ty<'tcx>>>, +} + +/// Result from a generalization operation. This includes +/// not only the generalized type, but also a bool flag +/// indicating whether further WF checks are needed. +struct Generalization<'tcx> { + ty: Ty<'tcx>, + + /// If true, then the generalized type may not be well-formed, + /// even if the source type is well-formed, so we should add an + /// additional check to enforce that it is. This arises in + /// particular around 'bivariant' type parameters that are only + /// constrained by a where-clause. As an example, imagine a type: + /// + /// struct Foo<A, B> where A: Iterator<Item = B> { + /// data: A + /// } + /// + /// here, `A` will be covariant, but `B` is + /// unconstrained. However, whatever it is, for `Foo` to be WF, it + /// must be equal to `A::Item`. If we have an input `Foo<?A, ?B>`, + /// then after generalization we will wind up with a type like + /// `Foo<?C, ?D>`. When we enforce that `Foo<?A, ?B> <: Foo<?C, + /// ?D>` (or `>:`), we will wind up with the requirement that `?A + /// <: ?C`, but no particular relationship between `?B` and `?D` + /// (after all, we do not know the variance of the normalized form + /// of `A::Item` with respect to `A`). If we do nothing else, this + /// may mean that `?D` goes unconstrained (as in #41677). So, in + /// this scenario where we create a new type variable in a + /// bivariant context, we set the `needs_wf` flag to true. This + /// will force the calling code to check that `WF(Foo<?C, ?D>)` + /// holds, which in turn implies that `?C::Item == ?D`. So once + /// `?C` is constrained, that should suffice to restrict `?D`. + needs_wf: bool, +} + +impl<'tcx> TypeRelation<'tcx> for Generalizer<'_, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } + + fn tag(&self) -> &'static str { + "Generalizer" + } + + fn a_is_expected(&self) -> bool { + true + } + + fn binders<T>( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) + } + + fn relate_item_substs( + &mut self, + item_def_id: DefId, + a_subst: SubstsRef<'tcx>, + b_subst: SubstsRef<'tcx>, + ) -> RelateResult<'tcx, SubstsRef<'tcx>> { + if self.ambient_variance == ty::Variance::Invariant { + // Avoid fetching the variance if we are in an invariant + // context; no need, and it can induce dependency cycles + // (e.g., #41849). + relate::relate_substs(self, a_subst, b_subst) + } else { + let tcx = self.tcx(); + let opt_variances = tcx.variances_of(item_def_id); + relate::relate_substs_with_variances( + self, + item_def_id, + &opt_variances, + a_subst, + b_subst, + ) + } + } + + fn relate_with_variance<T: Relate<'tcx>>( + &mut self, + variance: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + + let result = self.relate(a, b); + self.ambient_variance = old_ambient_variance; + result + } + + fn tys(&mut self, t: Ty<'tcx>, t2: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + assert_eq!(t, t2); // we are abusing TypeRelation here; both LHS and RHS ought to be == + + if let Some(result) = self.cache.get(&t) { + return result.clone(); + } + debug!("generalize: t={:?}", t); + + // Check to see whether the type we are generalizing references + // any other type variable related to `vid` via + // subtyping. This is basically our "occurs check", preventing + // us from creating infinitely sized types. + let result = match *t.kind() { + ty::Infer(ty::TyVar(vid)) => { + let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid); + let sub_vid = self.infcx.inner.borrow_mut().type_variables().sub_root_var(vid); + if sub_vid == self.for_vid_sub_root { + // If sub-roots are equal, then `for_vid` and + // `vid` are related via subtyping. + Err(TypeError::CyclicTy(self.root_ty)) + } else { + let probe = self.infcx.inner.borrow_mut().type_variables().probe(vid); + match probe { + TypeVariableValue::Known { value: u } => { + debug!("generalize: known value {:?}", u); + self.relate(u, u) + } + TypeVariableValue::Unknown { universe } => { + match self.ambient_variance { + // Invariant: no need to make a fresh type variable. + ty::Invariant => { + if self.for_universe.can_name(universe) { + return Ok(t); + } + } + + // Bivariant: make a fresh var, but we + // may need a WF predicate. See + // comment on `needs_wf` field for + // more info. + ty::Bivariant => self.needs_wf = true, + + // Co/contravariant: this will be + // sufficiently constrained later on. + ty::Covariant | ty::Contravariant => (), + } + + let origin = + *self.infcx.inner.borrow_mut().type_variables().var_origin(vid); + let new_var_id = self + .infcx + .inner + .borrow_mut() + .type_variables() + .new_var(self.for_universe, origin); + let u = self.tcx().mk_ty_var(new_var_id); + + // Record that we replaced `vid` with `new_var_id` as part of a generalization + // operation. This is needed to detect cyclic types. To see why, see the + // docs in the `type_variables` module. + self.infcx.inner.borrow_mut().type_variables().sub(vid, new_var_id); + debug!("generalize: replacing original vid={:?} with new={:?}", vid, u); + Ok(u) + } + } + } + } + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => { + // No matter what mode we are in, + // integer/floating-point types must be equal to be + // relatable. + Ok(t) + } + _ => relate::super_relate_tys(self, t, t), + }; + + self.cache.insert(t, result.clone()); + return result; + } + + fn regions( + &mut self, + r: ty::Region<'tcx>, + r2: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + assert_eq!(r, r2); // we are abusing TypeRelation here; both LHS and RHS ought to be == + + debug!("generalize: regions r={:?}", r); + + match *r { + // Never make variables for regions bound within the type itself, + // nor for erased regions. + ty::ReLateBound(..) | ty::ReErased => { + return Ok(r); + } + + ty::RePlaceholder(..) + | ty::ReVar(..) + | ty::ReEmpty(_) + | ty::ReStatic + | ty::ReEarlyBound(..) + | ty::ReFree(..) => { + // see common code below + } + } + + // If we are in an invariant context, we can re-use the region + // as is, unless it happens to be in some universe that we + // can't name. (In the case of a region *variable*, we could + // use it if we promoted it into our universe, but we don't + // bother.) + if let ty::Invariant = self.ambient_variance { + let r_universe = self.infcx.universe_of_region(r); + if self.for_universe.can_name(r_universe) { + return Ok(r); + } + } + + // FIXME: This is non-ideal because we don't give a + // very descriptive origin for this region variable. + Ok(self.infcx.next_region_var_in_universe(MiscVariable(self.cause.span), self.for_universe)) + } + + fn consts( + &mut self, + c: ty::Const<'tcx>, + c2: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + assert_eq!(c, c2); // we are abusing TypeRelation here; both LHS and RHS ought to be == + + match c.kind() { + ty::ConstKind::Infer(InferConst::Var(vid)) => { + let mut inner = self.infcx.inner.borrow_mut(); + let variable_table = &mut inner.const_unification_table(); + let var_value = variable_table.probe_value(vid); + match var_value.val { + ConstVariableValue::Known { value: u } => { + drop(inner); + self.relate(u, u) + } + ConstVariableValue::Unknown { universe } => { + if self.for_universe.can_name(universe) { + Ok(c) + } else { + let new_var_id = variable_table.new_key(ConstVarValue { + origin: var_value.origin, + val: ConstVariableValue::Unknown { universe: self.for_universe }, + }); + Ok(self.tcx().mk_const_var(new_var_id, c.ty())) + } + } + } + } + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) + if self.tcx().lazy_normalization() => + { + assert_eq!(promoted, None); + let substs = self.relate_with_variance( + ty::Variance::Invariant, + ty::VarianceDiagInfo::default(), + substs, + substs, + )?; + Ok(self.tcx().mk_const(ty::ConstS { + ty: c.ty(), + kind: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }), + })) + } + _ => relate::super_relate_consts(self, c, c), + } + } +} + +pub trait ConstEquateRelation<'tcx>: TypeRelation<'tcx> { + /// Register an obligation that both constants must be equal to each other. + /// + /// If they aren't equal then the relation doesn't hold. + fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>); +} + +pub fn const_unification_error<'tcx>( + a_is_expected: bool, + (a, b): (ty::Const<'tcx>, ty::Const<'tcx>), +) -> TypeError<'tcx> { + TypeError::ConstMismatch(ExpectedFound::new(a_is_expected, a, b)) +} + +fn int_unification_error<'tcx>( + a_is_expected: bool, + v: (ty::IntVarValue, ty::IntVarValue), +) -> TypeError<'tcx> { + let (a, b) = v; + TypeError::IntMismatch(ExpectedFound::new(a_is_expected, a, b)) +} + +fn float_unification_error<'tcx>( + a_is_expected: bool, + v: (ty::FloatVarValue, ty::FloatVarValue), +) -> TypeError<'tcx> { + let (ty::FloatVarValue(a), ty::FloatVarValue(b)) = v; + TypeError::FloatMismatch(ExpectedFound::new(a_is_expected, a, b)) +} + +struct ConstInferUnifier<'cx, 'tcx> { + infcx: &'cx InferCtxt<'cx, 'tcx>, + + span: Span, + + param_env: ty::ParamEnv<'tcx>, + + for_universe: ty::UniverseIndex, + + /// The vid of the const variable that is in the process of being + /// instantiated; if we find this within the const we are folding, + /// that means we would have created a cyclic const. + target_vid: ty::ConstVid<'tcx>, +} + +// We use `TypeRelation` here to propagate `RelateResult` upwards. +// +// Both inputs are expected to be the same. +impl<'tcx> TypeRelation<'tcx> for ConstInferUnifier<'_, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } + + fn tag(&self) -> &'static str { + "ConstInferUnifier" + } + + fn a_is_expected(&self) -> bool { + true + } + + fn relate_with_variance<T: Relate<'tcx>>( + &mut self, + _variance: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + // We don't care about variance here. + self.relate(a, b) + } + + fn binders<T>( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + Ok(a.rebind(self.relate(a.skip_binder(), b.skip_binder())?)) + } + + #[tracing::instrument(level = "debug", skip(self))] + fn tys(&mut self, t: Ty<'tcx>, _t: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + debug_assert_eq!(t, _t); + debug!("ConstInferUnifier: t={:?}", t); + + match t.kind() { + &ty::Infer(ty::TyVar(vid)) => { + let vid = self.infcx.inner.borrow_mut().type_variables().root_var(vid); + let probe = self.infcx.inner.borrow_mut().type_variables().probe(vid); + match probe { + TypeVariableValue::Known { value: u } => { + debug!("ConstOccursChecker: known value {:?}", u); + self.tys(u, u) + } + TypeVariableValue::Unknown { universe } => { + if self.for_universe.can_name(universe) { + return Ok(t); + } + + let origin = + *self.infcx.inner.borrow_mut().type_variables().var_origin(vid); + let new_var_id = self + .infcx + .inner + .borrow_mut() + .type_variables() + .new_var(self.for_universe, origin); + let u = self.tcx().mk_ty_var(new_var_id); + debug!( + "ConstInferUnifier: replacing original vid={:?} with new={:?}", + vid, u + ); + Ok(u) + } + } + } + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => Ok(t), + _ => relate::super_relate_tys(self, t, t), + } + } + + fn regions( + &mut self, + r: ty::Region<'tcx>, + _r: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug_assert_eq!(r, _r); + debug!("ConstInferUnifier: r={:?}", r); + + match *r { + // Never make variables for regions bound within the type itself, + // nor for erased regions. + ty::ReLateBound(..) | ty::ReErased => { + return Ok(r); + } + + ty::RePlaceholder(..) + | ty::ReVar(..) + | ty::ReEmpty(_) + | ty::ReStatic + | ty::ReEarlyBound(..) + | ty::ReFree(..) => { + // see common code below + } + } + + let r_universe = self.infcx.universe_of_region(r); + if self.for_universe.can_name(r_universe) { + return Ok(r); + } else { + // FIXME: This is non-ideal because we don't give a + // very descriptive origin for this region variable. + Ok(self.infcx.next_region_var_in_universe(MiscVariable(self.span), self.for_universe)) + } + } + + #[tracing::instrument(level = "debug", skip(self))] + fn consts( + &mut self, + c: ty::Const<'tcx>, + _c: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + debug_assert_eq!(c, _c); + debug!("ConstInferUnifier: c={:?}", c); + + match c.kind() { + ty::ConstKind::Infer(InferConst::Var(vid)) => { + // Check if the current unification would end up + // unifying `target_vid` with a const which contains + // an inference variable which is unioned with `target_vid`. + // + // Not doing so can easily result in stack overflows. + if self + .infcx + .inner + .borrow_mut() + .const_unification_table() + .unioned(self.target_vid, vid) + { + return Err(TypeError::CyclicConst(c)); + } + + let var_value = + self.infcx.inner.borrow_mut().const_unification_table().probe_value(vid); + match var_value.val { + ConstVariableValue::Known { value: u } => self.consts(u, u), + ConstVariableValue::Unknown { universe } => { + if self.for_universe.can_name(universe) { + Ok(c) + } else { + let new_var_id = + self.infcx.inner.borrow_mut().const_unification_table().new_key( + ConstVarValue { + origin: var_value.origin, + val: ConstVariableValue::Unknown { + universe: self.for_universe, + }, + }, + ); + Ok(self.tcx().mk_const_var(new_var_id, c.ty())) + } + } + } + } + ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }) + if self.tcx().lazy_normalization() => + { + assert_eq!(promoted, None); + let substs = self.relate_with_variance( + ty::Variance::Invariant, + ty::VarianceDiagInfo::default(), + substs, + substs, + )?; + Ok(self.tcx().mk_const(ty::ConstS { + ty: c.ty(), + kind: ty::ConstKind::Unevaluated(ty::Unevaluated { def, substs, promoted }), + })) + } + _ => relate::super_relate_consts(self, c, c), + } + } +} diff --git a/compiler/rustc_infer/src/infer/equate.rs b/compiler/rustc_infer/src/infer/equate.rs new file mode 100644 index 000000000..3b1798ca7 --- /dev/null +++ b/compiler/rustc_infer/src/infer/equate.rs @@ -0,0 +1,169 @@ +use super::combine::{CombineFields, ConstEquateRelation, RelationDir}; +use super::Subtype; + +use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::TyVar; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +use rustc_hir::def_id::DefId; + +/// Ensures `a` is made equal to `b`. Returns `a` on success. +pub struct Equate<'combine, 'infcx, 'tcx> { + fields: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, +} + +impl<'combine, 'infcx, 'tcx> Equate<'combine, 'infcx, 'tcx> { + pub fn new( + fields: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, + ) -> Equate<'combine, 'infcx, 'tcx> { + Equate { fields, a_is_expected } + } +} + +impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { + fn tag(&self) -> &'static str { + "Equate" + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.fields.tcx() + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.fields.param_env + } + + fn a_is_expected(&self) -> bool { + self.a_is_expected + } + + fn relate_item_substs( + &mut self, + _item_def_id: DefId, + a_subst: SubstsRef<'tcx>, + b_subst: SubstsRef<'tcx>, + ) -> RelateResult<'tcx, SubstsRef<'tcx>> { + // N.B., once we are equating types, we don't care about + // variance, so don't try to lookup the variance here. This + // also avoids some cycles (e.g., #41849) since looking up + // variance requires computing types which can require + // performing trait matching (which then performs equality + // unification). + + relate::relate_substs(self, a_subst, b_subst) + } + + fn relate_with_variance<T: Relate<'tcx>>( + &mut self, + _: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + self.relate(a, b) + } + + #[instrument(skip(self), level = "debug")] + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + if a == b { + return Ok(a); + } + + trace!(a = ?a.kind(), b = ?b.kind()); + + let infcx = self.fields.infcx; + + let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); + let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); + + match (a.kind(), b.kind()) { + (&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => { + infcx.inner.borrow_mut().type_variables().equate(a_id, b_id); + } + + (&ty::Infer(TyVar(a_id)), _) => { + self.fields.instantiate(b, RelationDir::EqTo, a_id, self.a_is_expected)?; + } + + (_, &ty::Infer(TyVar(b_id))) => { + self.fields.instantiate(a, RelationDir::EqTo, b_id, self.a_is_expected)?; + } + + (&ty::Opaque(a_def_id, _), &ty::Opaque(b_def_id, _)) if a_def_id == b_def_id => { + self.fields.infcx.super_combine_tys(self, a, b)?; + } + (&ty::Opaque(did, ..), _) | (_, &ty::Opaque(did, ..)) + if self.fields.define_opaque_types && did.is_local() => + { + self.fields.obligations.extend( + infcx + .handle_opaque_type( + a, + b, + self.a_is_expected(), + &self.fields.trace.cause, + self.param_env(), + )? + .obligations, + ); + } + + _ => { + self.fields.infcx.super_combine_tys(self, a, b)?; + } + } + + Ok(a) + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("{}.regions({:?}, {:?})", self.tag(), a, b); + let origin = Subtype(Box::new(self.fields.trace.clone())); + self.fields + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .make_eqregion(origin, a, b); + Ok(a) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + self.fields.infcx.super_combine_consts(self, a, b) + } + + fn binders<T>( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { + self.fields.higher_ranked_sub(a, b, self.a_is_expected)?; + self.fields.higher_ranked_sub(b, a, self.a_is_expected)?; + } else { + // Fast path for the common case. + self.relate(a.skip_binder(), b.skip_binder())?; + } + Ok(a) + } +} + +impl<'tcx> ConstEquateRelation<'tcx> for Equate<'_, '_, 'tcx> { + fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { + self.fields.add_const_equate_obligation(self.a_is_expected, a, b); + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs new file mode 100644 index 000000000..20864c657 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -0,0 +1,3121 @@ +//! Error Reporting Code for the inference engine +//! +//! Because of the way inference, and in particular region inference, +//! works, it often happens that errors are not detected until far after +//! the relevant line of code has been type-checked. Therefore, there is +//! an elaborate system to track why a particular constraint in the +//! inference graph arose so that we can explain to the user what gave +//! rise to a particular error. +//! +//! The system is based around a set of "origin" types. An "origin" is the +//! reason that a constraint or inference variable arose. There are +//! different "origin" enums for different kinds of constraints/variables +//! (e.g., `TypeOrigin`, `RegionVariableOrigin`). An origin always has +//! a span, but also more information so that we can generate a meaningful +//! error message. +//! +//! Having a catalog of all the different reasons an error can arise is +//! also useful for other reasons, like cross-referencing FAQs etc, though +//! we are not really taking advantage of this yet. +//! +//! # Region Inference +//! +//! Region inference is particularly tricky because it always succeeds "in +//! the moment" and simply registers a constraint. Then, at the end, we +//! can compute the full graph and report errors, so we need to be able to +//! store and later report what gave rise to the conflicting constraints. +//! +//! # Subtype Trace +//! +//! Determining whether `T1 <: T2` often involves a number of subtypes and +//! subconstraints along the way. A "TypeTrace" is an extended version +//! of an origin that traces the types and other values that were being +//! compared. It is not necessarily comprehensive (in fact, at the time of +//! this writing it only tracks the root values being compared) but I'd +//! like to extend it to include significant "waypoints". For example, if +//! you are comparing `(T1, T2) <: (T3, T4)`, and the problem is that `T2 +//! <: T4` fails, I'd like the trace to include enough information to say +//! "in the 2nd element of the tuple". Similarly, failures when comparing +//! arguments or return types in fn types should be able to cite the +//! specific position, etc. +//! +//! # Reality vs plan +//! +//! Of course, there is still a LOT of code in typeck that has yet to be +//! ported to this system, and which relies on string concatenation at the +//! time of error detection. + +use super::lexical_region_resolve::RegionResolutionError; +use super::region_constraints::GenericKind; +use super::{InferCtxt, RegionVariableOrigin, SubregionOrigin, TypeTrace, ValuePairs}; + +use crate::infer; +use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; +use crate::traits::error_reporting::report_object_safety_error; +use crate::traits::{ + IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, + StatementAsExpression, +}; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed}; +use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan}; +use rustc_hir as hir; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_hir::lang_items::LangItem; +use rustc_hir::Node; +use rustc_middle::dep_graph::DepContext; +use rustc_middle::ty::print::with_no_trimmed_paths; +use rustc_middle::ty::{ + self, error::TypeError, Binder, List, Region, Subst, Ty, TyCtxt, TypeFoldable, + TypeSuperVisitable, TypeVisitable, +}; +use rustc_span::{sym, symbol::kw, BytePos, DesugaringKind, Pos, Span}; +use rustc_target::spec::abi; +use std::ops::ControlFlow; +use std::{cmp, fmt, iter}; + +mod note; + +mod need_type_info; +pub use need_type_info::TypeAnnotationNeeded; + +pub mod nice_region_error; + +pub(super) fn note_and_explain_region<'tcx>( + tcx: TyCtxt<'tcx>, + err: &mut Diagnostic, + prefix: &str, + region: ty::Region<'tcx>, + suffix: &str, + alt_span: Option<Span>, +) { + let (description, span) = match *region { + ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic => { + msg_span_from_free_region(tcx, region, alt_span) + } + + ty::ReEmpty(ty::UniverseIndex::ROOT) => ("the empty lifetime".to_owned(), alt_span), + + // uh oh, hope no user ever sees THIS + ty::ReEmpty(ui) => (format!("the empty lifetime in universe {:?}", ui), alt_span), + + ty::RePlaceholder(_) => return, + + // FIXME(#13998) RePlaceholder should probably print like + // ReFree rather than dumping Debug output on the user. + // + // We shouldn't really be having unification failures with ReVar + // and ReLateBound though. + ty::ReVar(_) | ty::ReLateBound(..) | ty::ReErased => { + (format!("lifetime {:?}", region), alt_span) + } + }; + + emit_msg_span(err, prefix, description, span, suffix); +} + +fn explain_free_region<'tcx>( + tcx: TyCtxt<'tcx>, + err: &mut Diagnostic, + prefix: &str, + region: ty::Region<'tcx>, + suffix: &str, +) { + let (description, span) = msg_span_from_free_region(tcx, region, None); + + label_msg_span(err, prefix, description, span, suffix); +} + +fn msg_span_from_free_region<'tcx>( + tcx: TyCtxt<'tcx>, + region: ty::Region<'tcx>, + alt_span: Option<Span>, +) -> (String, Option<Span>) { + match *region { + ty::ReEarlyBound(_) | ty::ReFree(_) => { + let (msg, span) = msg_span_from_early_bound_and_free_regions(tcx, region); + (msg, Some(span)) + } + ty::ReStatic => ("the static lifetime".to_owned(), alt_span), + ty::ReEmpty(ty::UniverseIndex::ROOT) => ("an empty lifetime".to_owned(), alt_span), + ty::ReEmpty(ui) => (format!("an empty lifetime in universe {:?}", ui), alt_span), + _ => bug!("{:?}", region), + } +} + +fn msg_span_from_early_bound_and_free_regions<'tcx>( + tcx: TyCtxt<'tcx>, + region: ty::Region<'tcx>, +) -> (String, Span) { + let scope = region.free_region_binding_scope(tcx).expect_local(); + match *region { + ty::ReEarlyBound(ref br) => { + let mut sp = tcx.def_span(scope); + if let Some(param) = + tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) + { + sp = param.span; + } + let text = if br.has_name() { + format!("the lifetime `{}` as defined here", br.name) + } else { + format!("the anonymous lifetime as defined here") + }; + (text, sp) + } + ty::ReFree(ref fr) => { + if !fr.bound_region.is_named() + && let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region) + { + ("the anonymous lifetime defined here".to_string(), ty.span) + } else { + match fr.bound_region { + ty::BoundRegionKind::BrNamed(_, name) => { + let mut sp = tcx.def_span(scope); + if let Some(param) = + tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name)) + { + sp = param.span; + } + let text = if name == kw::UnderscoreLifetime { + format!("the anonymous lifetime as defined here") + } else { + format!("the lifetime `{}` as defined here", name) + }; + (text, sp) + } + ty::BrAnon(idx) => ( + format!("the anonymous lifetime #{} defined here", idx + 1), + tcx.def_span(scope) + ), + _ => ( + format!("the lifetime `{}` as defined here", region), + tcx.def_span(scope), + ), + } + } + } + _ => bug!(), + } +} + +fn emit_msg_span( + err: &mut Diagnostic, + prefix: &str, + description: String, + span: Option<Span>, + suffix: &str, +) { + let message = format!("{}{}{}", prefix, description, suffix); + + if let Some(span) = span { + err.span_note(span, &message); + } else { + err.note(&message); + } +} + +fn label_msg_span( + err: &mut Diagnostic, + prefix: &str, + description: String, + span: Option<Span>, + suffix: &str, +) { + let message = format!("{}{}{}", prefix, description, suffix); + + if let Some(span) = span { + err.span_label(span, &message); + } else { + err.note(&message); + } +} + +pub fn unexpected_hidden_region_diagnostic<'tcx>( + tcx: TyCtxt<'tcx>, + span: Span, + hidden_ty: Ty<'tcx>, + hidden_region: ty::Region<'tcx>, + opaque_ty: ty::OpaqueTypeKey<'tcx>, +) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let opaque_ty = tcx.mk_opaque(opaque_ty.def_id.to_def_id(), opaque_ty.substs); + let mut err = struct_span_err!( + tcx.sess, + span, + E0700, + "hidden type for `{opaque_ty}` captures lifetime that does not appear in bounds", + ); + + // Explain the region we are capturing. + match *hidden_region { + ty::ReEmpty(ty::UniverseIndex::ROOT) => { + // All lifetimes shorter than the function body are `empty` in + // lexical region resolution. The default explanation of "an empty + // lifetime" isn't really accurate here. + let message = format!( + "hidden type `{}` captures lifetime smaller than the function body", + hidden_ty + ); + err.span_note(span, &message); + } + ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReStatic | ty::ReEmpty(_) => { + // Assuming regionck succeeded (*), we ought to always be + // capturing *some* region from the fn header, and hence it + // ought to be free. So under normal circumstances, we will go + // down this path which gives a decent human readable + // explanation. + // + // (*) if not, the `tainted_by_errors` field would be set to + // `Some(ErrorGuaranteed)` in any case, so we wouldn't be here at all. + explain_free_region( + tcx, + &mut err, + &format!("hidden type `{}` captures ", hidden_ty), + hidden_region, + "", + ); + if let Some(reg_info) = tcx.is_suitable_region(hidden_region) { + let fn_returns = tcx.return_type_impl_or_dyn_traits(reg_info.def_id); + nice_region_error::suggest_new_region_bound( + tcx, + &mut err, + fn_returns, + hidden_region.to_string(), + None, + format!("captures `{}`", hidden_region), + None, + ) + } + } + _ => { + // Ugh. This is a painful case: the hidden region is not one + // that we can easily summarize or explain. This can happen + // in a case like + // `src/test/ui/multiple-lifetimes/ordinary-bounds-unsuited.rs`: + // + // ``` + // fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b> { + // if condition() { a } else { b } + // } + // ``` + // + // Here the captured lifetime is the intersection of `'a` and + // `'b`, which we can't quite express. + + // We can at least report a really cryptic error for now. + note_and_explain_region( + tcx, + &mut err, + &format!("hidden type `{}` captures ", hidden_ty), + hidden_region, + "", + None, + ); + } + } + + err +} + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + pub fn report_region_errors( + &self, + generic_param_scope: LocalDefId, + errors: &[RegionResolutionError<'tcx>], + ) { + debug!("report_region_errors(): {} errors to start", errors.len()); + + // try to pre-process the errors, which will group some of them + // together into a `ProcessedErrors` group: + let errors = self.process_errors(errors); + + debug!("report_region_errors: {} errors after preprocessing", errors.len()); + + for error in errors { + debug!("report_region_errors: error = {:?}", error); + + if !self.try_report_nice_region_error(&error) { + match error.clone() { + // These errors could indicate all manner of different + // problems with many different solutions. Rather + // than generate a "one size fits all" error, what we + // attempt to do is go through a number of specific + // scenarios and try to find the best way to present + // the error. If all of these fails, we fall back to a rather + // general bit of code that displays the error information + RegionResolutionError::ConcreteFailure(origin, sub, sup) => { + if sub.is_placeholder() || sup.is_placeholder() { + self.report_placeholder_failure(origin, sub, sup).emit(); + } else { + self.report_concrete_failure(origin, sub, sup).emit(); + } + } + + RegionResolutionError::GenericBoundFailure(origin, param_ty, sub) => { + self.report_generic_bound_failure( + generic_param_scope, + origin.span(), + Some(origin), + param_ty, + sub, + ); + } + + RegionResolutionError::SubSupConflict( + _, + var_origin, + sub_origin, + sub_r, + sup_origin, + sup_r, + _, + ) => { + if sub_r.is_placeholder() { + self.report_placeholder_failure(sub_origin, sub_r, sup_r).emit(); + } else if sup_r.is_placeholder() { + self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit(); + } else { + self.report_sub_sup_conflict( + var_origin, sub_origin, sub_r, sup_origin, sup_r, + ); + } + } + + RegionResolutionError::UpperBoundUniverseConflict( + _, + _, + var_universe, + sup_origin, + sup_r, + ) => { + assert!(sup_r.is_placeholder()); + + // Make a dummy value for the "sub region" -- + // this is the initial value of the + // placeholder. In practice, we expect more + // tailored errors that don't really use this + // value. + let sub_r = self.tcx.mk_region(ty::ReEmpty(var_universe)); + + self.report_placeholder_failure(sup_origin, sub_r, sup_r).emit(); + } + } + } + } + } + + // This method goes through all the errors and try to group certain types + // of error together, for the purpose of suggesting explicit lifetime + // parameters to the user. This is done so that we can have a more + // complete view of what lifetimes should be the same. + // If the return value is an empty vector, it means that processing + // failed (so the return value of this method should not be used). + // + // The method also attempts to weed out messages that seem like + // duplicates that will be unhelpful to the end-user. But + // obviously it never weeds out ALL errors. + fn process_errors( + &self, + errors: &[RegionResolutionError<'tcx>], + ) -> Vec<RegionResolutionError<'tcx>> { + debug!("process_errors()"); + + // We want to avoid reporting generic-bound failures if we can + // avoid it: these have a very high rate of being unhelpful in + // practice. This is because they are basically secondary + // checks that test the state of the region graph after the + // rest of inference is done, and the other kinds of errors + // indicate that the region constraint graph is internally + // inconsistent, so these test results are likely to be + // meaningless. + // + // Therefore, we filter them out of the list unless they are + // the only thing in the list. + + let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e { + RegionResolutionError::GenericBoundFailure(..) => true, + RegionResolutionError::ConcreteFailure(..) + | RegionResolutionError::SubSupConflict(..) + | RegionResolutionError::UpperBoundUniverseConflict(..) => false, + }; + + let mut errors = if errors.iter().all(|e| is_bound_failure(e)) { + errors.to_owned() + } else { + errors.iter().filter(|&e| !is_bound_failure(e)).cloned().collect() + }; + + // sort the errors by span, for better error message stability. + errors.sort_by_key(|u| match *u { + RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(), + RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(), + RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _, _) => rvo.span(), + RegionResolutionError::UpperBoundUniverseConflict(_, ref rvo, _, _, _) => rvo.span(), + }); + errors + } + + /// Adds a note if the types come from similarly named crates + fn check_and_note_conflicting_crates(&self, err: &mut Diagnostic, terr: &TypeError<'tcx>) { + use hir::def_id::CrateNum; + use rustc_hir::definitions::DisambiguatedDefPathData; + use ty::print::Printer; + use ty::subst::GenericArg; + + struct AbsolutePathPrinter<'tcx> { + tcx: TyCtxt<'tcx>, + } + + struct NonTrivialPath; + + impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> { + type Error = NonTrivialPath; + + type Path = Vec<String>; + type Region = !; + type Type = !; + type DynExistential = !; + type Const = !; + + fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { + self.tcx + } + + fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> { + Err(NonTrivialPath) + } + + fn print_type(self, _ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> { + Err(NonTrivialPath) + } + + fn print_dyn_existential( + self, + _predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>, + ) -> Result<Self::DynExistential, Self::Error> { + Err(NonTrivialPath) + } + + fn print_const(self, _ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> { + Err(NonTrivialPath) + } + + fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error> { + Ok(vec![self.tcx.crate_name(cnum).to_string()]) + } + fn path_qualified( + self, + _self_ty: Ty<'tcx>, + _trait_ref: Option<ty::TraitRef<'tcx>>, + ) -> Result<Self::Path, Self::Error> { + Err(NonTrivialPath) + } + + fn path_append_impl( + self, + _print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, + _disambiguated_data: &DisambiguatedDefPathData, + _self_ty: Ty<'tcx>, + _trait_ref: Option<ty::TraitRef<'tcx>>, + ) -> Result<Self::Path, Self::Error> { + Err(NonTrivialPath) + } + fn path_append( + self, + print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, + disambiguated_data: &DisambiguatedDefPathData, + ) -> Result<Self::Path, Self::Error> { + let mut path = print_prefix(self)?; + path.push(disambiguated_data.to_string()); + Ok(path) + } + fn path_generic_args( + self, + print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>, + _args: &[GenericArg<'tcx>], + ) -> Result<Self::Path, Self::Error> { + print_prefix(self) + } + } + + let report_path_match = |err: &mut Diagnostic, did1: DefId, did2: DefId| { + // Only external crates, if either is from a local + // module we could have false positives + if !(did1.is_local() || did2.is_local()) && did1.krate != did2.krate { + let abs_path = + |def_id| AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]); + + // We compare strings because DefPath can be different + // for imported and non-imported crates + let same_path = || -> Result<_, NonTrivialPath> { + Ok(self.tcx.def_path_str(did1) == self.tcx.def_path_str(did2) + || abs_path(did1)? == abs_path(did2)?) + }; + if same_path().unwrap_or(false) { + let crate_name = self.tcx.crate_name(did1.krate); + err.note(&format!( + "perhaps two different versions of crate `{}` are being used?", + crate_name + )); + } + } + }; + match *terr { + TypeError::Sorts(ref exp_found) => { + // if they are both "path types", there's a chance of ambiguity + // due to different versions of the same crate + if let (&ty::Adt(exp_adt, _), &ty::Adt(found_adt, _)) = + (exp_found.expected.kind(), exp_found.found.kind()) + { + report_path_match(err, exp_adt.did(), found_adt.did()); + } + } + TypeError::Traits(ref exp_found) => { + report_path_match(err, exp_found.expected, exp_found.found); + } + _ => (), // FIXME(#22750) handle traits and stuff + } + } + + fn note_error_origin( + &self, + err: &mut Diagnostic, + cause: &ObligationCause<'tcx>, + exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>, + terr: &TypeError<'tcx>, + ) { + match *cause.code() { + ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => { + let ty = self.resolve_vars_if_possible(root_ty); + if !matches!(ty.kind(), ty::Infer(ty::InferTy::TyVar(_) | ty::InferTy::FreshTy(_))) + { + // don't show type `_` + if span.desugaring_kind() == Some(DesugaringKind::ForLoop) + && let ty::Adt(def, substs) = ty.kind() + && Some(def.did()) == self.tcx.get_diagnostic_item(sym::Option) + { + err.span_label(span, format!("this is an iterator with items of type `{}`", substs.type_at(0))); + } else { + err.span_label(span, format!("this expression has type `{}`", ty)); + } + } + if let Some(ty::error::ExpectedFound { found, .. }) = exp_found + && ty.is_box() && ty.boxed_ty() == found + && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) + { + err.span_suggestion( + span, + "consider dereferencing the boxed value", + format!("*{}", snippet), + Applicability::MachineApplicable, + ); + } + } + ObligationCauseCode::Pattern { origin_expr: false, span: Some(span), .. } => { + err.span_label(span, "expected due to this"); + } + ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { + arm_block_id, + arm_span, + arm_ty, + prior_arm_block_id, + prior_arm_span, + prior_arm_ty, + source, + ref prior_arms, + scrut_hir_id, + opt_suggest_box_span, + scrut_span, + .. + }) => match source { + hir::MatchSource::TryDesugar => { + if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found { + let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id); + let scrut_ty = if let hir::ExprKind::Call(_, args) = &scrut_expr.kind { + let arg_expr = args.first().expect("try desugaring call w/out arg"); + self.in_progress_typeck_results.and_then(|typeck_results| { + typeck_results.borrow().expr_ty_opt(arg_expr) + }) + } else { + bug!("try desugaring w/out call expr as scrutinee"); + }; + + match scrut_ty { + Some(ty) if expected == ty => { + let source_map = self.tcx.sess.source_map(); + err.span_suggestion( + source_map.end_point(cause.span), + "try removing this `?`", + "", + Applicability::MachineApplicable, + ); + } + _ => {} + } + } + } + _ => { + // `prior_arm_ty` can be `!`, `expected` will have better info when present. + let t = self.resolve_vars_if_possible(match exp_found { + Some(ty::error::ExpectedFound { expected, .. }) => expected, + _ => prior_arm_ty, + }); + let source_map = self.tcx.sess.source_map(); + let mut any_multiline_arm = source_map.is_multiline(arm_span); + if prior_arms.len() <= 4 { + for sp in prior_arms { + any_multiline_arm |= source_map.is_multiline(*sp); + err.span_label(*sp, format!("this is found to be of type `{}`", t)); + } + } else if let Some(sp) = prior_arms.last() { + any_multiline_arm |= source_map.is_multiline(*sp); + err.span_label( + *sp, + format!("this and all prior arms are found to be of type `{}`", t), + ); + } + let outer_error_span = if any_multiline_arm { + // Cover just `match` and the scrutinee expression, not + // the entire match body, to reduce diagram noise. + cause.span.shrink_to_lo().to(scrut_span) + } else { + cause.span + }; + let msg = "`match` arms have incompatible types"; + err.span_label(outer_error_span, msg); + self.suggest_remove_semi_or_return_binding( + err, + prior_arm_block_id, + prior_arm_ty, + prior_arm_span, + arm_block_id, + arm_ty, + arm_span, + ); + if let Some(ret_sp) = opt_suggest_box_span { + // Get return type span and point to it. + self.suggest_boxing_for_return_impl_trait( + err, + ret_sp, + prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s), + ); + } + } + }, + ObligationCauseCode::IfExpression(box IfExpressionCause { + then_id, + else_id, + then_ty, + else_ty, + outer_span, + opt_suggest_box_span, + }) => { + let then_span = self.find_block_span_from_hir_id(then_id); + let else_span = self.find_block_span_from_hir_id(else_id); + err.span_label(then_span, "expected because of this"); + if let Some(sp) = outer_span { + err.span_label(sp, "`if` and `else` have incompatible types"); + } + self.suggest_remove_semi_or_return_binding( + err, + Some(then_id), + then_ty, + then_span, + Some(else_id), + else_ty, + else_span, + ); + if let Some(ret_sp) = opt_suggest_box_span { + self.suggest_boxing_for_return_impl_trait( + err, + ret_sp, + [then_span, else_span].into_iter(), + ); + } + } + ObligationCauseCode::LetElse => { + err.help("try adding a diverging expression, such as `return` or `panic!(..)`"); + err.help("...or use `match` instead of `let...else`"); + } + _ => { + if let ObligationCauseCode::BindingObligation(_, binding_span) = + cause.code().peel_derives() + { + if matches!(terr, TypeError::RegionsPlaceholderMismatch) { + err.span_note(*binding_span, "the lifetime requirement is introduced here"); + } + } + } + } + } + + fn suggest_remove_semi_or_return_binding( + &self, + err: &mut Diagnostic, + first_id: Option<hir::HirId>, + first_ty: Ty<'tcx>, + first_span: Span, + second_id: Option<hir::HirId>, + second_ty: Ty<'tcx>, + second_span: Span, + ) { + let remove_semicolon = [ + (first_id, self.resolve_vars_if_possible(second_ty)), + (second_id, self.resolve_vars_if_possible(first_ty)), + ] + .into_iter() + .find_map(|(id, ty)| { + let hir::Node::Block(blk) = self.tcx.hir().get(id?) else { return None }; + self.could_remove_semicolon(blk, ty) + }); + match remove_semicolon { + Some((sp, StatementAsExpression::NeedsBoxing)) => { + err.multipart_suggestion( + "consider removing this semicolon and boxing the expressions", + vec![ + (first_span.shrink_to_lo(), "Box::new(".to_string()), + (first_span.shrink_to_hi(), ")".to_string()), + (second_span.shrink_to_lo(), "Box::new(".to_string()), + (second_span.shrink_to_hi(), ")".to_string()), + (sp, String::new()), + ], + Applicability::MachineApplicable, + ); + } + Some((sp, StatementAsExpression::CorrectType)) => { + err.span_suggestion_short( + sp, + "consider removing this semicolon", + "", + Applicability::MachineApplicable, + ); + } + None => { + for (id, ty) in [(first_id, second_ty), (second_id, first_ty)] { + if let Some(id) = id + && let hir::Node::Block(blk) = self.tcx.hir().get(id) + && self.consider_returning_binding(blk, ty, err) + { + break; + } + } + } + } + } + + fn suggest_boxing_for_return_impl_trait( + &self, + err: &mut Diagnostic, + return_sp: Span, + arm_spans: impl Iterator<Item = Span>, + ) { + err.multipart_suggestion( + "you could change the return type to be a boxed trait object", + vec![ + (return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()), + (return_sp.shrink_to_hi(), ">".to_string()), + ], + Applicability::MaybeIncorrect, + ); + let sugg = arm_spans + .flat_map(|sp| { + [(sp.shrink_to_lo(), "Box::new(".to_string()), (sp.shrink_to_hi(), ")".to_string())] + .into_iter() + }) + .collect::<Vec<_>>(); + err.multipart_suggestion( + "if you change the return type to expect trait objects, box the returned expressions", + sugg, + Applicability::MaybeIncorrect, + ); + } + + /// Given that `other_ty` is the same as a type argument for `name` in `sub`, populate `value` + /// highlighting `name` and every type argument that isn't at `pos` (which is `other_ty`), and + /// populate `other_value` with `other_ty`. + /// + /// ```text + /// Foo<Bar<Qux>> + /// ^^^^--------^ this is highlighted + /// | | + /// | this type argument is exactly the same as the other type, not highlighted + /// this is highlighted + /// Bar<Qux> + /// -------- this type is the same as a type argument in the other type, not highlighted + /// ``` + fn highlight_outer( + &self, + value: &mut DiagnosticStyledString, + other_value: &mut DiagnosticStyledString, + name: String, + sub: ty::subst::SubstsRef<'tcx>, + pos: usize, + other_ty: Ty<'tcx>, + ) { + // `value` and `other_value` hold two incomplete type representation for display. + // `name` is the path of both types being compared. `sub` + value.push_highlighted(name); + let len = sub.len(); + if len > 0 { + value.push_highlighted("<"); + } + + // Output the lifetimes for the first type + let lifetimes = sub + .regions() + .map(|lifetime| { + let s = lifetime.to_string(); + if s.is_empty() { "'_".to_string() } else { s } + }) + .collect::<Vec<_>>() + .join(", "); + if !lifetimes.is_empty() { + if sub.regions().count() < len { + value.push_normal(lifetimes + ", "); + } else { + value.push_normal(lifetimes); + } + } + + // Highlight all the type arguments that aren't at `pos` and compare the type argument at + // `pos` and `other_ty`. + for (i, type_arg) in sub.types().enumerate() { + if i == pos { + let values = self.cmp(type_arg, other_ty); + value.0.extend((values.0).0); + other_value.0.extend((values.1).0); + } else { + value.push_highlighted(type_arg.to_string()); + } + + if len > 0 && i != len - 1 { + value.push_normal(", "); + } + } + if len > 0 { + value.push_highlighted(">"); + } + } + + /// If `other_ty` is the same as a type argument present in `sub`, highlight `path` in `t1_out`, + /// as that is the difference to the other type. + /// + /// For the following code: + /// + /// ```ignore (illustrative) + /// let x: Foo<Bar<Qux>> = foo::<Bar<Qux>>(); + /// ``` + /// + /// The type error output will behave in the following way: + /// + /// ```text + /// Foo<Bar<Qux>> + /// ^^^^--------^ this is highlighted + /// | | + /// | this type argument is exactly the same as the other type, not highlighted + /// this is highlighted + /// Bar<Qux> + /// -------- this type is the same as a type argument in the other type, not highlighted + /// ``` + fn cmp_type_arg( + &self, + mut t1_out: &mut DiagnosticStyledString, + mut t2_out: &mut DiagnosticStyledString, + path: String, + sub: &'tcx [ty::GenericArg<'tcx>], + other_path: String, + other_ty: Ty<'tcx>, + ) -> Option<()> { + // FIXME/HACK: Go back to `SubstsRef` to use its inherent methods, + // ideally that shouldn't be necessary. + let sub = self.tcx.intern_substs(sub); + for (i, ta) in sub.types().enumerate() { + if ta == other_ty { + self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, other_ty); + return Some(()); + } + if let ty::Adt(def, _) = ta.kind() { + let path_ = self.tcx.def_path_str(def.did()); + if path_ == other_path { + self.highlight_outer(&mut t1_out, &mut t2_out, path, sub, i, other_ty); + return Some(()); + } + } + } + None + } + + /// Adds a `,` to the type representation only if it is appropriate. + fn push_comma( + &self, + value: &mut DiagnosticStyledString, + other_value: &mut DiagnosticStyledString, + len: usize, + pos: usize, + ) { + if len > 0 && pos != len - 1 { + value.push_normal(", "); + other_value.push_normal(", "); + } + } + + /// Given two `fn` signatures highlight only sub-parts that are different. + fn cmp_fn_sig( + &self, + sig1: &ty::PolyFnSig<'tcx>, + sig2: &ty::PolyFnSig<'tcx>, + ) -> (DiagnosticStyledString, DiagnosticStyledString) { + let get_lifetimes = |sig| { + use rustc_hir::def::Namespace; + let (_, sig, reg) = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS) + .name_all_regions(sig) + .unwrap(); + let lts: Vec<String> = reg.into_iter().map(|(_, kind)| kind.to_string()).collect(); + (if lts.is_empty() { String::new() } else { format!("for<{}> ", lts.join(", ")) }, sig) + }; + + let (lt1, sig1) = get_lifetimes(sig1); + let (lt2, sig2) = get_lifetimes(sig2); + + // unsafe extern "C" for<'a> fn(&'a T) -> &'a T + let mut values = ( + DiagnosticStyledString::normal("".to_string()), + DiagnosticStyledString::normal("".to_string()), + ); + + // unsafe extern "C" for<'a> fn(&'a T) -> &'a T + // ^^^^^^ + values.0.push(sig1.unsafety.prefix_str(), sig1.unsafety != sig2.unsafety); + values.1.push(sig2.unsafety.prefix_str(), sig1.unsafety != sig2.unsafety); + + // unsafe extern "C" for<'a> fn(&'a T) -> &'a T + // ^^^^^^^^^^ + if sig1.abi != abi::Abi::Rust { + values.0.push(format!("extern {} ", sig1.abi), sig1.abi != sig2.abi); + } + if sig2.abi != abi::Abi::Rust { + values.1.push(format!("extern {} ", sig2.abi), sig1.abi != sig2.abi); + } + + // unsafe extern "C" for<'a> fn(&'a T) -> &'a T + // ^^^^^^^^ + let lifetime_diff = lt1 != lt2; + values.0.push(lt1, lifetime_diff); + values.1.push(lt2, lifetime_diff); + + // unsafe extern "C" for<'a> fn(&'a T) -> &'a T + // ^^^ + values.0.push_normal("fn("); + values.1.push_normal("fn("); + + // unsafe extern "C" for<'a> fn(&'a T) -> &'a T + // ^^^^^ + let len1 = sig1.inputs().len(); + let len2 = sig2.inputs().len(); + if len1 == len2 { + for (i, (l, r)) in iter::zip(sig1.inputs(), sig2.inputs()).enumerate() { + let (x1, x2) = self.cmp(*l, *r); + (values.0).0.extend(x1.0); + (values.1).0.extend(x2.0); + self.push_comma(&mut values.0, &mut values.1, len1, i); + } + } else { + for (i, l) in sig1.inputs().iter().enumerate() { + values.0.push_highlighted(l.to_string()); + if i != len1 - 1 { + values.0.push_highlighted(", "); + } + } + for (i, r) in sig2.inputs().iter().enumerate() { + values.1.push_highlighted(r.to_string()); + if i != len2 - 1 { + values.1.push_highlighted(", "); + } + } + } + + if sig1.c_variadic { + if len1 > 0 { + values.0.push_normal(", "); + } + values.0.push("...", !sig2.c_variadic); + } + if sig2.c_variadic { + if len2 > 0 { + values.1.push_normal(", "); + } + values.1.push("...", !sig1.c_variadic); + } + + // unsafe extern "C" for<'a> fn(&'a T) -> &'a T + // ^ + values.0.push_normal(")"); + values.1.push_normal(")"); + + // unsafe extern "C" for<'a> fn(&'a T) -> &'a T + // ^^^^^^^^ + let output1 = sig1.output(); + let output2 = sig2.output(); + let (x1, x2) = self.cmp(output1, output2); + if !output1.is_unit() { + values.0.push_normal(" -> "); + (values.0).0.extend(x1.0); + } + if !output2.is_unit() { + values.1.push_normal(" -> "); + (values.1).0.extend(x2.0); + } + values + } + + /// Compares two given types, eliding parts that are the same between them and highlighting + /// relevant differences, and return two representation of those types for highlighted printing. + pub fn cmp( + &self, + t1: Ty<'tcx>, + t2: Ty<'tcx>, + ) -> (DiagnosticStyledString, DiagnosticStyledString) { + debug!("cmp(t1={}, t1.kind={:?}, t2={}, t2.kind={:?})", t1, t1.kind(), t2, t2.kind()); + + // helper functions + fn equals<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool { + match (a.kind(), b.kind()) { + (a, b) if *a == *b => true, + (&ty::Int(_), &ty::Infer(ty::InferTy::IntVar(_))) + | ( + &ty::Infer(ty::InferTy::IntVar(_)), + &ty::Int(_) | &ty::Infer(ty::InferTy::IntVar(_)), + ) + | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_))) + | ( + &ty::Infer(ty::InferTy::FloatVar(_)), + &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)), + ) => true, + _ => false, + } + } + + fn push_ty_ref<'tcx>( + region: ty::Region<'tcx>, + ty: Ty<'tcx>, + mutbl: hir::Mutability, + s: &mut DiagnosticStyledString, + ) { + let mut r = region.to_string(); + if r == "'_" { + r.clear(); + } else { + r.push(' '); + } + s.push_highlighted(format!("&{}{}", r, mutbl.prefix_str())); + s.push_normal(ty.to_string()); + } + + // process starts here + match (t1.kind(), t2.kind()) { + (&ty::Adt(def1, sub1), &ty::Adt(def2, sub2)) => { + let did1 = def1.did(); + let did2 = def2.did(); + let sub_no_defaults_1 = + self.tcx.generics_of(did1).own_substs_no_defaults(self.tcx, sub1); + let sub_no_defaults_2 = + self.tcx.generics_of(did2).own_substs_no_defaults(self.tcx, sub2); + let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); + let path1 = self.tcx.def_path_str(did1); + let path2 = self.tcx.def_path_str(did2); + if did1 == did2 { + // Easy case. Replace same types with `_` to shorten the output and highlight + // the differing ones. + // let x: Foo<Bar, Qux> = y::<Foo<Quz, Qux>>(); + // Foo<Bar, _> + // Foo<Quz, _> + // --- ^ type argument elided + // | + // highlighted in output + values.0.push_normal(path1); + values.1.push_normal(path2); + + // Avoid printing out default generic parameters that are common to both + // types. + let len1 = sub_no_defaults_1.len(); + let len2 = sub_no_defaults_2.len(); + let common_len = cmp::min(len1, len2); + let remainder1: Vec<_> = sub1.types().skip(common_len).collect(); + let remainder2: Vec<_> = sub2.types().skip(common_len).collect(); + let common_default_params = + iter::zip(remainder1.iter().rev(), remainder2.iter().rev()) + .filter(|(a, b)| a == b) + .count(); + let len = sub1.len() - common_default_params; + let consts_offset = len - sub1.consts().count(); + + // Only draw `<...>` if there are lifetime/type arguments. + if len > 0 { + values.0.push_normal("<"); + values.1.push_normal("<"); + } + + fn lifetime_display(lifetime: Region<'_>) -> String { + let s = lifetime.to_string(); + if s.is_empty() { "'_".to_string() } else { s } + } + // At one point we'd like to elide all lifetimes here, they are irrelevant for + // all diagnostics that use this output + // + // Foo<'x, '_, Bar> + // Foo<'y, '_, Qux> + // ^^ ^^ --- type arguments are not elided + // | | + // | elided as they were the same + // not elided, they were different, but irrelevant + // + // For bound lifetimes, keep the names of the lifetimes, + // even if they are the same so that it's clear what's happening + // if we have something like + // + // for<'r, 's> fn(Inv<'r>, Inv<'s>) + // for<'r> fn(Inv<'r>, Inv<'r>) + let lifetimes = sub1.regions().zip(sub2.regions()); + for (i, lifetimes) in lifetimes.enumerate() { + let l1 = lifetime_display(lifetimes.0); + let l2 = lifetime_display(lifetimes.1); + if lifetimes.0 != lifetimes.1 { + values.0.push_highlighted(l1); + values.1.push_highlighted(l2); + } else if lifetimes.0.is_late_bound() { + values.0.push_normal(l1); + values.1.push_normal(l2); + } else { + values.0.push_normal("'_"); + values.1.push_normal("'_"); + } + self.push_comma(&mut values.0, &mut values.1, len, i); + } + + // We're comparing two types with the same path, so we compare the type + // arguments for both. If they are the same, do not highlight and elide from the + // output. + // Foo<_, Bar> + // Foo<_, Qux> + // ^ elided type as this type argument was the same in both sides + let type_arguments = sub1.types().zip(sub2.types()); + let regions_len = sub1.regions().count(); + let num_display_types = consts_offset - regions_len; + for (i, (ta1, ta2)) in type_arguments.take(num_display_types).enumerate() { + let i = i + regions_len; + if ta1 == ta2 { + values.0.push_normal("_"); + values.1.push_normal("_"); + } else { + let (x1, x2) = self.cmp(ta1, ta2); + (values.0).0.extend(x1.0); + (values.1).0.extend(x2.0); + } + self.push_comma(&mut values.0, &mut values.1, len, i); + } + + // Do the same for const arguments, if they are equal, do not highlight and + // elide them from the output. + let const_arguments = sub1.consts().zip(sub2.consts()); + for (i, (ca1, ca2)) in const_arguments.enumerate() { + let i = i + consts_offset; + if ca1 == ca2 { + values.0.push_normal("_"); + values.1.push_normal("_"); + } else { + values.0.push_highlighted(ca1.to_string()); + values.1.push_highlighted(ca2.to_string()); + } + self.push_comma(&mut values.0, &mut values.1, len, i); + } + + // Close the type argument bracket. + // Only draw `<...>` if there are lifetime/type arguments. + if len > 0 { + values.0.push_normal(">"); + values.1.push_normal(">"); + } + values + } else { + // Check for case: + // let x: Foo<Bar<Qux> = foo::<Bar<Qux>>(); + // Foo<Bar<Qux> + // ------- this type argument is exactly the same as the other type + // Bar<Qux> + if self + .cmp_type_arg( + &mut values.0, + &mut values.1, + path1.clone(), + sub_no_defaults_1, + path2.clone(), + t2, + ) + .is_some() + { + return values; + } + // Check for case: + // let x: Bar<Qux> = y:<Foo<Bar<Qux>>>(); + // Bar<Qux> + // Foo<Bar<Qux>> + // ------- this type argument is exactly the same as the other type + if self + .cmp_type_arg( + &mut values.1, + &mut values.0, + path2, + sub_no_defaults_2, + path1, + t1, + ) + .is_some() + { + return values; + } + + // We can't find anything in common, highlight relevant part of type path. + // let x: foo::bar::Baz<Qux> = y:<foo::bar::Bar<Zar>>(); + // foo::bar::Baz<Qux> + // foo::bar::Bar<Zar> + // -------- this part of the path is different + + let t1_str = t1.to_string(); + let t2_str = t2.to_string(); + let min_len = t1_str.len().min(t2_str.len()); + + const SEPARATOR: &str = "::"; + let separator_len = SEPARATOR.len(); + let split_idx: usize = + iter::zip(t1_str.split(SEPARATOR), t2_str.split(SEPARATOR)) + .take_while(|(mod1_str, mod2_str)| mod1_str == mod2_str) + .map(|(mod_str, _)| mod_str.len() + separator_len) + .sum(); + + debug!( + "cmp: separator_len={}, split_idx={}, min_len={}", + separator_len, split_idx, min_len + ); + + if split_idx >= min_len { + // paths are identical, highlight everything + ( + DiagnosticStyledString::highlighted(t1_str), + DiagnosticStyledString::highlighted(t2_str), + ) + } else { + let (common, uniq1) = t1_str.split_at(split_idx); + let (_, uniq2) = t2_str.split_at(split_idx); + debug!("cmp: common={}, uniq1={}, uniq2={}", common, uniq1, uniq2); + + values.0.push_normal(common); + values.0.push_highlighted(uniq1); + values.1.push_normal(common); + values.1.push_highlighted(uniq2); + + values + } + } + } + + // When finding T != &T, highlight only the borrow + (&ty::Ref(r1, ref_ty1, mutbl1), _) if equals(ref_ty1, t2) => { + let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); + push_ty_ref(r1, ref_ty1, mutbl1, &mut values.0); + values.1.push_normal(t2.to_string()); + values + } + (_, &ty::Ref(r2, ref_ty2, mutbl2)) if equals(t1, ref_ty2) => { + let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); + values.0.push_normal(t1.to_string()); + push_ty_ref(r2, ref_ty2, mutbl2, &mut values.1); + values + } + + // When encountering &T != &mut T, highlight only the borrow + (&ty::Ref(r1, ref_ty1, mutbl1), &ty::Ref(r2, ref_ty2, mutbl2)) + if equals(ref_ty1, ref_ty2) => + { + let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); + push_ty_ref(r1, ref_ty1, mutbl1, &mut values.0); + push_ty_ref(r2, ref_ty2, mutbl2, &mut values.1); + values + } + + // When encountering tuples of the same size, highlight only the differing types + (&ty::Tuple(substs1), &ty::Tuple(substs2)) if substs1.len() == substs2.len() => { + let mut values = + (DiagnosticStyledString::normal("("), DiagnosticStyledString::normal("(")); + let len = substs1.len(); + for (i, (left, right)) in substs1.iter().zip(substs2).enumerate() { + let (x1, x2) = self.cmp(left, right); + (values.0).0.extend(x1.0); + (values.1).0.extend(x2.0); + self.push_comma(&mut values.0, &mut values.1, len, i); + } + if len == 1 { + // Keep the output for single element tuples as `(ty,)`. + values.0.push_normal(","); + values.1.push_normal(","); + } + values.0.push_normal(")"); + values.1.push_normal(")"); + values + } + + (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { + let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1); + let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2); + let mut values = self.cmp_fn_sig(&sig1, &sig2); + let path1 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did1, substs1)); + let path2 = format!(" {{{}}}", self.tcx.def_path_str_with_substs(*did2, substs2)); + let same_path = path1 == path2; + values.0.push(path1, !same_path); + values.1.push(path2, !same_path); + values + } + + (ty::FnDef(did1, substs1), ty::FnPtr(sig2)) => { + let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1); + let mut values = self.cmp_fn_sig(&sig1, sig2); + values.0.push_highlighted(format!( + " {{{}}}", + self.tcx.def_path_str_with_substs(*did1, substs1) + )); + values + } + + (ty::FnPtr(sig1), ty::FnDef(did2, substs2)) => { + let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2); + let mut values = self.cmp_fn_sig(sig1, &sig2); + values.1.push_normal(format!( + " {{{}}}", + self.tcx.def_path_str_with_substs(*did2, substs2) + )); + values + } + + (ty::FnPtr(sig1), ty::FnPtr(sig2)) => self.cmp_fn_sig(sig1, sig2), + + _ => { + if t1 == t2 { + // The two types are the same, elide and don't highlight. + (DiagnosticStyledString::normal("_"), DiagnosticStyledString::normal("_")) + } else { + // We couldn't find anything in common, highlight everything. + ( + DiagnosticStyledString::highlighted(t1.to_string()), + DiagnosticStyledString::highlighted(t2.to_string()), + ) + } + } + } + } + + /// Extend a type error with extra labels pointing at "non-trivial" types, like closures and + /// the return type of `async fn`s. + /// + /// `secondary_span` gives the caller the opportunity to expand `diag` with a `span_label`. + /// + /// `swap_secondary_and_primary` is used to make projection errors in particular nicer by using + /// the message in `secondary_span` as the primary label, and apply the message that would + /// otherwise be used for the primary label on the `secondary_span` `Span`. This applies on + /// E0271, like `src/test/ui/issues/issue-39970.stderr`. + #[tracing::instrument( + level = "debug", + skip(self, diag, secondary_span, swap_secondary_and_primary, force_label) + )] + pub fn note_type_err( + &self, + diag: &mut Diagnostic, + cause: &ObligationCause<'tcx>, + secondary_span: Option<(Span, String)>, + mut values: Option<ValuePairs<'tcx>>, + terr: &TypeError<'tcx>, + swap_secondary_and_primary: bool, + force_label: bool, + ) { + let span = cause.span(); + + // For some types of errors, expected-found does not make + // sense, so just ignore the values we were given. + if let TypeError::CyclicTy(_) = terr { + values = None; + } + struct OpaqueTypesVisitor<'tcx> { + types: FxHashMap<TyCategory, FxHashSet<Span>>, + expected: FxHashMap<TyCategory, FxHashSet<Span>>, + found: FxHashMap<TyCategory, FxHashSet<Span>>, + ignore_span: Span, + tcx: TyCtxt<'tcx>, + } + + impl<'tcx> OpaqueTypesVisitor<'tcx> { + fn visit_expected_found( + tcx: TyCtxt<'tcx>, + expected: Ty<'tcx>, + found: Ty<'tcx>, + ignore_span: Span, + ) -> Self { + let mut types_visitor = OpaqueTypesVisitor { + types: Default::default(), + expected: Default::default(), + found: Default::default(), + ignore_span, + tcx, + }; + // The visitor puts all the relevant encountered types in `self.types`, but in + // here we want to visit two separate types with no relation to each other, so we + // move the results from `types` to `expected` or `found` as appropriate. + expected.visit_with(&mut types_visitor); + std::mem::swap(&mut types_visitor.expected, &mut types_visitor.types); + found.visit_with(&mut types_visitor); + std::mem::swap(&mut types_visitor.found, &mut types_visitor.types); + types_visitor + } + + fn report(&self, err: &mut Diagnostic) { + self.add_labels_for_types(err, "expected", &self.expected); + self.add_labels_for_types(err, "found", &self.found); + } + + fn add_labels_for_types( + &self, + err: &mut Diagnostic, + target: &str, + types: &FxHashMap<TyCategory, FxHashSet<Span>>, + ) { + for (key, values) in types.iter() { + let count = values.len(); + let kind = key.descr(); + let mut returned_async_output_error = false; + for &sp in values { + if sp.is_desugaring(DesugaringKind::Async) && !returned_async_output_error { + if [sp] != err.span.primary_spans() { + let mut span: MultiSpan = sp.into(); + span.push_span_label( + sp, + format!( + "checked the `Output` of this `async fn`, {}{} {}{}", + if count > 1 { "one of the " } else { "" }, + target, + kind, + pluralize!(count), + ), + ); + err.span_note( + span, + "while checking the return type of the `async fn`", + ); + } else { + err.span_label( + sp, + format!( + "checked the `Output` of this `async fn`, {}{} {}{}", + if count > 1 { "one of the " } else { "" }, + target, + kind, + pluralize!(count), + ), + ); + err.note("while checking the return type of the `async fn`"); + } + returned_async_output_error = true; + } else { + err.span_label( + sp, + format!( + "{}{} {}{}", + if count == 1 { "the " } else { "one of the " }, + target, + kind, + pluralize!(count), + ), + ); + } + } + } + } + } + + impl<'tcx> ty::visit::TypeVisitor<'tcx> for OpaqueTypesVisitor<'tcx> { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + if let Some((kind, def_id)) = TyCategory::from_ty(self.tcx, t) { + let span = self.tcx.def_span(def_id); + // Avoid cluttering the output when the "found" and error span overlap: + // + // error[E0308]: mismatched types + // --> $DIR/issue-20862.rs:2:5 + // | + // LL | |y| x + y + // | ^^^^^^^^^ + // | | + // | the found closure + // | expected `()`, found closure + // | + // = note: expected unit type `()` + // found closure `[closure@$DIR/issue-20862.rs:2:5: 2:14 x:_]` + if !self.ignore_span.overlaps(span) { + self.types.entry(kind).or_default().insert(span); + } + } + t.super_visit_with(self) + } + } + + debug!("note_type_err(diag={:?})", diag); + enum Mismatch<'a> { + Variable(ty::error::ExpectedFound<Ty<'a>>), + Fixed(&'static str), + } + let (expected_found, exp_found, is_simple_error, values) = match values { + None => (None, Mismatch::Fixed("type"), false, None), + Some(values) => { + let values = self.resolve_vars_if_possible(values); + let (is_simple_error, exp_found) = match values { + ValuePairs::Terms(infer::ExpectedFound { + expected: ty::Term::Ty(expected), + found: ty::Term::Ty(found), + }) => { + let is_simple_err = expected.is_simple_text() && found.is_simple_text(); + OpaqueTypesVisitor::visit_expected_found(self.tcx, expected, found, span) + .report(diag); + + ( + is_simple_err, + Mismatch::Variable(infer::ExpectedFound { expected, found }), + ) + } + ValuePairs::TraitRefs(_) | ValuePairs::PolyTraitRefs(_) => { + (false, Mismatch::Fixed("trait")) + } + _ => (false, Mismatch::Fixed("type")), + }; + let vals = match self.values_str(values) { + Some((expected, found)) => Some((expected, found)), + None => { + // Derived error. Cancel the emitter. + // NOTE(eddyb) this was `.cancel()`, but `diag` + // is borrowed, so we can't fully defuse it. + diag.downgrade_to_delayed_bug(); + return; + } + }; + (vals, exp_found, is_simple_error, Some(values)) + } + }; + + match terr { + // Ignore msg for object safe coercion + // since E0038 message will be printed + TypeError::ObjectUnsafeCoercion(_) => {} + _ => { + let mut label_or_note = |span: Span, msg: &str| { + if force_label || &[span] == diag.span.primary_spans() { + diag.span_label(span, msg); + } else { + diag.span_note(span, msg); + } + }; + if let Some((sp, msg)) = secondary_span { + if swap_secondary_and_primary { + let terr = if let Some(infer::ValuePairs::Terms(infer::ExpectedFound { + expected, + .. + })) = values + { + format!("expected this to be `{}`", expected) + } else { + terr.to_string() + }; + label_or_note(sp, &terr); + label_or_note(span, &msg); + } else { + label_or_note(span, &terr.to_string()); + label_or_note(sp, &msg); + } + } else { + label_or_note(span, &terr.to_string()); + } + } + }; + if let Some((expected, found)) = expected_found { + let (expected_label, found_label, exp_found) = match exp_found { + Mismatch::Variable(ef) => ( + ef.expected.prefix_string(self.tcx), + ef.found.prefix_string(self.tcx), + Some(ef), + ), + Mismatch::Fixed(s) => (s.into(), s.into(), None), + }; + match (&terr, expected == found) { + (TypeError::Sorts(values), extra) => { + let sort_string = |ty: Ty<'tcx>| match (extra, ty.kind()) { + (true, ty::Opaque(def_id, _)) => { + let sm = self.tcx.sess.source_map(); + let pos = sm.lookup_char_pos(self.tcx.def_span(*def_id).lo()); + format!( + " (opaque type at <{}:{}:{}>)", + sm.filename_for_diagnostics(&pos.file.name), + pos.line, + pos.col.to_usize() + 1, + ) + } + (true, _) => format!(" ({})", ty.sort_string(self.tcx)), + (false, _) => "".to_string(), + }; + if !(values.expected.is_simple_text() && values.found.is_simple_text()) + || (exp_found.map_or(false, |ef| { + // This happens when the type error is a subset of the expectation, + // like when you have two references but one is `usize` and the other + // is `f32`. In those cases we still want to show the `note`. If the + // value from `ef` is `Infer(_)`, then we ignore it. + if !ef.expected.is_ty_infer() { + ef.expected != values.expected + } else if !ef.found.is_ty_infer() { + ef.found != values.found + } else { + false + } + })) + { + diag.note_expected_found_extra( + &expected_label, + expected, + &found_label, + found, + &sort_string(values.expected), + &sort_string(values.found), + ); + } + } + (TypeError::ObjectUnsafeCoercion(_), _) => { + diag.note_unsuccessful_coercion(found, expected); + } + (_, _) => { + debug!( + "note_type_err: exp_found={:?}, expected={:?} found={:?}", + exp_found, expected, found + ); + if !is_simple_error || terr.must_include_note() { + diag.note_expected_found(&expected_label, expected, &found_label, found); + } + } + } + } + let exp_found = match exp_found { + Mismatch::Variable(exp_found) => Some(exp_found), + Mismatch::Fixed(_) => None, + }; + let exp_found = match terr { + // `terr` has more accurate type information than `exp_found` in match expressions. + ty::error::TypeError::Sorts(terr) + if exp_found.map_or(false, |ef| terr.found == ef.found) => + { + Some(*terr) + } + _ => exp_found, + }; + debug!("exp_found {:?} terr {:?} cause.code {:?}", exp_found, terr, cause.code()); + if let Some(exp_found) = exp_found { + let should_suggest_fixes = + if let ObligationCauseCode::Pattern { root_ty, .. } = cause.code() { + // Skip if the root_ty of the pattern is not the same as the expected_ty. + // If these types aren't equal then we've probably peeled off a layer of arrays. + self.same_type_modulo_infer(*root_ty, exp_found.expected) + } else { + true + }; + + if should_suggest_fixes { + self.suggest_tuple_pattern(cause, &exp_found, diag); + self.suggest_as_ref_where_appropriate(span, &exp_found, diag); + self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag); + self.suggest_await_on_expect_found(cause, span, &exp_found, diag); + } + } + + // In some (most?) cases cause.body_id points to actual body, but in some cases + // it's an actual definition. According to the comments (e.g. in + // librustc_typeck/check/compare_method.rs:compare_predicate_entailment) the latter + // is relied upon by some other code. This might (or might not) need cleanup. + let body_owner_def_id = + self.tcx.hir().opt_local_def_id(cause.body_id).unwrap_or_else(|| { + self.tcx.hir().body_owner_def_id(hir::BodyId { hir_id: cause.body_id }) + }); + self.check_and_note_conflicting_crates(diag, terr); + self.tcx.note_and_explain_type_err(diag, terr, cause, span, body_owner_def_id.to_def_id()); + + if let Some(ValuePairs::PolyTraitRefs(exp_found)) = values + && let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind() + && let Some(def_id) = def_id.as_local() + { + let span = self.tcx.def_span(def_id); + diag.span_note(span, "this closure does not fulfill the lifetime requirements"); + } + + // It reads better to have the error origin as the final + // thing. + self.note_error_origin(diag, cause, exp_found, terr); + + debug!(?diag); + } + + fn suggest_tuple_pattern( + &self, + cause: &ObligationCause<'tcx>, + exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, + diag: &mut Diagnostic, + ) { + // Heavily inspired by `FnCtxt::suggest_compatible_variants`, with + // some modifications due to that being in typeck and this being in infer. + if let ObligationCauseCode::Pattern { .. } = cause.code() { + if let ty::Adt(expected_adt, substs) = exp_found.expected.kind() { + let compatible_variants: Vec<_> = expected_adt + .variants() + .iter() + .filter(|variant| { + variant.fields.len() == 1 && variant.ctor_kind == hir::def::CtorKind::Fn + }) + .filter_map(|variant| { + let sole_field = &variant.fields[0]; + let sole_field_ty = sole_field.ty(self.tcx, substs); + if self.same_type_modulo_infer(sole_field_ty, exp_found.found) { + let variant_path = + with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id)); + // FIXME #56861: DRYer prelude filtering + if let Some(path) = variant_path.strip_prefix("std::prelude::") { + if let Some((_, path)) = path.split_once("::") { + return Some(path.to_string()); + } + } + Some(variant_path) + } else { + None + } + }) + .collect(); + match &compatible_variants[..] { + [] => {} + [variant] => { + diag.multipart_suggestion_verbose( + &format!("try wrapping the pattern in `{}`", variant), + vec![ + (cause.span.shrink_to_lo(), format!("{}(", variant)), + (cause.span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } + _ => { + // More than one matching variant. + diag.multipart_suggestions( + &format!( + "try wrapping the pattern in a variant of `{}`", + self.tcx.def_path_str(expected_adt.did()) + ), + compatible_variants.into_iter().map(|variant| { + vec![ + (cause.span.shrink_to_lo(), format!("{}(", variant)), + (cause.span.shrink_to_hi(), ")".to_string()), + ] + }), + Applicability::MaybeIncorrect, + ); + } + } + } + } + } + + pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<Binder<'tcx, Ty<'tcx>>> { + if let ty::Opaque(def_id, substs) = ty.kind() { + let future_trait = self.tcx.require_lang_item(LangItem::Future, None); + // Future::Output + let item_def_id = self.tcx.associated_item_def_ids(future_trait)[0]; + + let bounds = self.tcx.bound_explicit_item_bounds(*def_id); + + for predicate in bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) { + let predicate = predicate.subst(self.tcx, substs); + let output = predicate + .kind() + .map_bound(|kind| match kind { + ty::PredicateKind::Projection(projection_predicate) + if projection_predicate.projection_ty.item_def_id == item_def_id => + { + projection_predicate.term.ty() + } + _ => None, + }) + .transpose(); + if output.is_some() { + // We don't account for multiple `Future::Output = Ty` constraints. + return output; + } + } + } + None + } + + /// A possible error is to forget to add `.await` when using futures: + /// + /// ```compile_fail,E0308 + /// async fn make_u32() -> u32 { + /// 22 + /// } + /// + /// fn take_u32(x: u32) {} + /// + /// async fn foo() { + /// let x = make_u32(); + /// take_u32(x); + /// } + /// ``` + /// + /// This routine checks if the found type `T` implements `Future<Output=U>` where `U` is the + /// expected type. If this is the case, and we are inside of an async body, it suggests adding + /// `.await` to the tail of the expression. + fn suggest_await_on_expect_found( + &self, + cause: &ObligationCause<'tcx>, + exp_span: Span, + exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, + diag: &mut Diagnostic, + ) { + debug!( + "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}", + exp_span, exp_found.expected, exp_found.found, + ); + + if let ObligationCauseCode::CompareImplItemObligation { .. } = cause.code() { + return; + } + + match ( + self.get_impl_future_output_ty(exp_found.expected).map(Binder::skip_binder), + self.get_impl_future_output_ty(exp_found.found).map(Binder::skip_binder), + ) { + (Some(exp), Some(found)) if self.same_type_modulo_infer(exp, found) => match cause + .code() + { + ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => { + let then_span = self.find_block_span_from_hir_id(*then_id); + diag.multipart_suggestion( + "consider `await`ing on both `Future`s", + vec![ + (then_span.shrink_to_hi(), ".await".to_string()), + (exp_span.shrink_to_hi(), ".await".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } + ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { + prior_arms, + .. + }) => { + if let [.., arm_span] = &prior_arms[..] { + diag.multipart_suggestion( + "consider `await`ing on both `Future`s", + vec![ + (arm_span.shrink_to_hi(), ".await".to_string()), + (exp_span.shrink_to_hi(), ".await".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } else { + diag.help("consider `await`ing on both `Future`s"); + } + } + _ => { + diag.help("consider `await`ing on both `Future`s"); + } + }, + (_, Some(ty)) if self.same_type_modulo_infer(exp_found.expected, ty) => { + diag.span_suggestion_verbose( + exp_span.shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await", + Applicability::MaybeIncorrect, + ); + } + (Some(ty), _) if self.same_type_modulo_infer(ty, exp_found.found) => match cause.code() + { + ObligationCauseCode::Pattern { span: Some(then_span), .. } => { + diag.span_suggestion_verbose( + then_span.shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await", + Applicability::MaybeIncorrect, + ); + } + ObligationCauseCode::IfExpression(box IfExpressionCause { then_id, .. }) => { + let then_span = self.find_block_span_from_hir_id(*then_id); + diag.span_suggestion_verbose( + then_span.shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await", + Applicability::MaybeIncorrect, + ); + } + ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause { + ref prior_arms, + .. + }) => { + diag.multipart_suggestion_verbose( + "consider `await`ing on the `Future`", + prior_arms + .iter() + .map(|arm| (arm.shrink_to_hi(), ".await".to_string())) + .collect(), + Applicability::MaybeIncorrect, + ); + } + _ => {} + }, + _ => {} + } + } + + fn suggest_accessing_field_where_appropriate( + &self, + cause: &ObligationCause<'tcx>, + exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, + diag: &mut Diagnostic, + ) { + debug!( + "suggest_accessing_field_where_appropriate(cause={:?}, exp_found={:?})", + cause, exp_found + ); + if let ty::Adt(expected_def, expected_substs) = exp_found.expected.kind() { + if expected_def.is_enum() { + return; + } + + if let Some((name, ty)) = expected_def + .non_enum_variant() + .fields + .iter() + .filter(|field| field.vis.is_accessible_from(field.did, self.tcx)) + .map(|field| (field.name, field.ty(self.tcx, expected_substs))) + .find(|(_, ty)| self.same_type_modulo_infer(*ty, exp_found.found)) + { + if let ObligationCauseCode::Pattern { span: Some(span), .. } = *cause.code() { + if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { + let suggestion = if expected_def.is_struct() { + format!("{}.{}", snippet, name) + } else if expected_def.is_union() { + format!("unsafe {{ {}.{} }}", snippet, name) + } else { + return; + }; + diag.span_suggestion( + span, + &format!( + "you might have meant to use field `{}` whose type is `{}`", + name, ty + ), + suggestion, + Applicability::MaybeIncorrect, + ); + } + } + } + } + } + + /// When encountering a case where `.as_ref()` on a `Result` or `Option` would be appropriate, + /// suggests it. + fn suggest_as_ref_where_appropriate( + &self, + span: Span, + exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, + diag: &mut Diagnostic, + ) { + if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) = + (exp_found.expected.kind(), exp_found.found.kind()) + { + if let ty::Adt(found_def, found_substs) = *found_ty.kind() { + let path_str = format!("{:?}", exp_def); + if exp_def == &found_def { + let opt_msg = "you can convert from `&Option<T>` to `Option<&T>` using \ + `.as_ref()`"; + let result_msg = "you can convert from `&Result<T, E>` to \ + `Result<&T, &E>` using `.as_ref()`"; + let have_as_ref = &[ + ("std::option::Option", opt_msg), + ("core::option::Option", opt_msg), + ("std::result::Result", result_msg), + ("core::result::Result", result_msg), + ]; + if let Some(msg) = have_as_ref + .iter() + .find_map(|(path, msg)| (&path_str == path).then_some(msg)) + { + let mut show_suggestion = true; + for (exp_ty, found_ty) in + iter::zip(exp_substs.types(), found_substs.types()) + { + match *exp_ty.kind() { + ty::Ref(_, exp_ty, _) => { + match (exp_ty.kind(), found_ty.kind()) { + (_, ty::Param(_)) + | (_, ty::Infer(_)) + | (ty::Param(_), _) + | (ty::Infer(_), _) => {} + _ if self.same_type_modulo_infer(exp_ty, found_ty) => {} + _ => show_suggestion = false, + }; + } + ty::Param(_) | ty::Infer(_) => {} + _ => show_suggestion = false, + } + } + if let (Ok(snippet), true) = + (self.tcx.sess.source_map().span_to_snippet(span), show_suggestion) + { + diag.span_suggestion( + span, + *msg, + format!("{}.as_ref()", snippet), + Applicability::MachineApplicable, + ); + } + } + } + } + } + } + + pub fn report_and_explain_type_error( + &self, + trace: TypeTrace<'tcx>, + terr: &TypeError<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + use crate::traits::ObligationCauseCode::MatchExpressionArm; + + debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr); + + let span = trace.cause.span(); + let failure_code = trace.cause.as_failure_code(terr); + let mut diag = match failure_code { + FailureCode::Error0038(did) => { + let violations = self.tcx.object_safety_violations(did); + report_object_safety_error(self.tcx, span, did, violations) + } + FailureCode::Error0317(failure_str) => { + struct_span_err!(self.tcx.sess, span, E0317, "{}", failure_str) + } + FailureCode::Error0580(failure_str) => { + struct_span_err!(self.tcx.sess, span, E0580, "{}", failure_str) + } + FailureCode::Error0308(failure_str) => { + let mut err = struct_span_err!(self.tcx.sess, span, E0308, "{}", failure_str); + if let Some((expected, found)) = trace.values.ty() { + match (expected.kind(), found.kind()) { + (ty::Tuple(_), ty::Tuple(_)) => {} + // If a tuple of length one was expected and the found expression has + // parentheses around it, perhaps the user meant to write `(expr,)` to + // build a tuple (issue #86100) + (ty::Tuple(fields), _) => { + self.emit_tuple_wrap_err(&mut err, span, found, fields) + } + // If a character was expected and the found expression is a string literal + // containing a single character, perhaps the user meant to write `'c'` to + // specify a character literal (issue #92479) + (ty::Char, ty::Ref(_, r, _)) if r.is_str() => { + if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) + && let Some(code) = code.strip_prefix('"').and_then(|s| s.strip_suffix('"')) + && code.chars().count() == 1 + { + err.span_suggestion( + span, + "if you meant to write a `char` literal, use single quotes", + format!("'{}'", code), + Applicability::MachineApplicable, + ); + } + } + // If a string was expected and the found expression is a character literal, + // perhaps the user meant to write `"s"` to specify a string literal. + (ty::Ref(_, r, _), ty::Char) if r.is_str() => { + if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) { + if let Some(code) = + code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) + { + err.span_suggestion( + span, + "if you meant to write a `str` literal, use double quotes", + format!("\"{}\"", code), + Applicability::MachineApplicable, + ); + } + } + } + _ => {} + } + } + let code = trace.cause.code(); + if let &MatchExpressionArm(box MatchExpressionArmCause { source, .. }) = code + && let hir::MatchSource::TryDesugar = source + && let Some((expected_ty, found_ty)) = self.values_str(trace.values) + { + err.note(&format!( + "`?` operator cannot convert from `{}` to `{}`", + found_ty.content(), + expected_ty.content(), + )); + } + err + } + FailureCode::Error0644(failure_str) => { + struct_span_err!(self.tcx.sess, span, E0644, "{}", failure_str) + } + }; + self.note_type_err(&mut diag, &trace.cause, None, Some(trace.values), terr, false, false); + diag + } + + fn emit_tuple_wrap_err( + &self, + err: &mut Diagnostic, + span: Span, + found: Ty<'tcx>, + expected_fields: &List<Ty<'tcx>>, + ) { + let [expected_tup_elem] = expected_fields[..] else { return }; + + if !self.same_type_modulo_infer(expected_tup_elem, found) { + return; + } + + let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) + else { return }; + + let msg = "use a trailing comma to create a tuple with one element"; + if code.starts_with('(') && code.ends_with(')') { + let before_close = span.hi() - BytePos::from_u32(1); + err.span_suggestion( + span.with_hi(before_close).shrink_to_hi(), + msg, + ",", + Applicability::MachineApplicable, + ); + } else { + err.multipart_suggestion( + msg, + vec![(span.shrink_to_lo(), "(".into()), (span.shrink_to_hi(), ",)".into())], + Applicability::MachineApplicable, + ); + } + } + + fn values_str( + &self, + values: ValuePairs<'tcx>, + ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> { + match values { + infer::Regions(exp_found) => self.expected_found_str(exp_found), + infer::Terms(exp_found) => self.expected_found_str_term(exp_found), + infer::TraitRefs(exp_found) => { + let pretty_exp_found = ty::error::ExpectedFound { + expected: exp_found.expected.print_only_trait_path(), + found: exp_found.found.print_only_trait_path(), + }; + match self.expected_found_str(pretty_exp_found) { + Some((expected, found)) if expected == found => { + self.expected_found_str(exp_found) + } + ret => ret, + } + } + infer::PolyTraitRefs(exp_found) => { + let pretty_exp_found = ty::error::ExpectedFound { + expected: exp_found.expected.print_only_trait_path(), + found: exp_found.found.print_only_trait_path(), + }; + match self.expected_found_str(pretty_exp_found) { + Some((expected, found)) if expected == found => { + self.expected_found_str(exp_found) + } + ret => ret, + } + } + } + } + + fn expected_found_str_term( + &self, + exp_found: ty::error::ExpectedFound<ty::Term<'tcx>>, + ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> { + let exp_found = self.resolve_vars_if_possible(exp_found); + if exp_found.references_error() { + return None; + } + + Some(match (exp_found.expected, exp_found.found) { + (ty::Term::Ty(expected), ty::Term::Ty(found)) => self.cmp(expected, found), + (expected, found) => ( + DiagnosticStyledString::highlighted(expected.to_string()), + DiagnosticStyledString::highlighted(found.to_string()), + ), + }) + } + + /// Returns a string of the form "expected `{}`, found `{}`". + fn expected_found_str<T: fmt::Display + TypeFoldable<'tcx>>( + &self, + exp_found: ty::error::ExpectedFound<T>, + ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> { + let exp_found = self.resolve_vars_if_possible(exp_found); + if exp_found.references_error() { + return None; + } + + Some(( + DiagnosticStyledString::highlighted(exp_found.expected.to_string()), + DiagnosticStyledString::highlighted(exp_found.found.to_string()), + )) + } + + pub fn report_generic_bound_failure( + &self, + generic_param_scope: LocalDefId, + span: Span, + origin: Option<SubregionOrigin<'tcx>>, + bound_kind: GenericKind<'tcx>, + sub: Region<'tcx>, + ) { + self.construct_generic_bound_failure(generic_param_scope, span, origin, bound_kind, sub) + .emit(); + } + + pub fn construct_generic_bound_failure( + &self, + generic_param_scope: LocalDefId, + span: Span, + origin: Option<SubregionOrigin<'tcx>>, + bound_kind: GenericKind<'tcx>, + sub: Region<'tcx>, + ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { + // Attempt to obtain the span of the parameter so we can + // suggest adding an explicit lifetime bound to it. + let generics = self.tcx.generics_of(generic_param_scope); + // type_param_span is (span, has_bounds) + let type_param_span = match bound_kind { + GenericKind::Param(ref param) => { + // Account for the case where `param` corresponds to `Self`, + // which doesn't have the expected type argument. + if !(generics.has_self && param.index == 0) { + let type_param = generics.type_param(param, self.tcx); + type_param.def_id.as_local().map(|def_id| { + // Get the `hir::Param` to verify whether it already has any bounds. + // We do this to avoid suggesting code that ends up as `T: 'a'b`, + // instead we suggest `T: 'a + 'b` in that case. + let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); + let ast_generics = self.tcx.hir().get_generics(hir_id.owner); + let bounds = + ast_generics.and_then(|g| g.bounds_span_for_suggestions(def_id)); + // `sp` only covers `T`, change it so that it covers + // `T:` when appropriate + if let Some(span) = bounds { + (span, true) + } else { + let sp = self.tcx.def_span(def_id); + (sp.shrink_to_hi(), false) + } + }) + } else { + None + } + } + _ => None, + }; + + let new_lt = { + let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char)); + let lts_names = + iter::successors(Some(generics), |g| g.parent.map(|p| self.tcx.generics_of(p))) + .flat_map(|g| &g.params) + .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) + .map(|p| p.name.as_str()) + .collect::<Vec<_>>(); + possible + .find(|candidate| !lts_names.contains(&&candidate[..])) + .unwrap_or("'lt".to_string()) + }; + + let add_lt_sugg = generics + .params + .first() + .and_then(|param| param.def_id.as_local()) + .map(|def_id| (self.tcx.def_span(def_id).shrink_to_lo(), format!("{}, ", new_lt))); + + let labeled_user_string = match bound_kind { + GenericKind::Param(ref p) => format!("the parameter type `{}`", p), + GenericKind::Projection(ref p) => format!("the associated type `{}`", p), + }; + + if let Some(SubregionOrigin::CompareImplItemObligation { + span, + impl_item_def_id, + trait_item_def_id, + }) = origin + { + return self.report_extra_impl_obligation( + span, + impl_item_def_id, + trait_item_def_id, + &format!("`{}: {}`", bound_kind, sub), + ); + } + + fn binding_suggestion<'tcx, S: fmt::Display>( + err: &mut Diagnostic, + type_param_span: Option<(Span, bool)>, + bound_kind: GenericKind<'tcx>, + sub: S, + ) { + let msg = "consider adding an explicit lifetime bound"; + if let Some((sp, has_lifetimes)) = type_param_span { + let suggestion = + if has_lifetimes { format!(" + {}", sub) } else { format!(": {}", sub) }; + err.span_suggestion_verbose( + sp, + &format!("{}...", msg), + suggestion, + Applicability::MaybeIncorrect, // Issue #41966 + ); + } else { + let consider = format!("{} `{}: {}`...", msg, bound_kind, sub,); + err.help(&consider); + } + } + + let new_binding_suggestion = + |err: &mut Diagnostic, type_param_span: Option<(Span, bool)>| { + let msg = "consider introducing an explicit lifetime bound"; + if let Some((sp, has_lifetimes)) = type_param_span { + let suggestion = if has_lifetimes { + format!(" + {}", new_lt) + } else { + format!(": {}", new_lt) + }; + let mut sugg = + vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {}", new_lt))]; + if let Some(lt) = add_lt_sugg { + sugg.push(lt); + sugg.rotate_right(1); + } + // `MaybeIncorrect` due to issue #41966. + err.multipart_suggestion(msg, sugg, Applicability::MaybeIncorrect); + } + }; + + #[derive(Debug)] + enum SubOrigin<'hir> { + GAT(&'hir hir::Generics<'hir>), + Impl, + Trait, + Fn, + Unknown, + } + let sub_origin = 'origin: { + match *sub { + ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, .. }) => { + let node = self.tcx.hir().get_if_local(def_id).unwrap(); + match node { + Node::GenericParam(param) => { + for h in self.tcx.hir().parent_iter(param.hir_id) { + break 'origin match h.1 { + Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::TyAlias(..), + generics, + .. + }) + | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Type(..), + generics, + .. + }) => SubOrigin::GAT(generics), + Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(..), + .. + }) + | Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(..), + .. + }) + | Node::Item(hir::Item { + kind: hir::ItemKind::Fn(..), .. + }) => SubOrigin::Fn, + Node::Item(hir::Item { + kind: hir::ItemKind::Trait(..), + .. + }) => SubOrigin::Trait, + Node::Item(hir::Item { + kind: hir::ItemKind::Impl(..), .. + }) => SubOrigin::Impl, + _ => continue, + }; + } + } + _ => {} + } + } + _ => {} + } + SubOrigin::Unknown + }; + debug!(?sub_origin); + + let mut err = match (*sub, sub_origin) { + // In the case of GATs, we have to be careful. If we a type parameter `T` on an impl, + // but a lifetime `'a` on an associated type, then we might need to suggest adding + // `where T: 'a`. Importantly, this is on the GAT span, not on the `T` declaration. + (ty::ReEarlyBound(ty::EarlyBoundRegion { name: _, .. }), SubOrigin::GAT(generics)) => { + // Does the required lifetime have a nice name we can print? + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0309, + "{} may not live long enough", + labeled_user_string + ); + let pred = format!("{}: {}", bound_kind, sub); + let suggestion = format!("{} {}", generics.add_where_or_trailing_comma(), pred,); + err.span_suggestion( + generics.tail_span_for_predicate_suggestion(), + "consider adding a where clause", + suggestion, + Applicability::MaybeIncorrect, + ); + err + } + ( + ty::ReEarlyBound(ty::EarlyBoundRegion { name, .. }) + | ty::ReFree(ty::FreeRegion { bound_region: ty::BrNamed(_, name), .. }), + _, + ) if name != kw::UnderscoreLifetime => { + // Does the required lifetime have a nice name we can print? + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0309, + "{} may not live long enough", + labeled_user_string + ); + // Explicitly use the name instead of `sub`'s `Display` impl. The `Display` impl + // for the bound is not suitable for suggestions when `-Zverbose` is set because it + // uses `Debug` output, so we handle it specially here so that suggestions are + // always correct. + binding_suggestion(&mut err, type_param_span, bound_kind, name); + err + } + + (ty::ReStatic, _) => { + // Does the required lifetime have a nice name we can print? + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0310, + "{} may not live long enough", + labeled_user_string + ); + binding_suggestion(&mut err, type_param_span, bound_kind, "'static"); + err + } + + _ => { + // If not, be less specific. + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0311, + "{} may not live long enough", + labeled_user_string + ); + note_and_explain_region( + self.tcx, + &mut err, + &format!("{} must be valid for ", labeled_user_string), + sub, + "...", + None, + ); + if let Some(infer::RelateParamBound(_, t, _)) = origin { + let return_impl_trait = + self.tcx.return_type_impl_trait(generic_param_scope).is_some(); + let t = self.resolve_vars_if_possible(t); + match t.kind() { + // We've got: + // fn get_later<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + // suggest: + // fn get_later<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a + ty::Closure(_, _substs) | ty::Opaque(_, _substs) if return_impl_trait => { + new_binding_suggestion(&mut err, type_param_span); + } + _ => { + binding_suggestion(&mut err, type_param_span, bound_kind, new_lt); + } + } + } + err + } + }; + + if let Some(origin) = origin { + self.note_region_origin(&mut err, &origin); + } + err + } + + fn report_sub_sup_conflict( + &self, + var_origin: RegionVariableOrigin, + sub_origin: SubregionOrigin<'tcx>, + sub_region: Region<'tcx>, + sup_origin: SubregionOrigin<'tcx>, + sup_region: Region<'tcx>, + ) { + let mut err = self.report_inference_failure(var_origin); + + note_and_explain_region( + self.tcx, + &mut err, + "first, the lifetime cannot outlive ", + sup_region, + "...", + None, + ); + + debug!("report_sub_sup_conflict: var_origin={:?}", var_origin); + debug!("report_sub_sup_conflict: sub_region={:?}", sub_region); + debug!("report_sub_sup_conflict: sub_origin={:?}", sub_origin); + debug!("report_sub_sup_conflict: sup_region={:?}", sup_region); + debug!("report_sub_sup_conflict: sup_origin={:?}", sup_origin); + + if let (&infer::Subtype(ref sup_trace), &infer::Subtype(ref sub_trace)) = + (&sup_origin, &sub_origin) + { + debug!("report_sub_sup_conflict: sup_trace={:?}", sup_trace); + debug!("report_sub_sup_conflict: sub_trace={:?}", sub_trace); + debug!("report_sub_sup_conflict: sup_trace.values={:?}", sup_trace.values); + debug!("report_sub_sup_conflict: sub_trace.values={:?}", sub_trace.values); + + if let (Some((sup_expected, sup_found)), Some((sub_expected, sub_found))) = + (self.values_str(sup_trace.values), self.values_str(sub_trace.values)) + { + if sub_expected == sup_expected && sub_found == sup_found { + note_and_explain_region( + self.tcx, + &mut err, + "...but the lifetime must also be valid for ", + sub_region, + "...", + None, + ); + err.span_note( + sup_trace.cause.span, + &format!("...so that the {}", sup_trace.cause.as_requirement_str()), + ); + + err.note_expected_found(&"", sup_expected, &"", sup_found); + err.emit(); + return; + } + } + } + + self.note_region_origin(&mut err, &sup_origin); + + note_and_explain_region( + self.tcx, + &mut err, + "but, the lifetime must be valid for ", + sub_region, + "...", + None, + ); + + self.note_region_origin(&mut err, &sub_origin); + err.emit(); + } + + /// Determine whether an error associated with the given span and definition + /// should be treated as being caused by the implicit `From` conversion + /// within `?` desugaring. + pub fn is_try_conversion(&self, span: Span, trait_def_id: DefId) -> bool { + span.is_desugaring(DesugaringKind::QuestionMark) + && self.tcx.is_diagnostic_item(sym::From, trait_def_id) + } + + /// Structurally compares two types, modulo any inference variables. + /// + /// Returns `true` if two types are equal, or if one type is an inference variable compatible + /// with the other type. A TyVar inference type is compatible with any type, and an IntVar or + /// FloatVar inference type are compatible with themselves or their concrete types (Int and + /// Float types, respectively). When comparing two ADTs, these rules apply recursively. + pub fn same_type_modulo_infer(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool { + let (a, b) = self.resolve_vars_if_possible((a, b)); + match (a.kind(), b.kind()) { + (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b)) => { + if def_a != def_b { + return false; + } + + substs_a + .types() + .zip(substs_b.types()) + .all(|(a, b)| self.same_type_modulo_infer(a, b)) + } + (&ty::FnDef(did_a, substs_a), &ty::FnDef(did_b, substs_b)) => { + if did_a != did_b { + return false; + } + + substs_a + .types() + .zip(substs_b.types()) + .all(|(a, b)| self.same_type_modulo_infer(a, b)) + } + (&ty::Int(_) | &ty::Uint(_), &ty::Infer(ty::InferTy::IntVar(_))) + | ( + &ty::Infer(ty::InferTy::IntVar(_)), + &ty::Int(_) | &ty::Uint(_) | &ty::Infer(ty::InferTy::IntVar(_)), + ) + | (&ty::Float(_), &ty::Infer(ty::InferTy::FloatVar(_))) + | ( + &ty::Infer(ty::InferTy::FloatVar(_)), + &ty::Float(_) | &ty::Infer(ty::InferTy::FloatVar(_)), + ) + | (&ty::Infer(ty::InferTy::TyVar(_)), _) + | (_, &ty::Infer(ty::InferTy::TyVar(_))) => true, + (&ty::Ref(_, ty_a, mut_a), &ty::Ref(_, ty_b, mut_b)) => { + mut_a == mut_b && self.same_type_modulo_infer(ty_a, ty_b) + } + (&ty::RawPtr(a), &ty::RawPtr(b)) => { + a.mutbl == b.mutbl && self.same_type_modulo_infer(a.ty, b.ty) + } + (&ty::Slice(a), &ty::Slice(b)) => self.same_type_modulo_infer(a, b), + (&ty::Array(a_ty, a_ct), &ty::Array(b_ty, b_ct)) => { + self.same_type_modulo_infer(a_ty, b_ty) && a_ct == b_ct + } + (&ty::Tuple(a), &ty::Tuple(b)) => { + if a.len() != b.len() { + return false; + } + std::iter::zip(a.iter(), b.iter()).all(|(a, b)| self.same_type_modulo_infer(a, b)) + } + (&ty::FnPtr(a), &ty::FnPtr(b)) => { + let a = a.skip_binder().inputs_and_output; + let b = b.skip_binder().inputs_and_output; + if a.len() != b.len() { + return false; + } + std::iter::zip(a.iter(), b.iter()).all(|(a, b)| self.same_type_modulo_infer(a, b)) + } + // FIXME(compiler-errors): This needs to be generalized more + _ => a == b, + } + } +} + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + fn report_inference_failure( + &self, + var_origin: RegionVariableOrigin, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let br_string = |br: ty::BoundRegionKind| { + let mut s = match br { + ty::BrNamed(_, name) => name.to_string(), + _ => String::new(), + }; + if !s.is_empty() { + s.push(' '); + } + s + }; + let var_description = match var_origin { + infer::MiscVariable(_) => String::new(), + infer::PatternRegion(_) => " for pattern".to_string(), + infer::AddrOfRegion(_) => " for borrow expression".to_string(), + infer::Autoref(_) => " for autoref".to_string(), + infer::Coercion(_) => " for automatic coercion".to_string(), + infer::LateBoundRegion(_, br, infer::FnCall) => { + format!(" for lifetime parameter {}in function call", br_string(br)) + } + infer::LateBoundRegion(_, br, infer::HigherRankedType) => { + format!(" for lifetime parameter {}in generic type", br_string(br)) + } + infer::LateBoundRegion(_, br, infer::AssocTypeProjection(def_id)) => format!( + " for lifetime parameter {}in trait containing associated type `{}`", + br_string(br), + self.tcx.associated_item(def_id).name + ), + infer::EarlyBoundRegion(_, name) => format!(" for lifetime parameter `{}`", name), + infer::UpvarRegion(ref upvar_id, _) => { + let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); + format!(" for capture of `{}` by closure", var_name) + } + infer::Nll(..) => bug!("NLL variable found in lexical phase"), + }; + + struct_span_err!( + self.tcx.sess, + var_origin.span(), + E0495, + "cannot infer an appropriate lifetime{} due to conflicting requirements", + var_description + ) + } +} + +pub enum FailureCode { + Error0038(DefId), + Error0317(&'static str), + Error0580(&'static str), + Error0308(&'static str), + Error0644(&'static str), +} + +pub trait ObligationCauseExt<'tcx> { + fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode; + fn as_requirement_str(&self) -> &'static str; +} + +impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> { + fn as_failure_code(&self, terr: &TypeError<'tcx>) -> FailureCode { + use self::FailureCode::*; + use crate::traits::ObligationCauseCode::*; + match self.code() { + CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => { + Error0308("method not compatible with trait") + } + CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => { + Error0308("type not compatible with trait") + } + CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => { + Error0308("const not compatible with trait") + } + MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => { + Error0308(match source { + hir::MatchSource::TryDesugar => "`?` operator has incompatible types", + _ => "`match` arms have incompatible types", + }) + } + IfExpression { .. } => Error0308("`if` and `else` have incompatible types"), + IfExpressionWithNoElse => Error0317("`if` may be missing an `else` clause"), + LetElse => Error0308("`else` clause of `let...else` does not diverge"), + MainFunctionType => Error0580("`main` function has wrong type"), + StartFunctionType => Error0308("`#[start]` function has wrong type"), + IntrinsicType => Error0308("intrinsic has wrong type"), + MethodReceiver => Error0308("mismatched `self` parameter type"), + + // In the case where we have no more specific thing to + // say, also take a look at the error code, maybe we can + // tailor to that. + _ => match terr { + TypeError::CyclicTy(ty) if ty.is_closure() || ty.is_generator() => { + Error0644("closure/generator type that references itself") + } + TypeError::IntrinsicCast => { + Error0308("cannot coerce intrinsics to function pointers") + } + TypeError::ObjectUnsafeCoercion(did) => Error0038(*did), + _ => Error0308("mismatched types"), + }, + } + } + + fn as_requirement_str(&self) -> &'static str { + use crate::traits::ObligationCauseCode::*; + match self.code() { + CompareImplItemObligation { kind: ty::AssocKind::Fn, .. } => { + "method type is compatible with trait" + } + CompareImplItemObligation { kind: ty::AssocKind::Type, .. } => { + "associated type is compatible with trait" + } + CompareImplItemObligation { kind: ty::AssocKind::Const, .. } => { + "const is compatible with trait" + } + ExprAssignable => "expression is assignable", + IfExpression { .. } => "`if` and `else` have incompatible types", + IfExpressionWithNoElse => "`if` missing an `else` returns `()`", + MainFunctionType => "`main` function has the correct type", + StartFunctionType => "`#[start]` function has the correct type", + IntrinsicType => "intrinsic has the correct type", + MethodReceiver => "method receiver has the correct type", + _ => "types are compatible", + } + } +} + +/// This is a bare signal of what kind of type we're dealing with. `ty::TyKind` tracks +/// extra information about each type, but we only care about the category. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub enum TyCategory { + Closure, + Opaque, + Generator(hir::GeneratorKind), + Foreign, +} + +impl TyCategory { + fn descr(&self) -> &'static str { + match self { + Self::Closure => "closure", + Self::Opaque => "opaque type", + Self::Generator(gk) => gk.descr(), + Self::Foreign => "foreign type", + } + } + + pub fn from_ty(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<(Self, DefId)> { + match *ty.kind() { + ty::Closure(def_id, _) => Some((Self::Closure, def_id)), + ty::Opaque(def_id, _) => Some((Self::Opaque, def_id)), + ty::Generator(def_id, ..) => { + Some((Self::Generator(tcx.generator_kind(def_id).unwrap()), def_id)) + } + ty::Foreign(def_id) => Some((Self::Foreign, def_id)), + _ => None, + } + } +} + +impl<'tcx> InferCtxt<'_, 'tcx> { + /// Given a [`hir::Block`], get the span of its last expression or + /// statement, peeling off any inner blocks. + pub fn find_block_span(&self, block: &'tcx hir::Block<'tcx>) -> Span { + let block = block.innermost_block(); + if let Some(expr) = &block.expr { + expr.span + } else if let Some(stmt) = block.stmts.last() { + // possibly incorrect trailing `;` in the else arm + stmt.span + } else { + // empty block; point at its entirety + block.span + } + } + + /// Given a [`hir::HirId`] for a block, get the span of its last expression + /// or statement, peeling off any inner blocks. + pub fn find_block_span_from_hir_id(&self, hir_id: hir::HirId) -> Span { + match self.tcx.hir().get(hir_id) { + hir::Node::Block(blk) => self.find_block_span(blk), + // The parser was in a weird state if either of these happen, but + // it's better not to panic. + hir::Node::Expr(e) => e.span, + _ => rustc_span::DUMMY_SP, + } + } + + /// Be helpful when the user wrote `{... expr; }` and taking the `;` off + /// is enough to fix the error. + pub fn could_remove_semicolon( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + ) -> Option<(Span, StatementAsExpression)> { + let blk = blk.innermost_block(); + // Do not suggest if we have a tail expr. + if blk.expr.is_some() { + return None; + } + let last_stmt = blk.stmts.last()?; + let hir::StmtKind::Semi(ref last_expr) = last_stmt.kind else { + return None; + }; + let last_expr_ty = self.in_progress_typeck_results?.borrow().expr_ty_opt(*last_expr)?; + let needs_box = match (last_expr_ty.kind(), expected_ty.kind()) { + _ if last_expr_ty.references_error() => return None, + _ if self.same_type_modulo_infer(last_expr_ty, expected_ty) => { + StatementAsExpression::CorrectType + } + (ty::Opaque(last_def_id, _), ty::Opaque(exp_def_id, _)) + if last_def_id == exp_def_id => + { + StatementAsExpression::CorrectType + } + (ty::Opaque(last_def_id, last_bounds), ty::Opaque(exp_def_id, exp_bounds)) => { + debug!( + "both opaque, likely future {:?} {:?} {:?} {:?}", + last_def_id, last_bounds, exp_def_id, exp_bounds + ); + + let last_local_id = last_def_id.as_local()?; + let exp_local_id = exp_def_id.as_local()?; + + match ( + &self.tcx.hir().expect_item(last_local_id).kind, + &self.tcx.hir().expect_item(exp_local_id).kind, + ) { + ( + hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: last_bounds, .. }), + hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds: exp_bounds, .. }), + ) if iter::zip(*last_bounds, *exp_bounds).all(|(left, right)| { + match (left, right) { + ( + hir::GenericBound::Trait(tl, ml), + hir::GenericBound::Trait(tr, mr), + ) if tl.trait_ref.trait_def_id() == tr.trait_ref.trait_def_id() + && ml == mr => + { + true + } + ( + hir::GenericBound::LangItemTrait(langl, _, _, argsl), + hir::GenericBound::LangItemTrait(langr, _, _, argsr), + ) if langl == langr => { + // FIXME: consider the bounds! + debug!("{:?} {:?}", argsl, argsr); + true + } + _ => false, + } + }) => + { + StatementAsExpression::NeedsBoxing + } + _ => StatementAsExpression::CorrectType, + } + } + _ => return None, + }; + let span = if last_stmt.span.from_expansion() { + let mac_call = rustc_span::source_map::original_sp(last_stmt.span, blk.span); + self.tcx.sess.source_map().mac_call_stmt_semi_span(mac_call)? + } else { + last_stmt.span.with_lo(last_stmt.span.hi() - BytePos(1)) + }; + Some((span, needs_box)) + } + + /// Suggest returning a local binding with a compatible type if the block + /// has no return expression. + pub fn consider_returning_binding( + &self, + blk: &'tcx hir::Block<'tcx>, + expected_ty: Ty<'tcx>, + err: &mut Diagnostic, + ) -> bool { + let blk = blk.innermost_block(); + // Do not suggest if we have a tail expr. + if blk.expr.is_some() { + return false; + } + let mut shadowed = FxHashSet::default(); + let mut candidate_idents = vec![]; + let mut find_compatible_candidates = |pat: &hir::Pat<'_>| { + if let hir::PatKind::Binding(_, hir_id, ident, _) = &pat.kind + && let Some(pat_ty) = self + .in_progress_typeck_results + .and_then(|typeck_results| typeck_results.borrow().node_type_opt(*hir_id)) + { + let pat_ty = self.resolve_vars_if_possible(pat_ty); + if self.same_type_modulo_infer(pat_ty, expected_ty) + && !(pat_ty, expected_ty).references_error() + && shadowed.insert(ident.name) + { + candidate_idents.push((*ident, pat_ty)); + } + } + true + }; + + let hir = self.tcx.hir(); + for stmt in blk.stmts.iter().rev() { + let hir::StmtKind::Local(local) = &stmt.kind else { continue; }; + local.pat.walk(&mut find_compatible_candidates); + } + match hir.find(hir.get_parent_node(blk.hir_id)) { + Some(hir::Node::Expr(hir::Expr { hir_id, .. })) => { + match hir.find(hir.get_parent_node(*hir_id)) { + Some(hir::Node::Arm(hir::Arm { pat, .. })) => { + pat.walk(&mut find_compatible_candidates); + } + Some( + hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, _, body), .. }) + | hir::Node::ImplItem(hir::ImplItem { + kind: hir::ImplItemKind::Fn(_, body), + .. + }) + | hir::Node::TraitItem(hir::TraitItem { + kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(body)), + .. + }) + | hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { body, .. }), + .. + }), + ) => { + for param in hir.body(*body).params { + param.pat.walk(&mut find_compatible_candidates); + } + } + Some(hir::Node::Expr(hir::Expr { + kind: + hir::ExprKind::If( + hir::Expr { kind: hir::ExprKind::Let(let_), .. }, + then_block, + _, + ), + .. + })) if then_block.hir_id == *hir_id => { + let_.pat.walk(&mut find_compatible_candidates); + } + _ => {} + } + } + _ => {} + } + + match &candidate_idents[..] { + [(ident, _ty)] => { + let sm = self.tcx.sess.source_map(); + if let Some(stmt) = blk.stmts.last() { + let stmt_span = sm.stmt_span(stmt.span, blk.span); + let sugg = if sm.is_multiline(blk.span) + && let Some(spacing) = sm.indentation_before(stmt_span) + { + format!("\n{spacing}{ident}") + } else { + format!(" {ident}") + }; + err.span_suggestion_verbose( + stmt_span.shrink_to_hi(), + format!("consider returning the local binding `{ident}`"), + sugg, + Applicability::MaybeIncorrect, + ); + } else { + let sugg = if sm.is_multiline(blk.span) + && let Some(spacing) = sm.indentation_before(blk.span.shrink_to_lo()) + { + format!("\n{spacing} {ident}\n{spacing}") + } else { + format!(" {ident} ") + }; + let left_span = sm.span_through_char(blk.span, '{').shrink_to_hi(); + err.span_suggestion_verbose( + sm.span_extend_while(left_span, |c| c.is_whitespace()).unwrap_or(left_span), + format!("consider returning the local binding `{ident}`"), + sugg, + Applicability::MaybeIncorrect, + ); + } + true + } + values if (1..3).contains(&values.len()) => { + let spans = values.iter().map(|(ident, _)| ident.span).collect::<Vec<_>>(); + err.span_note(spans, "consider returning one of these bindings"); + true + } + _ => false, + } + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs new file mode 100644 index 000000000..561d1354e --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -0,0 +1,1134 @@ +use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::infer::InferCtxt; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::def::{CtorOf, DefKind, Namespace}; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{Body, Closure, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource}; +use rustc_middle::hir::nested_filter; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst, SubstsRef}; +use rustc_middle::ty::{self, DefIdTree, InferConst}; +use rustc_middle::ty::{IsSuggestable, Ty, TyCtxt, TypeckResults}; +use rustc_span::symbol::{kw, Ident}; +use rustc_span::{BytePos, Span}; +use std::borrow::Cow; +use std::iter; + +pub enum TypeAnnotationNeeded { + /// ```compile_fail,E0282 + /// let x = "hello".chars().rev().collect(); + /// ``` + E0282, + /// An implementation cannot be chosen unambiguously because of lack of information. + /// ```compile_fail,E0283 + /// let _ = Default::default(); + /// ``` + E0283, + /// ```compile_fail,E0284 + /// let mut d: u64 = 2; + /// d = d % 1u32.into(); + /// ``` + E0284, +} + +impl Into<rustc_errors::DiagnosticId> for TypeAnnotationNeeded { + fn into(self) -> rustc_errors::DiagnosticId { + match self { + Self::E0282 => rustc_errors::error_code!(E0282), + Self::E0283 => rustc_errors::error_code!(E0283), + Self::E0284 => rustc_errors::error_code!(E0284), + } + } +} + +/// Information about a constant or a type containing inference variables. +pub struct InferenceDiagnosticsData { + pub name: String, + pub span: Option<Span>, + pub kind: UnderspecifiedArgKind, + pub parent: Option<InferenceDiagnosticsParentData>, +} + +/// Data on the parent definition where a generic argument was declared. +pub struct InferenceDiagnosticsParentData { + prefix: &'static str, + name: String, +} + +pub enum UnderspecifiedArgKind { + Type { prefix: Cow<'static, str> }, + Const { is_parameter: bool }, +} + +impl InferenceDiagnosticsData { + /// Generate a label for a generic argument which can't be inferred. When not + /// much is known about the argument, `use_diag` may be used to describe the + /// labeled value. + fn cannot_infer_msg(&self) -> String { + if self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }) { + return "cannot infer type".to_string(); + } + + let suffix = match &self.parent { + Some(parent) => parent.suffix_string(), + None => String::new(), + }; + + // For example: "cannot infer type for type parameter `T`" + format!("cannot infer {} `{}`{}", self.kind.prefix_string(), self.name, suffix) + } + + fn where_x_is_specified(&self, in_type: Ty<'_>) -> String { + if in_type.is_ty_infer() { + String::new() + } else if self.name == "_" { + // FIXME: Consider specializing this message if there is a single `_` + // in the type. + ", where the placeholders `_` are specified".to_string() + } else { + format!(", where the {} `{}` is specified", self.kind.prefix_string(), self.name) + } + } +} + +impl InferenceDiagnosticsParentData { + fn for_parent_def_id( + tcx: TyCtxt<'_>, + parent_def_id: DefId, + ) -> Option<InferenceDiagnosticsParentData> { + let parent_name = + tcx.def_key(parent_def_id).disambiguated_data.data.get_opt_name()?.to_string(); + + Some(InferenceDiagnosticsParentData { + prefix: tcx.def_kind(parent_def_id).descr(parent_def_id), + name: parent_name, + }) + } + + fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> { + Self::for_parent_def_id(tcx, tcx.parent(def_id)) + } + + fn suffix_string(&self) -> String { + format!(" declared on the {} `{}`", self.prefix, self.name) + } +} + +impl UnderspecifiedArgKind { + fn prefix_string(&self) -> Cow<'static, str> { + match self { + Self::Type { prefix } => format!("type for {}", prefix).into(), + Self::Const { is_parameter: true } => "the value of const parameter".into(), + Self::Const { is_parameter: false } => "the value of the constant".into(), + } + } +} + +fn fmt_printer<'a, 'tcx>(infcx: &'a InferCtxt<'_, 'tcx>, ns: Namespace) -> FmtPrinter<'a, 'tcx> { + let mut printer = FmtPrinter::new(infcx.tcx, ns); + let ty_getter = move |ty_vid| { + if infcx.probe_ty_var(ty_vid).is_ok() { + warn!("resolved ty var in error message"); + } + if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = + infcx.inner.borrow_mut().type_variables().var_origin(ty_vid).kind + { + Some(name) + } else { + None + } + }; + printer.ty_infer_name_resolver = Some(Box::new(ty_getter)); + let const_getter = move |ct_vid| { + if infcx.probe_const_var(ct_vid).is_ok() { + warn!("resolved const var in error message"); + } + if let ConstVariableOriginKind::ConstParameterDefinition(name, _) = + infcx.inner.borrow_mut().const_unification_table().probe_value(ct_vid).origin.kind + { + return Some(name); + } else { + None + } + }; + printer.const_infer_name_resolver = Some(Box::new(const_getter)); + printer +} + +fn ty_to_string<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String { + let printer = fmt_printer(infcx, Namespace::TypeNS); + let ty = infcx.resolve_vars_if_possible(ty); + match ty.kind() { + // We don't want the regular output for `fn`s because it includes its path in + // invalid pseudo-syntax, we want the `fn`-pointer output instead. + ty::FnDef(..) => ty.fn_sig(infcx.tcx).print(printer).unwrap().into_buffer(), + // FIXME: The same thing for closures, but this only works when the closure + // does not capture anything. + // + // We do have to hide the `extern "rust-call"` ABI in that case though, + // which is too much of a bother for now. + _ => ty.print(printer).unwrap().into_buffer(), + } +} + +/// We don't want to directly use `ty_to_string` for closures as their type isn't really +/// something users are familar with. Directly printing the `fn_sig` of closures also +/// doesn't work as they actually use the "rust-call" API. +fn closure_as_fn_str<'tcx>(infcx: &InferCtxt<'_, 'tcx>, ty: Ty<'tcx>) -> String { + let ty::Closure(_, substs) = ty.kind() else { unreachable!() }; + let fn_sig = substs.as_closure().sig(); + let args = fn_sig + .inputs() + .skip_binder() + .iter() + .next() + .map(|args| { + args.tuple_fields() + .iter() + .map(|arg| ty_to_string(infcx, arg)) + .collect::<Vec<_>>() + .join(", ") + }) + .unwrap_or_default(); + let ret = if fn_sig.output().skip_binder().is_unit() { + String::new() + } else { + format!(" -> {}", ty_to_string(infcx, fn_sig.output().skip_binder())) + }; + format!("fn({}){}", args, ret) +} + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + /// Extracts data used by diagnostic for either types or constants + /// which were stuck during inference. + pub fn extract_inference_diagnostics_data( + &self, + arg: GenericArg<'tcx>, + highlight: Option<ty::print::RegionHighlightMode<'tcx>>, + ) -> InferenceDiagnosticsData { + match arg.unpack() { + GenericArgKind::Type(ty) => { + if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() { + let mut inner = self.inner.borrow_mut(); + let ty_vars = &inner.type_variables(); + let var_origin = ty_vars.var_origin(ty_vid); + if let TypeVariableOriginKind::TypeParameterDefinition(name, def_id) = + var_origin.kind + { + if name != kw::SelfUpper { + return InferenceDiagnosticsData { + name: name.to_string(), + span: Some(var_origin.span), + kind: UnderspecifiedArgKind::Type { + prefix: "type parameter".into(), + }, + parent: def_id.and_then(|def_id| { + InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id) + }), + }; + } + } + } + + let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS); + if let Some(highlight) = highlight { + printer.region_highlight_mode = highlight; + } + InferenceDiagnosticsData { + name: ty.print(printer).unwrap().into_buffer(), + span: None, + kind: UnderspecifiedArgKind::Type { prefix: ty.prefix_string(self.tcx) }, + parent: None, + } + } + GenericArgKind::Const(ct) => { + if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() { + let origin = + self.inner.borrow_mut().const_unification_table().probe_value(vid).origin; + if let ConstVariableOriginKind::ConstParameterDefinition(name, def_id) = + origin.kind + { + return InferenceDiagnosticsData { + name: name.to_string(), + span: Some(origin.span), + kind: UnderspecifiedArgKind::Const { is_parameter: true }, + parent: InferenceDiagnosticsParentData::for_def_id(self.tcx, def_id), + }; + } + + debug_assert!(!origin.span.is_dummy()); + let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS); + if let Some(highlight) = highlight { + printer.region_highlight_mode = highlight; + } + InferenceDiagnosticsData { + name: ct.print(printer).unwrap().into_buffer(), + span: Some(origin.span), + kind: UnderspecifiedArgKind::Const { is_parameter: false }, + parent: None, + } + } else { + // If we end up here the `FindInferSourceVisitor` + // won't work, as its expected argument isn't an inference variable. + // + // FIXME: Ideally we should look into the generic constant + // to figure out which inference var is actually unresolved so that + // this path is unreachable. + let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::ValueNS); + if let Some(highlight) = highlight { + printer.region_highlight_mode = highlight; + } + InferenceDiagnosticsData { + name: ct.print(printer).unwrap().into_buffer(), + span: None, + kind: UnderspecifiedArgKind::Const { is_parameter: false }, + parent: None, + } + } + } + GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), + } + } + + /// Used as a fallback in [InferCtxt::emit_inference_failure_err] + /// in case we weren't able to get a better error. + fn bad_inference_failure_err( + &self, + span: Span, + arg_data: InferenceDiagnosticsData, + error_code: TypeAnnotationNeeded, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let error_code = error_code.into(); + let mut err = + self.tcx.sess.struct_span_err_with_code(span, "type annotations needed", error_code); + err.span_label(span, arg_data.cannot_infer_msg()); + err + } + + pub fn emit_inference_failure_err( + &self, + body_id: Option<hir::BodyId>, + failure_span: Span, + arg: GenericArg<'tcx>, + error_code: TypeAnnotationNeeded, + should_label_span: bool, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let arg = self.resolve_vars_if_possible(arg); + let arg_data = self.extract_inference_diagnostics_data(arg, None); + + let Some(typeck_results) = self.in_progress_typeck_results else { + // If we don't have any typeck results we're outside + // of a body, so we won't be able to get better info + // here. + return self.bad_inference_failure_err(failure_span, arg_data, error_code); + }; + let typeck_results = typeck_results.borrow(); + let typeck_results = &typeck_results; + + let mut local_visitor = FindInferSourceVisitor::new(&self, typeck_results, arg); + if let Some(body_id) = body_id { + let expr = self.tcx.hir().expect_expr(body_id.hir_id); + local_visitor.visit_expr(expr); + } + + let Some(InferSource { span, kind }) = local_visitor.infer_source else { + return self.bad_inference_failure_err(failure_span, arg_data, error_code) + }; + + let error_code = error_code.into(); + let mut err = self.tcx.sess.struct_span_err_with_code( + span, + &format!("type annotations needed{}", kind.ty_msg(self)), + error_code, + ); + + if should_label_span && !failure_span.overlaps(span) { + err.span_label(failure_span, "type must be known at this point"); + } + + match kind { + InferSourceKind::LetBinding { insert_span, pattern_name, ty } => { + let suggestion_msg = if let Some(name) = pattern_name { + format!( + "consider giving `{}` an explicit type{}", + name, + arg_data.where_x_is_specified(ty) + ) + } else { + format!( + "consider giving this pattern a type{}", + arg_data.where_x_is_specified(ty) + ) + }; + err.span_suggestion_verbose( + insert_span, + &suggestion_msg, + format!(": {}", ty_to_string(self, ty)), + Applicability::HasPlaceholders, + ); + } + InferSourceKind::ClosureArg { insert_span, ty } => { + err.span_suggestion_verbose( + insert_span, + &format!( + "consider giving this closure parameter an explicit type{}", + arg_data.where_x_is_specified(ty) + ), + format!(": {}", ty_to_string(self, ty)), + Applicability::HasPlaceholders, + ); + } + InferSourceKind::GenericArg { + insert_span, + argument_index, + generics_def_id, + def_id: _, + generic_args, + } => { + let generics = self.tcx.generics_of(generics_def_id); + let is_type = matches!(arg.unpack(), GenericArgKind::Type(_)); + + let cannot_infer_msg = format!( + "cannot infer {} of the {} parameter `{}`{}", + if is_type { "type" } else { "the value" }, + if is_type { "type" } else { "const" }, + generics.params[argument_index].name, + // We use the `generics_def_id` here, as even when suggesting `None::<T>`, + // the type parameter `T` was still declared on the enum, not on the + // variant. + InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id) + .map_or(String::new(), |parent| parent.suffix_string()), + ); + + err.span_label(span, cannot_infer_msg); + + let args = fmt_printer(self, Namespace::TypeNS) + .comma_sep(generic_args.iter().copied().map(|arg| { + if arg.is_suggestable(self.tcx, true) { + return arg; + } + + match arg.unpack() { + GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), + GenericArgKind::Type(_) => self + .next_ty_var(TypeVariableOrigin { + span: rustc_span::DUMMY_SP, + kind: TypeVariableOriginKind::MiscVariable, + }) + .into(), + GenericArgKind::Const(arg) => self + .next_const_var( + arg.ty(), + ConstVariableOrigin { + span: rustc_span::DUMMY_SP, + kind: ConstVariableOriginKind::MiscVariable, + }, + ) + .into(), + } + })) + .unwrap() + .into_buffer(); + + err.span_suggestion_verbose( + insert_span, + &format!( + "consider specifying the generic argument{}", + pluralize!(generic_args.len()), + ), + format!("::<{}>", args), + Applicability::HasPlaceholders, + ); + } + InferSourceKind::FullyQualifiedMethodCall { receiver, successor, substs, def_id } => { + let printer = fmt_printer(self, Namespace::ValueNS); + let def_path = printer.print_def_path(def_id, substs).unwrap().into_buffer(); + + // We only care about whether we have to add `&` or `&mut ` for now. + // This is the case if the last adjustment is a borrow and the + // first adjustment was not a builtin deref. + let adjustment = match typeck_results.expr_adjustments(receiver) { + [ + Adjustment { kind: Adjust::Deref(None), target: _ }, + .., + Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, + ] => "", + [ + .., + Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mut_)), target: _ }, + ] => match mut_ { + AutoBorrowMutability::Mut { .. } => "&mut ", + AutoBorrowMutability::Not => "&", + }, + _ => "", + }; + + let suggestion = vec![ + (receiver.span.shrink_to_lo(), format!("{def_path}({adjustment}")), + (receiver.span.shrink_to_hi().with_hi(successor.1), successor.0.to_string()), + ]; + err.multipart_suggestion_verbose( + "try using a fully qualified path to specify the expected types", + suggestion, + Applicability::HasPlaceholders, + ); + } + InferSourceKind::ClosureReturn { ty, data, should_wrap_expr } => { + let ret = ty_to_string(self, ty); + let (arrow, post) = match data { + FnRetTy::DefaultReturn(_) => ("-> ", " "), + _ => ("", ""), + }; + let suggestion = match should_wrap_expr { + Some(end_span) => vec![ + (data.span(), format!("{}{}{}{{ ", arrow, ret, post)), + (end_span, " }".to_string()), + ], + None => vec![(data.span(), format!("{}{}{}", arrow, ret, post))], + }; + err.multipart_suggestion_verbose( + "try giving this closure an explicit return type", + suggestion, + Applicability::HasPlaceholders, + ); + } + } + err + } + + pub fn need_type_info_err_in_generator( + &self, + kind: hir::GeneratorKind, + span: Span, + ty: Ty<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let ty = self.resolve_vars_if_possible(ty); + let data = self.extract_inference_diagnostics_data(ty.into(), None); + + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0698, + "type inside {} must be known in this context", + kind, + ); + err.span_label(span, data.cannot_infer_msg()); + err + } +} + +#[derive(Debug)] +struct InferSource<'tcx> { + span: Span, + kind: InferSourceKind<'tcx>, +} + +#[derive(Debug)] +enum InferSourceKind<'tcx> { + LetBinding { + insert_span: Span, + pattern_name: Option<Ident>, + ty: Ty<'tcx>, + }, + ClosureArg { + insert_span: Span, + ty: Ty<'tcx>, + }, + GenericArg { + insert_span: Span, + argument_index: usize, + generics_def_id: DefId, + def_id: DefId, + generic_args: &'tcx [GenericArg<'tcx>], + }, + FullyQualifiedMethodCall { + receiver: &'tcx Expr<'tcx>, + /// If the method has other arguments, this is ", " and the start of the first argument, + /// while for methods without arguments this is ")" and the end of the method call. + successor: (&'static str, BytePos), + substs: SubstsRef<'tcx>, + def_id: DefId, + }, + ClosureReturn { + ty: Ty<'tcx>, + data: &'tcx FnRetTy<'tcx>, + should_wrap_expr: Option<Span>, + }, +} + +impl<'tcx> InferSource<'tcx> { + fn from_expansion(&self) -> bool { + let source_from_expansion = match self.kind { + InferSourceKind::LetBinding { insert_span, .. } + | InferSourceKind::ClosureArg { insert_span, .. } + | InferSourceKind::GenericArg { insert_span, .. } => insert_span.from_expansion(), + InferSourceKind::FullyQualifiedMethodCall { receiver, .. } => { + receiver.span.from_expansion() + } + InferSourceKind::ClosureReturn { data, should_wrap_expr, .. } => { + data.span().from_expansion() || should_wrap_expr.map_or(false, Span::from_expansion) + } + }; + source_from_expansion || self.span.from_expansion() + } +} + +impl<'tcx> InferSourceKind<'tcx> { + fn ty_msg(&self, infcx: &InferCtxt<'_, 'tcx>) -> String { + match *self { + InferSourceKind::LetBinding { ty, .. } + | InferSourceKind::ClosureArg { ty, .. } + | InferSourceKind::ClosureReturn { ty, .. } => { + if ty.is_closure() { + format!(" for the closure `{}`", closure_as_fn_str(infcx, ty)) + } else if !ty.is_ty_infer() { + format!(" for `{}`", ty_to_string(infcx, ty)) + } else { + String::new() + } + } + // FIXME: We should be able to add some additional info here. + InferSourceKind::GenericArg { .. } + | InferSourceKind::FullyQualifiedMethodCall { .. } => String::new(), + } + } +} + +#[derive(Debug)] +struct InsertableGenericArgs<'tcx> { + insert_span: Span, + substs: SubstsRef<'tcx>, + generics_def_id: DefId, + def_id: DefId, +} + +/// A visitor which searches for the "best" spot to use in the inference error. +/// +/// For this it walks over the hir body and tries to check all places where +/// inference variables could be bound. +/// +/// While doing so, the currently best spot is stored in `infer_source`. +/// For details on how we rank spots, see [Self::source_cost] +struct FindInferSourceVisitor<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + typeck_results: &'a TypeckResults<'tcx>, + + target: GenericArg<'tcx>, + + attempt: usize, + infer_source_cost: usize, + infer_source: Option<InferSource<'tcx>>, +} + +impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { + fn new( + infcx: &'a InferCtxt<'a, 'tcx>, + typeck_results: &'a TypeckResults<'tcx>, + target: GenericArg<'tcx>, + ) -> Self { + FindInferSourceVisitor { + infcx, + typeck_results, + + target, + + attempt: 0, + infer_source_cost: usize::MAX, + infer_source: None, + } + } + + /// Computes cost for the given source. + /// + /// Sources with a small cost are prefer and should result + /// in a clearer and idiomatic suggestion. + fn source_cost(&self, source: &InferSource<'tcx>) -> usize { + #[derive(Clone, Copy)] + struct CostCtxt<'tcx> { + tcx: TyCtxt<'tcx>, + } + impl<'tcx> CostCtxt<'tcx> { + fn arg_cost(self, arg: GenericArg<'tcx>) -> usize { + match arg.unpack() { + GenericArgKind::Lifetime(_) => 0, // erased + GenericArgKind::Type(ty) => self.ty_cost(ty), + GenericArgKind::Const(_) => 3, // some non-zero value + } + } + fn ty_cost(self, ty: Ty<'tcx>) -> usize { + match *ty.kind() { + ty::Closure(..) => 1000, + ty::FnDef(..) => 150, + ty::FnPtr(..) => 30, + ty::Adt(def, substs) => { + 5 + self + .tcx + .generics_of(def.did()) + .own_substs_no_defaults(self.tcx, substs) + .iter() + .map(|&arg| self.arg_cost(arg)) + .sum::<usize>() + } + ty::Tuple(args) => 5 + args.iter().map(|arg| self.ty_cost(arg)).sum::<usize>(), + ty::Ref(_, ty, _) => 2 + self.ty_cost(ty), + ty::Infer(..) => 0, + _ => 1, + } + } + } + + // The sources are listed in order of preference here. + let tcx = self.infcx.tcx; + let ctx = CostCtxt { tcx }; + let base_cost = match source.kind { + InferSourceKind::LetBinding { ty, .. } => ctx.ty_cost(ty), + InferSourceKind::ClosureArg { ty, .. } => ctx.ty_cost(ty), + InferSourceKind::GenericArg { def_id, generic_args, .. } => { + let variant_cost = match tcx.def_kind(def_id) { + // `None::<u32>` and friends are ugly. + DefKind::Variant | DefKind::Ctor(CtorOf::Variant, _) => 15, + _ => 10, + }; + variant_cost + generic_args.iter().map(|&arg| ctx.arg_cost(arg)).sum::<usize>() + } + InferSourceKind::FullyQualifiedMethodCall { substs, .. } => { + 20 + substs.iter().map(|arg| ctx.arg_cost(arg)).sum::<usize>() + } + InferSourceKind::ClosureReturn { ty, should_wrap_expr, .. } => { + 30 + ctx.ty_cost(ty) + if should_wrap_expr.is_some() { 10 } else { 0 } + } + }; + + let suggestion_may_apply = if source.from_expansion() { 10000 } else { 0 }; + + base_cost + suggestion_may_apply + } + + /// Uses `fn source_cost` to determine whether this inference source is preferable to + /// previous sources. We generally prefer earlier sources. + #[instrument(level = "debug", skip(self))] + fn update_infer_source(&mut self, new_source: InferSource<'tcx>) { + let cost = self.source_cost(&new_source) + self.attempt; + debug!(?cost); + self.attempt += 1; + if cost < self.infer_source_cost { + self.infer_source_cost = cost; + self.infer_source = Some(new_source); + } + } + + fn node_substs_opt(&self, hir_id: HirId) -> Option<SubstsRef<'tcx>> { + let substs = self.typeck_results.node_substs_opt(hir_id); + self.infcx.resolve_vars_if_possible(substs) + } + + fn opt_node_type(&self, hir_id: HirId) -> Option<Ty<'tcx>> { + let ty = self.typeck_results.node_type_opt(hir_id); + self.infcx.resolve_vars_if_possible(ty) + } + + // Check whether this generic argument is the inference variable we + // are looking for. + fn generic_arg_is_target(&self, arg: GenericArg<'tcx>) -> bool { + if arg == self.target { + return true; + } + + match (arg.unpack(), self.target.unpack()) { + (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => { + use ty::{Infer, TyVar}; + match (inner_ty.kind(), target_ty.kind()) { + (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => { + self.infcx.inner.borrow_mut().type_variables().sub_unified(a_vid, b_vid) + } + _ => false, + } + } + (GenericArgKind::Const(inner_ct), GenericArgKind::Const(target_ct)) => { + use ty::InferConst::*; + match (inner_ct.kind(), target_ct.kind()) { + (ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => self + .infcx + .inner + .borrow_mut() + .const_unification_table() + .unioned(a_vid, b_vid), + _ => false, + } + } + _ => false, + } + } + + /// Does this generic argument contain our target inference variable + /// in a way which can be written by the user. + fn generic_arg_contains_target(&self, arg: GenericArg<'tcx>) -> bool { + let mut walker = arg.walk(); + while let Some(inner) = walker.next() { + if self.generic_arg_is_target(inner) { + return true; + } + match inner.unpack() { + GenericArgKind::Lifetime(_) => {} + GenericArgKind::Type(ty) => { + if matches!(ty.kind(), ty::Opaque(..) | ty::Closure(..) | ty::Generator(..)) { + // Opaque types can't be named by the user right now. + // + // Both the generic arguments of closures and generators can + // also not be named. We may want to only look into the closure + // signature in case it has no captures, as that can be represented + // using `fn(T) -> R`. + + // FIXME(type_alias_impl_trait): These opaque types + // can actually be named, so it would make sense to + // adjust this case and add a test for it. + walker.skip_current_subtree(); + } + } + GenericArgKind::Const(ct) => { + if matches!(ct.kind(), ty::ConstKind::Unevaluated(..)) { + // You can't write the generic arguments for + // unevaluated constants. + walker.skip_current_subtree(); + } + } + } + } + false + } + + fn expr_inferred_subst_iter( + &self, + expr: &'tcx hir::Expr<'tcx>, + ) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> { + let tcx = self.infcx.tcx; + match expr.kind { + hir::ExprKind::Path(ref path) => { + if let Some(substs) = self.node_substs_opt(expr.hir_id) { + return self.path_inferred_subst_iter(expr.hir_id, substs, path); + } + } + // FIXME(#98711): Ideally we would also deal with type relative + // paths here, even if that is quite rare. + // + // See the `need_type_info/expr-struct-type-relative-gat.rs` test + // for an example where that would be needed. + // + // However, the `type_dependent_def_id` for `Self::Output` in an + // impl is currently the `DefId` of `Output` in the trait definition + // which makes this somewhat difficult and prevents us from just + // using `self.path_inferred_subst_iter` here. + hir::ExprKind::Struct(&hir::QPath::Resolved(_self_ty, path), _, _) => { + if let Some(ty) = self.opt_node_type(expr.hir_id) { + if let ty::Adt(_, substs) = ty.kind() { + return Box::new(self.resolved_path_inferred_subst_iter(path, substs)); + } + } + } + hir::ExprKind::MethodCall(segment, _, _) => { + if let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) { + let generics = tcx.generics_of(def_id); + let insertable: Option<_> = try { + if generics.has_impl_trait() { + None? + } + let substs = self.node_substs_opt(expr.hir_id)?; + let span = tcx.hir().span(segment.hir_id?); + let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); + InsertableGenericArgs { + insert_span, + substs, + generics_def_id: def_id, + def_id, + } + }; + return Box::new(insertable.into_iter()); + } + } + _ => {} + } + + Box::new(iter::empty()) + } + + fn resolved_path_inferred_subst_iter( + &self, + path: &'tcx hir::Path<'tcx>, + substs: SubstsRef<'tcx>, + ) -> impl Iterator<Item = InsertableGenericArgs<'tcx>> + 'a { + let tcx = self.infcx.tcx; + // The last segment of a path often has `Res::Err` and the + // correct `Res` is the one of the whole path. + // + // FIXME: We deal with that one separately for now, + // would be good to remove this special case. + let last_segment_using_path_data: Option<_> = try { + let generics_def_id = tcx.res_generics_def_id(path.res)?; + let generics = tcx.generics_of(generics_def_id); + if generics.has_impl_trait() { + None? + } + let insert_span = + path.segments.last().unwrap().ident.span.shrink_to_hi().with_hi(path.span.hi()); + InsertableGenericArgs { + insert_span, + substs, + generics_def_id, + def_id: path.res.def_id(), + } + }; + + path.segments + .iter() + .filter_map(move |segment| { + let res = segment.res?; + let generics_def_id = tcx.res_generics_def_id(res)?; + let generics = tcx.generics_of(generics_def_id); + if generics.has_impl_trait() { + return None; + } + let span = tcx.hir().span(segment.hir_id?); + let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); + Some(InsertableGenericArgs { + insert_span, + substs, + generics_def_id, + def_id: res.def_id(), + }) + }) + .chain(last_segment_using_path_data) + } + + fn path_inferred_subst_iter( + &self, + hir_id: HirId, + substs: SubstsRef<'tcx>, + qpath: &'tcx hir::QPath<'tcx>, + ) -> Box<dyn Iterator<Item = InsertableGenericArgs<'tcx>> + 'a> { + let tcx = self.infcx.tcx; + match qpath { + hir::QPath::Resolved(_self_ty, path) => { + Box::new(self.resolved_path_inferred_subst_iter(path, substs)) + } + hir::QPath::TypeRelative(ty, segment) => { + let Some(def_id) = self.typeck_results.type_dependent_def_id(hir_id) else { + return Box::new(iter::empty()); + }; + + let generics = tcx.generics_of(def_id); + let segment: Option<_> = try { + if !segment.infer_args || generics.has_impl_trait() { + None?; + } + let span = tcx.hir().span(segment.hir_id?); + let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); + InsertableGenericArgs { insert_span, substs, generics_def_id: def_id, def_id } + }; + + let parent_def_id = generics.parent.unwrap(); + if tcx.def_kind(parent_def_id) == DefKind::Impl { + let parent_ty = tcx.bound_type_of(parent_def_id).subst(tcx, substs); + match (parent_ty.kind(), &ty.kind) { + ( + ty::Adt(def, substs), + hir::TyKind::Path(hir::QPath::Resolved(_self_ty, path)), + ) => { + if tcx.res_generics_def_id(path.res) != Some(def.did()) { + match path.res { + Res::Def(DefKind::TyAlias, _) => { + // FIXME: Ideally we should support this. For that + // we have to map back from the self type to the + // type alias though. That's difficult. + // + // See the `need_type_info/type-alias.rs` test for + // some examples. + } + // There cannot be inference variables in the self type, + // so there's nothing for us to do here. + Res::SelfTy { .. } => {} + _ => warn!( + "unexpected path: def={:?} substs={:?} path={:?}", + def, substs, path, + ), + } + } else { + return Box::new( + self.resolved_path_inferred_subst_iter(path, substs) + .chain(segment), + ); + } + } + _ => (), + } + } + + Box::new(segment.into_iter()) + } + hir::QPath::LangItem(_, _, _) => Box::new(iter::empty()), + } + } +} + +impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.infcx.tcx.hir() + } + + fn visit_local(&mut self, local: &'tcx Local<'tcx>) { + intravisit::walk_local(self, local); + + if let Some(ty) = self.opt_node_type(local.hir_id) { + if self.generic_arg_contains_target(ty.into()) { + match local.source { + LocalSource::Normal if local.ty.is_none() => { + self.update_infer_source(InferSource { + span: local.pat.span, + kind: InferSourceKind::LetBinding { + insert_span: local.pat.span.shrink_to_hi(), + pattern_name: local.pat.simple_ident(), + ty, + }, + }) + } + _ => {} + } + } + } + } + + /// For closures, we first visit the parameters and then the content, + /// as we prefer those. + fn visit_body(&mut self, body: &'tcx Body<'tcx>) { + for param in body.params { + debug!( + "param: span {:?}, ty_span {:?}, pat.span {:?}", + param.span, param.ty_span, param.pat.span + ); + if param.ty_span != param.pat.span { + debug!("skipping param: has explicit type"); + continue; + } + + let Some(param_ty) = self.opt_node_type(param.hir_id) else { + continue + }; + + if self.generic_arg_contains_target(param_ty.into()) { + self.update_infer_source(InferSource { + span: param.pat.span, + kind: InferSourceKind::ClosureArg { + insert_span: param.pat.span.shrink_to_hi(), + ty: param_ty, + }, + }) + } + } + intravisit::walk_body(self, body); + } + + #[instrument(level = "debug", skip(self))] + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + let tcx = self.infcx.tcx; + match expr.kind { + // When encountering `func(arg)` first look into `arg` and then `func`, + // as `arg` is "more specific". + ExprKind::Call(func, args) => { + for arg in args { + self.visit_expr(arg); + } + self.visit_expr(func); + } + _ => intravisit::walk_expr(self, expr), + } + + for args in self.expr_inferred_subst_iter(expr) { + debug!(?args); + let InsertableGenericArgs { insert_span, substs, generics_def_id, def_id } = args; + let generics = tcx.generics_of(generics_def_id); + if let Some(argument_index) = generics + .own_substs(substs) + .iter() + .position(|&arg| self.generic_arg_contains_target(arg)) + { + let substs = self.infcx.resolve_vars_if_possible(substs); + let generic_args = &generics.own_substs_no_defaults(tcx, substs) + [generics.own_counts().lifetimes..]; + let span = match expr.kind { + ExprKind::MethodCall(path, _, _) => path.ident.span, + _ => expr.span, + }; + + self.update_infer_source(InferSource { + span, + kind: InferSourceKind::GenericArg { + insert_span, + argument_index, + generics_def_id, + def_id, + generic_args, + }, + }); + } + } + + if let Some(node_ty) = self.opt_node_type(expr.hir_id) { + if let ( + &ExprKind::Closure(&Closure { fn_decl, body, fn_decl_span, .. }), + ty::Closure(_, substs), + ) = (&expr.kind, node_ty.kind()) + { + let output = substs.as_closure().sig().output().skip_binder(); + if self.generic_arg_contains_target(output.into()) { + let body = self.infcx.tcx.hir().body(body); + let should_wrap_expr = if matches!(body.value.kind, ExprKind::Block(..)) { + None + } else { + Some(body.value.span.shrink_to_hi()) + }; + self.update_infer_source(InferSource { + span: fn_decl_span, + kind: InferSourceKind::ClosureReturn { + ty: output, + data: &fn_decl.output, + should_wrap_expr, + }, + }) + } + } + } + + let has_impl_trait = |def_id| { + iter::successors(Some(tcx.generics_of(def_id)), |generics| { + generics.parent.map(|def_id| tcx.generics_of(def_id)) + }) + .any(|generics| generics.has_impl_trait()) + }; + if let ExprKind::MethodCall(path, args, span) = expr.kind + && let Some(substs) = self.node_substs_opt(expr.hir_id) + && substs.iter().any(|arg| self.generic_arg_contains_target(arg)) + && let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) + && self.infcx.tcx.trait_of_item(def_id).is_some() + && !has_impl_trait(def_id) + { + let successor = + args.get(1).map_or_else(|| (")", span.hi()), |arg| (", ", arg.span.lo())); + let substs = self.infcx.resolve_vars_if_possible(substs); + self.update_infer_source(InferSource { + span: path.ident.span, + kind: InferSourceKind::FullyQualifiedMethodCall { + receiver: args.first().unwrap(), + successor, + substs, + def_id, + } + }) + } + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs new file mode 100644 index 000000000..9a2ab3e32 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -0,0 +1,234 @@ +//! Error Reporting for Anonymous Region Lifetime Errors +//! where both the regions are anonymous. + +use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; +use crate::infer::error_reporting::nice_region_error::util::AnonymousParamInfo; +use crate::infer::error_reporting::nice_region_error::NiceRegionError; +use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::SubregionOrigin; +use crate::infer::TyCtxt; + +use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed}; +use rustc_hir as hir; +use rustc_hir::{GenericParamKind, Ty}; +use rustc_middle::ty::Region; +use rustc_span::symbol::kw; + +impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { + /// Print the error message for lifetime errors when both the concerned regions are anonymous. + /// + /// Consider a case where we have + /// + /// ```compile_fail,E0623 + /// fn foo(x: &mut Vec<&u8>, y: &u8) { + /// x.push(y); + /// } + /// ``` + /// + /// The example gives + /// + /// ```text + /// fn foo(x: &mut Vec<&u8>, y: &u8) { + /// --- --- these references are declared with different lifetimes... + /// x.push(y); + /// ^ ...but data from `y` flows into `x` here + /// ``` + /// + /// It has been extended for the case of structs too. + /// + /// Consider the example + /// + /// ```no_run + /// struct Ref<'a> { x: &'a u32 } + /// ``` + /// + /// ```text + /// fn foo(mut x: Vec<Ref>, y: Ref) { + /// --- --- these structs are declared with different lifetimes... + /// x.push(y); + /// ^ ...but data from `y` flows into `x` here + /// } + /// ``` + /// + /// It will later be extended to trait objects. + pub(super) fn try_report_anon_anon_conflict(&self) -> Option<ErrorGuaranteed> { + let (span, sub, sup) = self.regions()?; + + if let Some(RegionResolutionError::ConcreteFailure( + SubregionOrigin::ReferenceOutlivesReferent(..), + .., + )) = self.error + { + // This error doesn't make much sense in this case. + return None; + } + + // Determine whether the sub and sup consist of both anonymous (elided) regions. + let anon_reg_sup = self.tcx().is_suitable_region(sup)?; + + let anon_reg_sub = self.tcx().is_suitable_region(sub)?; + let scope_def_id_sup = anon_reg_sup.def_id; + let bregion_sup = anon_reg_sup.boundregion; + let scope_def_id_sub = anon_reg_sub.def_id; + let bregion_sub = anon_reg_sub.boundregion; + + let ty_sup = find_anon_type(self.tcx(), sup, &bregion_sup)?; + + let ty_sub = find_anon_type(self.tcx(), sub, &bregion_sub)?; + + debug!( + "try_report_anon_anon_conflict: found_param1={:?} sup={:?} br1={:?}", + ty_sub, sup, bregion_sup + ); + debug!( + "try_report_anon_anon_conflict: found_param2={:?} sub={:?} br2={:?}", + ty_sup, sub, bregion_sub + ); + + let (ty_sup, ty_fndecl_sup) = ty_sup; + let (ty_sub, ty_fndecl_sub) = ty_sub; + + let AnonymousParamInfo { param: anon_param_sup, .. } = + self.find_param_with_region(sup, sup)?; + let AnonymousParamInfo { param: anon_param_sub, .. } = + self.find_param_with_region(sub, sub)?; + + let sup_is_ret_type = + self.is_return_type_anon(scope_def_id_sup, bregion_sup, ty_fndecl_sup); + let sub_is_ret_type = + self.is_return_type_anon(scope_def_id_sub, bregion_sub, ty_fndecl_sub); + + let span_label_var1 = match anon_param_sup.pat.simple_ident() { + Some(simple_ident) => format!(" from `{}`", simple_ident), + None => String::new(), + }; + + let span_label_var2 = match anon_param_sub.pat.simple_ident() { + Some(simple_ident) => format!(" into `{}`", simple_ident), + None => String::new(), + }; + + debug!( + "try_report_anon_anon_conflict: sub_is_ret_type={:?} sup_is_ret_type={:?}", + sub_is_ret_type, sup_is_ret_type + ); + + let mut err = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch"); + + match (sup_is_ret_type, sub_is_ret_type) { + (ret_capture @ Some(ret_span), _) | (_, ret_capture @ Some(ret_span)) => { + let param_span = + if sup_is_ret_type == ret_capture { ty_sub.span } else { ty_sup.span }; + + err.span_label( + param_span, + "this parameter and the return type are declared with different lifetimes...", + ); + err.span_label(ret_span, ""); + err.span_label(span, format!("...but data{} is returned here", span_label_var1)); + } + + (None, None) => { + if ty_sup.hir_id == ty_sub.hir_id { + err.span_label(ty_sup.span, "this type is declared with multiple lifetimes..."); + err.span_label(ty_sub.span, ""); + err.span_label(span, "...but data with one lifetime flows into the other here"); + } else { + err.span_label( + ty_sup.span, + "these two types are declared with different lifetimes...", + ); + err.span_label(ty_sub.span, ""); + err.span_label( + span, + format!("...but data{} flows{} here", span_label_var1, span_label_var2), + ); + } + } + } + + if suggest_adding_lifetime_params(self.tcx(), sub, ty_sup, ty_sub, &mut err) { + err.note("each elided lifetime in input position becomes a distinct lifetime"); + } + + let reported = err.emit(); + Some(reported) + } +} + +pub fn suggest_adding_lifetime_params<'tcx>( + tcx: TyCtxt<'tcx>, + sub: Region<'tcx>, + ty_sup: &Ty<'_>, + ty_sub: &Ty<'_>, + err: &mut Diagnostic, +) -> bool { + let ( + hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. }, + hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. }, + ) = (ty_sub, ty_sup) else { + return false; + }; + + if !lifetime_sub.name.is_anonymous() || !lifetime_sup.name.is_anonymous() { + return false; + }; + + let Some(anon_reg) = tcx.is_suitable_region(sub) else { + return false; + }; + + let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id); + + let node = tcx.hir().get(hir_id); + let is_impl = matches!(&node, hir::Node::ImplItem(_)); + let generics = match node { + hir::Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, ref generics, ..), .. }) + | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. }) + | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics, + _ => return false, + }; + + let suggestion_param_name = generics + .params + .iter() + .filter(|p| matches!(p.kind, GenericParamKind::Lifetime { .. })) + .map(|p| p.name.ident().name) + .find(|i| *i != kw::UnderscoreLifetime); + let introduce_new = suggestion_param_name.is_none(); + let suggestion_param_name = + suggestion_param_name.map(|n| n.to_string()).unwrap_or_else(|| "'a".to_owned()); + + debug!(?lifetime_sup.span); + debug!(?lifetime_sub.span); + let make_suggestion = |span: rustc_span::Span| { + if span.is_empty() { + (span, format!("{}, ", suggestion_param_name)) + } else if let Ok("&") = tcx.sess.source_map().span_to_snippet(span).as_deref() { + (span.shrink_to_hi(), format!("{} ", suggestion_param_name)) + } else { + (span, suggestion_param_name.clone()) + } + }; + let mut suggestions = + vec![make_suggestion(lifetime_sub.span), make_suggestion(lifetime_sup.span)]; + + if introduce_new { + let new_param_suggestion = + if let Some(first) = generics.params.iter().find(|p| !p.name.ident().span.is_empty()) { + (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name)) + } else { + (generics.span, format!("<{}>", suggestion_param_name)) + }; + + suggestions.push(new_param_suggestion); + } + + let mut sugg = String::from("consider introducing a named lifetime parameter"); + if is_impl { + sugg.push_str(" and update trait if needed"); + } + err.multipart_suggestion(sugg, suggestions, Applicability::MaybeIncorrect); + + true +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs new file mode 100644 index 000000000..c1b201da6 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -0,0 +1,234 @@ +use rustc_hir as hir; +use rustc_hir::intravisit::{self, Visitor}; +use rustc_middle::hir::map::Map; +use rustc_middle::hir::nested_filter; +use rustc_middle::middle::resolve_lifetime as rl; +use rustc_middle::ty::{self, Region, TyCtxt}; + +/// This function calls the `visit_ty` method for the parameters +/// corresponding to the anonymous regions. The `nested_visitor.found_type` +/// contains the anonymous type. +/// +/// # Arguments +/// region - the anonymous region corresponding to the anon_anon conflict +/// br - the bound region corresponding to the above region which is of type `BrAnon(_)` +/// +/// # Example +/// ```compile_fail,E0623 +/// fn foo(x: &mut Vec<&u8>, y: &u8) +/// { x.push(y); } +/// ``` +/// The function returns the nested type corresponding to the anonymous region +/// for e.g., `&u8` and `Vec<&u8>`. +pub fn find_anon_type<'tcx>( + tcx: TyCtxt<'tcx>, + region: Region<'tcx>, + br: &ty::BoundRegionKind, +) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnSig<'tcx>)> { + let anon_reg = tcx.is_suitable_region(region)?; + let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id); + let fn_sig = tcx.hir().get(hir_id).fn_sig()?; + + fn_sig + .decl + .inputs + .iter() + .find_map(|arg| find_component_for_bound_region(tcx, arg, br)) + .map(|ty| (ty, fn_sig)) +} + +// This method creates a FindNestedTypeVisitor which returns the type corresponding +// to the anonymous region. +fn find_component_for_bound_region<'tcx>( + tcx: TyCtxt<'tcx>, + arg: &'tcx hir::Ty<'tcx>, + br: &ty::BoundRegionKind, +) -> Option<&'tcx hir::Ty<'tcx>> { + let mut nested_visitor = FindNestedTypeVisitor { + tcx, + bound_region: *br, + found_type: None, + current_index: ty::INNERMOST, + }; + nested_visitor.visit_ty(arg); + nested_visitor.found_type +} + +// The FindNestedTypeVisitor captures the corresponding `hir::Ty` of the +// anonymous region. The example above would lead to a conflict between +// the two anonymous lifetimes for &u8 in x and y respectively. This visitor +// would be invoked twice, once for each lifetime, and would +// walk the types like &mut Vec<&u8> and &u8 looking for the HIR +// where that lifetime appears. This allows us to highlight the +// specific part of the type in the error message. +struct FindNestedTypeVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + // The bound_region corresponding to the Refree(freeregion) + // associated with the anonymous region we are looking for. + bound_region: ty::BoundRegionKind, + // The type where the anonymous lifetime appears + // for e.g., Vec<`&u8`> and <`&u8`> + found_type: Option<&'tcx hir::Ty<'tcx>>, + current_index: ty::DebruijnIndex, +} + +impl<'tcx> Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { + match arg.kind { + hir::TyKind::BareFn(_) => { + self.current_index.shift_in(1); + intravisit::walk_ty(self, arg); + self.current_index.shift_out(1); + return; + } + + hir::TyKind::TraitObject(bounds, ..) => { + for bound in bounds { + self.current_index.shift_in(1); + self.visit_poly_trait_ref(bound, hir::TraitBoundModifier::None); + self.current_index.shift_out(1); + } + } + + hir::TyKind::Rptr(ref lifetime, _) => { + // the lifetime of the TyRptr + let hir_id = lifetime.hir_id; + match (self.tcx.named_region(hir_id), self.bound_region) { + // Find the index of the named region that was part of the + // error. We will then search the function parameters for a bound + // region at the right depth with the same index + (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { + debug!("EarlyBound id={:?} def_id={:?}", id, def_id); + if id == def_id { + self.found_type = Some(arg); + return; // we can stop visiting now + } + } + + // Find the index of the named region that was part of the + // error. We will then search the function parameters for a bound + // region at the right depth with the same index + ( + Some(rl::Region::LateBound(debruijn_index, _, id)), + ty::BrNamed(def_id, _), + ) => { + debug!( + "FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", + debruijn_index + ); + debug!("LateBound id={:?} def_id={:?}", id, def_id); + if debruijn_index == self.current_index && id == def_id { + self.found_type = Some(arg); + return; // we can stop visiting now + } + } + + ( + Some( + rl::Region::Static + | rl::Region::Free(_, _) + | rl::Region::EarlyBound(_, _) + | rl::Region::LateBound(_, _, _), + ) + | None, + _, + ) => { + debug!("no arg found"); + } + } + } + // Checks if it is of type `hir::TyKind::Path` which corresponds to a struct. + hir::TyKind::Path(_) => { + let subvisitor = &mut TyPathVisitor { + tcx: self.tcx, + found_it: false, + bound_region: self.bound_region, + current_index: self.current_index, + }; + intravisit::walk_ty(subvisitor, arg); // call walk_ty; as visit_ty is empty, + // this will visit only outermost type + if subvisitor.found_it { + self.found_type = Some(arg); + } + } + _ => {} + } + // walk the embedded contents: e.g., if we are visiting `Vec<&Foo>`, + // go on to visit `&Foo` + intravisit::walk_ty(self, arg); + } +} + +// The visitor captures the corresponding `hir::Ty` of the anonymous region +// in the case of structs ie. `hir::TyKind::Path`. +// This visitor would be invoked for each lifetime corresponding to a struct, +// and would walk the types like Vec<Ref> in the above example and Ref looking for the HIR +// where that lifetime appears. This allows us to highlight the +// specific part of the type in the error message. +struct TyPathVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + found_it: bool, + bound_region: ty::BoundRegionKind, + current_index: ty::DebruijnIndex, +} + +impl<'tcx> Visitor<'tcx> for TyPathVisitor<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Map<'tcx> { + self.tcx.hir() + } + + fn visit_lifetime(&mut self, lifetime: &hir::Lifetime) { + match (self.tcx.named_region(lifetime.hir_id), self.bound_region) { + // the lifetime of the TyPath! + (Some(rl::Region::EarlyBound(_, id)), ty::BrNamed(def_id, _)) => { + debug!("EarlyBound id={:?} def_id={:?}", id, def_id); + if id == def_id { + self.found_it = true; + return; // we can stop visiting now + } + } + + (Some(rl::Region::LateBound(debruijn_index, _, id)), ty::BrNamed(def_id, _)) => { + debug!("FindNestedTypeVisitor::visit_ty: LateBound depth = {:?}", debruijn_index,); + debug!("id={:?}", id); + debug!("def_id={:?}", def_id); + if debruijn_index == self.current_index && id == def_id { + self.found_it = true; + return; // we can stop visiting now + } + } + + ( + Some( + rl::Region::Static + | rl::Region::EarlyBound(_, _) + | rl::Region::LateBound(_, _, _) + | rl::Region::Free(_, _), + ) + | None, + _, + ) => { + debug!("no arg found"); + } + } + } + + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { + // ignore nested types + // + // If you have a type like `Foo<'a, &Ty>` we + // are only interested in the immediate lifetimes ('a). + // + // Making `visit_ty` empty will ignore the `&Ty` embedded + // inside, it will get reached by the outer visitor. + debug!("`Ty` corresponding to a struct is {:?}", arg); + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs new file mode 100644 index 000000000..893ca3cf7 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -0,0 +1,102 @@ +//! Error Reporting for when the lifetime for a type doesn't match the `impl` selected for a predicate +//! to hold. + +use crate::infer::error_reporting::nice_region_error::NiceRegionError; +use crate::infer::error_reporting::note_and_explain_region; +use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::{SubregionOrigin, TypeTrace}; +use crate::traits::ObligationCauseCode; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan}; +use rustc_hir as hir; +use rustc_hir::intravisit::Visitor; +use rustc_middle::ty::TypeVisitor; + +impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { + pub(super) fn try_report_mismatched_static_lifetime(&self) -> Option<ErrorGuaranteed> { + let error = self.error.as_ref()?; + debug!("try_report_mismatched_static_lifetime {:?}", error); + + let RegionResolutionError::ConcreteFailure(origin, sub, sup) = error.clone() else { + return None; + }; + if !sub.is_static() { + return None; + } + let SubregionOrigin::Subtype(box TypeTrace { ref cause, .. }) = origin else { + return None; + }; + // If we added a "points at argument expression" obligation, we remove it here, we care + // about the original obligation only. + let code = match cause.code() { + ObligationCauseCode::FunctionArgumentObligation { parent_code, .. } => &*parent_code, + code => code, + }; + let ObligationCauseCode::MatchImpl(parent, impl_def_id) = code else { + return None; + }; + let ObligationCauseCode::BindingObligation(_def_id, binding_span) = *parent.code() else { + return None; + }; + let mut err = self.tcx().sess.struct_span_err(cause.span, "incompatible lifetime on type"); + // FIXME: we should point at the lifetime + let mut multi_span: MultiSpan = vec![binding_span].into(); + multi_span.push_span_label(binding_span, "introduces a `'static` lifetime requirement"); + err.span_note(multi_span, "because this has an unmet lifetime requirement"); + note_and_explain_region(self.tcx(), &mut err, "", sup, "...", Some(binding_span)); + if let Some(impl_node) = self.tcx().hir().get_if_local(*impl_def_id) { + // If an impl is local, then maybe this isn't what they want. Try to + // be as helpful as possible with implicit lifetimes. + + // First, let's get the hir self type of the impl + let hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { self_ty: impl_self_ty, .. }), + .. + }) = impl_node else { + bug!("Node not an impl."); + }; + + // Next, let's figure out the set of trait objects with implicit static bounds + let ty = self.tcx().type_of(*impl_def_id); + let mut v = super::static_impl_trait::TraitObjectVisitor(FxHashSet::default()); + v.visit_ty(ty); + let mut traits = vec![]; + for matching_def_id in v.0 { + let mut hir_v = + super::static_impl_trait::HirTraitObjectVisitor(&mut traits, matching_def_id); + hir_v.visit_ty(&impl_self_ty); + } + + if traits.is_empty() { + // If there are no trait object traits to point at, either because + // there aren't trait objects or because none are implicit, then just + // write a single note on the impl itself. + + let impl_span = self.tcx().def_span(*impl_def_id); + err.span_note(impl_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); + } else { + // Otherwise, point at all implicit static lifetimes + + err.note("...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); + for span in &traits { + err.span_note(*span, "this has an implicit `'static` lifetime requirement"); + // It would be nice to put this immediately under the above note, but they get + // pushed to the end. + err.span_suggestion_verbose( + span.shrink_to_hi(), + "consider relaxing the implicit `'static` requirement", + " + '_", + Applicability::MaybeIncorrect, + ); + } + } + } else { + // Otherwise just point out the impl. + + let impl_span = self.tcx().def_span(*impl_def_id); + err.span_note(impl_span, "...does not necessarily outlive the static lifetime introduced by the compatible `impl`"); + } + let reported = err.emit(); + Some(reported) + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs new file mode 100644 index 000000000..53d9acf7d --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs @@ -0,0 +1,77 @@ +use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::lexical_region_resolve::RegionResolutionError::*; +use crate::infer::InferCtxt; +use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; +use rustc_middle::ty::{self, TyCtxt}; +use rustc_span::source_map::Span; + +mod different_lifetimes; +pub mod find_anon_type; +mod mismatched_static_lifetime; +mod named_anon_conflict; +mod placeholder_error; +mod static_impl_trait; +mod trait_impl_difference; +mod util; + +pub use different_lifetimes::suggest_adding_lifetime_params; +pub use find_anon_type::find_anon_type; +pub use static_impl_trait::{suggest_new_region_bound, HirTraitObjectVisitor, TraitObjectVisitor}; +pub use util::find_param_with_region; + +impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { + pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool { + NiceRegionError::new(self, error.clone()).try_report().is_some() + } +} + +pub struct NiceRegionError<'cx, 'tcx> { + infcx: &'cx InferCtxt<'cx, 'tcx>, + error: Option<RegionResolutionError<'tcx>>, + regions: Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)>, +} + +impl<'cx, 'tcx> NiceRegionError<'cx, 'tcx> { + pub fn new(infcx: &'cx InferCtxt<'cx, 'tcx>, error: RegionResolutionError<'tcx>) -> Self { + Self { infcx, error: Some(error), regions: None } + } + + pub fn new_from_span( + infcx: &'cx InferCtxt<'cx, 'tcx>, + span: Span, + sub: ty::Region<'tcx>, + sup: ty::Region<'tcx>, + ) -> Self { + Self { infcx, error: None, regions: Some((span, sub, sup)) } + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + pub fn try_report_from_nll(&self) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { + // Due to the improved diagnostics returned by the MIR borrow checker, only a subset of + // the nice region errors are required when running under the MIR borrow checker. + self.try_report_named_anon_conflict().or_else(|| self.try_report_placeholder_conflict()) + } + + pub fn try_report(&self) -> Option<ErrorGuaranteed> { + self.try_report_from_nll() + .map(|mut diag| diag.emit()) + .or_else(|| self.try_report_impl_not_conforming_to_trait()) + .or_else(|| self.try_report_anon_anon_conflict()) + .or_else(|| self.try_report_static_impl_trait()) + .or_else(|| self.try_report_mismatched_static_lifetime()) + } + + pub(super) fn regions(&self) -> Option<(Span, ty::Region<'tcx>, ty::Region<'tcx>)> { + match (&self.error, self.regions) { + (Some(ConcreteFailure(origin, sub, sup)), None) => Some((origin.span(), *sub, *sup)), + (Some(SubSupConflict(_, _, origin, sub, _, sup, _)), None) => { + Some((origin.span(), *sub, *sup)) + } + (None, Some((span, sub, sup))) => Some((span, sub, sup)), + _ => None, + } + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs new file mode 100644 index 000000000..76cb76d9f --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -0,0 +1,116 @@ +//! Error Reporting for Anonymous Region Lifetime Errors +//! where one region is named and the other is anonymous. +use crate::infer::error_reporting::nice_region_error::find_anon_type::find_anon_type; +use crate::infer::error_reporting::nice_region_error::NiceRegionError; +use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_middle::ty; +use rustc_span::symbol::kw; + +impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { + /// When given a `ConcreteFailure` for a function with parameters containing a named region and + /// an anonymous region, emit an descriptive diagnostic error. + pub(super) fn try_report_named_anon_conflict( + &self, + ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { + let (span, sub, sup) = self.regions()?; + + debug!( + "try_report_named_anon_conflict(sub={:?}, sup={:?}, error={:?})", + sub, sup, self.error, + ); + + // Determine whether the sub and sup consist of one named region ('a) + // and one anonymous (elided) region. If so, find the parameter arg + // where the anonymous region appears (there must always be one; we + // only introduced anonymous regions in parameters) as well as a + // version new_ty of its type where the anonymous region is replaced + // with the named one. + let (named, anon, anon_param_info, region_info) = if sub.has_name() + && self.tcx().is_suitable_region(sup).is_some() + && self.find_param_with_region(sup, sub).is_some() + { + ( + sub, + sup, + self.find_param_with_region(sup, sub).unwrap(), + self.tcx().is_suitable_region(sup).unwrap(), + ) + } else if sup.has_name() + && self.tcx().is_suitable_region(sub).is_some() + && self.find_param_with_region(sub, sup).is_some() + { + ( + sup, + sub, + self.find_param_with_region(sub, sup).unwrap(), + self.tcx().is_suitable_region(sub).unwrap(), + ) + } else { + return None; // inapplicable + }; + + // Suggesting to add a `'static` lifetime to a parameter is nearly always incorrect, + // and can steer users down the wrong path. + if named.is_static() { + return None; + } + + debug!("try_report_named_anon_conflict: named = {:?}", named); + debug!("try_report_named_anon_conflict: anon_param_info = {:?}", anon_param_info); + debug!("try_report_named_anon_conflict: region_info = {:?}", region_info); + + let param = anon_param_info.param; + let new_ty = anon_param_info.param_ty; + let new_ty_span = anon_param_info.param_ty_span; + let br = anon_param_info.bound_region; + let is_first = anon_param_info.is_first; + let scope_def_id = region_info.def_id; + let is_impl_item = region_info.is_impl_item; + + match br { + ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(_) => {} + _ => { + /* not an anonymous region */ + debug!("try_report_named_anon_conflict: not an anonymous region"); + return None; + } + } + + if is_impl_item { + debug!("try_report_named_anon_conflict: impl item, bail out"); + return None; + } + + if find_anon_type(self.tcx(), anon, &br).is_some() + && self.is_self_anon(is_first, scope_def_id) + { + return None; + } + + let (error_var, span_label_var) = match param.pat.simple_ident() { + Some(simple_ident) => ( + format!("the type of `{}`", simple_ident), + format!("the type of `{}`", simple_ident), + ), + None => ("parameter type".to_owned(), "type".to_owned()), + }; + + let mut diag = struct_span_err!( + self.tcx().sess, + span, + E0621, + "explicit lifetime required in {}", + error_var + ); + + diag.span_label(span, format!("lifetime `{}` required", named)); + diag.span_suggestion( + new_ty_span, + &format!("add explicit lifetime `{}` to {}", named, span_label_var), + new_ty, + Applicability::Unspecified, + ); + + Some(diag) + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs new file mode 100644 index 000000000..998699158 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -0,0 +1,501 @@ +use crate::infer::error_reporting::nice_region_error::NiceRegionError; +use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::ValuePairs; +use crate::infer::{SubregionOrigin, TypeTrace}; +use crate::traits::{ObligationCause, ObligationCauseCode}; +use rustc_data_structures::intern::Interned; +use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_hir::def::Namespace; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::print::{FmtPrinter, Print, RegionHighlightMode}; +use rustc_middle::ty::subst::SubstsRef; +use rustc_middle::ty::{self, RePlaceholder, ReVar, Region, TyCtxt}; + +use std::fmt::{self, Write}; + +impl<'tcx> NiceRegionError<'_, 'tcx> { + /// When given a `ConcreteFailure` for a function with arguments containing a named region and + /// an anonymous region, emit a descriptive diagnostic error. + pub(super) fn try_report_placeholder_conflict( + &self, + ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { + match &self.error { + /////////////////////////////////////////////////////////////////////////// + // NB. The ordering of cases in this match is very + // sensitive, because we are often matching against + // specific cases and then using an `_` to match all + // others. + + /////////////////////////////////////////////////////////////////////////// + // Check for errors from comparing trait failures -- first + // with two placeholders, then with one. + Some(RegionResolutionError::SubSupConflict( + vid, + _, + SubregionOrigin::Subtype(box TypeTrace { cause, values }), + sub_placeholder @ Region(Interned(RePlaceholder(_), _)), + _, + sup_placeholder @ Region(Interned(RePlaceholder(_), _)), + _, + )) => self.try_report_trait_placeholder_mismatch( + Some(self.tcx().mk_region(ReVar(*vid))), + cause, + Some(*sub_placeholder), + Some(*sup_placeholder), + values, + ), + + Some(RegionResolutionError::SubSupConflict( + vid, + _, + SubregionOrigin::Subtype(box TypeTrace { cause, values }), + sub_placeholder @ Region(Interned(RePlaceholder(_), _)), + _, + _, + _, + )) => self.try_report_trait_placeholder_mismatch( + Some(self.tcx().mk_region(ReVar(*vid))), + cause, + Some(*sub_placeholder), + None, + values, + ), + + Some(RegionResolutionError::SubSupConflict( + vid, + _, + SubregionOrigin::Subtype(box TypeTrace { cause, values }), + _, + _, + sup_placeholder @ Region(Interned(RePlaceholder(_), _)), + _, + )) => self.try_report_trait_placeholder_mismatch( + Some(self.tcx().mk_region(ReVar(*vid))), + cause, + None, + Some(*sup_placeholder), + values, + ), + + Some(RegionResolutionError::SubSupConflict( + vid, + _, + _, + _, + SubregionOrigin::Subtype(box TypeTrace { cause, values }), + sup_placeholder @ Region(Interned(RePlaceholder(_), _)), + _, + )) => self.try_report_trait_placeholder_mismatch( + Some(self.tcx().mk_region(ReVar(*vid))), + cause, + None, + Some(*sup_placeholder), + values, + ), + + Some(RegionResolutionError::UpperBoundUniverseConflict( + vid, + _, + _, + SubregionOrigin::Subtype(box TypeTrace { cause, values }), + sup_placeholder @ Region(Interned(RePlaceholder(_), _)), + )) => self.try_report_trait_placeholder_mismatch( + Some(self.tcx().mk_region(ReVar(*vid))), + cause, + None, + Some(*sup_placeholder), + values, + ), + + Some(RegionResolutionError::ConcreteFailure( + SubregionOrigin::Subtype(box TypeTrace { cause, values }), + sub_region @ Region(Interned(RePlaceholder(_), _)), + sup_region @ Region(Interned(RePlaceholder(_), _)), + )) => self.try_report_trait_placeholder_mismatch( + None, + cause, + Some(*sub_region), + Some(*sup_region), + values, + ), + + Some(RegionResolutionError::ConcreteFailure( + SubregionOrigin::Subtype(box TypeTrace { cause, values }), + sub_region @ Region(Interned(RePlaceholder(_), _)), + sup_region, + )) => self.try_report_trait_placeholder_mismatch( + (!sup_region.has_name()).then_some(*sup_region), + cause, + Some(*sub_region), + None, + values, + ), + + Some(RegionResolutionError::ConcreteFailure( + SubregionOrigin::Subtype(box TypeTrace { cause, values }), + sub_region, + sup_region @ Region(Interned(RePlaceholder(_), _)), + )) => self.try_report_trait_placeholder_mismatch( + (!sub_region.has_name()).then_some(*sub_region), + cause, + None, + Some(*sup_region), + values, + ), + + _ => None, + } + } + + fn try_report_trait_placeholder_mismatch( + &self, + vid: Option<Region<'tcx>>, + cause: &ObligationCause<'tcx>, + sub_placeholder: Option<Region<'tcx>>, + sup_placeholder: Option<Region<'tcx>>, + value_pairs: &ValuePairs<'tcx>, + ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { + let (expected_substs, found_substs, trait_def_id) = match value_pairs { + ValuePairs::TraitRefs(ExpectedFound { expected, found }) + if expected.def_id == found.def_id => + { + (expected.substs, found.substs, expected.def_id) + } + ValuePairs::PolyTraitRefs(ExpectedFound { expected, found }) + if expected.def_id() == found.def_id() => + { + // It's possible that the placeholders come from a binder + // outside of this value pair. Use `no_bound_vars` as a + // simple heuristic for that. + (expected.no_bound_vars()?.substs, found.no_bound_vars()?.substs, expected.def_id()) + } + _ => return None, + }; + + Some(self.report_trait_placeholder_mismatch( + vid, + cause, + sub_placeholder, + sup_placeholder, + trait_def_id, + expected_substs, + found_substs, + )) + } + + // error[E0308]: implementation of `Foo` does not apply to enough lifetimes + // --> /home/nmatsakis/tmp/foo.rs:12:5 + // | + // 12 | all::<&'static u32>(); + // | ^^^^^^^^^^^^^^^^^^^ lifetime mismatch + // | + // = note: Due to a where-clause on the function `all`, + // = note: `T` must implement `...` for any two lifetimes `'1` and `'2`. + // = note: However, the type `T` only implements `...` for some specific lifetime `'2`. + #[instrument(level = "debug", skip(self))] + fn report_trait_placeholder_mismatch( + &self, + vid: Option<Region<'tcx>>, + cause: &ObligationCause<'tcx>, + sub_placeholder: Option<Region<'tcx>>, + sup_placeholder: Option<Region<'tcx>>, + trait_def_id: DefId, + expected_substs: SubstsRef<'tcx>, + actual_substs: SubstsRef<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let span = cause.span(); + let msg = format!( + "implementation of `{}` is not general enough", + self.tcx().def_path_str(trait_def_id), + ); + let mut err = self.tcx().sess.struct_span_err(span, &msg); + + let leading_ellipsis = if let ObligationCauseCode::ItemObligation(def_id) = *cause.code() { + err.span_label(span, "doesn't satisfy where-clause"); + err.span_label( + self.tcx().def_span(def_id), + &format!("due to a where-clause on `{}`...", self.tcx().def_path_str(def_id)), + ); + true + } else { + err.span_label(span, &msg); + false + }; + + let expected_trait_ref = self.infcx.resolve_vars_if_possible(ty::TraitRef { + def_id: trait_def_id, + substs: expected_substs, + }); + let actual_trait_ref = self + .infcx + .resolve_vars_if_possible(ty::TraitRef { def_id: trait_def_id, substs: actual_substs }); + + // Search the expected and actual trait references to see (a) + // whether the sub/sup placeholders appear in them (sometimes + // you have a trait ref like `T: Foo<fn(&u8)>`, where the + // placeholder was created as part of an inner type) and (b) + // whether the inference variable appears. In each case, + // assign a counter value in each case if so. + let mut counter = 0; + let mut has_sub = None; + let mut has_sup = None; + + let mut actual_has_vid = None; + let mut expected_has_vid = None; + + self.tcx().for_each_free_region(&expected_trait_ref, |r| { + if Some(r) == sub_placeholder && has_sub.is_none() { + has_sub = Some(counter); + counter += 1; + } else if Some(r) == sup_placeholder && has_sup.is_none() { + has_sup = Some(counter); + counter += 1; + } + + if Some(r) == vid && expected_has_vid.is_none() { + expected_has_vid = Some(counter); + counter += 1; + } + }); + + self.tcx().for_each_free_region(&actual_trait_ref, |r| { + if Some(r) == vid && actual_has_vid.is_none() { + actual_has_vid = Some(counter); + counter += 1; + } + }); + + let actual_self_ty_has_vid = + self.tcx().any_free_region_meets(&actual_trait_ref.self_ty(), |r| Some(r) == vid); + + let expected_self_ty_has_vid = + self.tcx().any_free_region_meets(&expected_trait_ref.self_ty(), |r| Some(r) == vid); + + let any_self_ty_has_vid = actual_self_ty_has_vid || expected_self_ty_has_vid; + + debug!( + ?actual_has_vid, + ?expected_has_vid, + ?has_sub, + ?has_sup, + ?actual_self_ty_has_vid, + ?expected_self_ty_has_vid, + ); + + self.explain_actual_impl_that_was_found( + &mut err, + sub_placeholder, + sup_placeholder, + has_sub, + has_sup, + expected_trait_ref, + actual_trait_ref, + vid, + expected_has_vid, + actual_has_vid, + any_self_ty_has_vid, + leading_ellipsis, + ); + + err + } + + /// Add notes with details about the expected and actual trait refs, with attention to cases + /// when placeholder regions are involved: either the trait or the self type containing + /// them needs to be mentioned the closest to the placeholders. + /// This makes the error messages read better, however at the cost of some complexity + /// due to the number of combinations we have to deal with. + fn explain_actual_impl_that_was_found( + &self, + err: &mut Diagnostic, + sub_placeholder: Option<Region<'tcx>>, + sup_placeholder: Option<Region<'tcx>>, + has_sub: Option<usize>, + has_sup: Option<usize>, + expected_trait_ref: ty::TraitRef<'tcx>, + actual_trait_ref: ty::TraitRef<'tcx>, + vid: Option<Region<'tcx>>, + expected_has_vid: Option<usize>, + actual_has_vid: Option<usize>, + any_self_ty_has_vid: bool, + leading_ellipsis: bool, + ) { + // HACK(eddyb) maybe move this in a more central location. + #[derive(Copy, Clone)] + struct Highlighted<'tcx, T> { + tcx: TyCtxt<'tcx>, + highlight: RegionHighlightMode<'tcx>, + value: T, + } + + impl<'tcx, T> Highlighted<'tcx, T> { + fn map<U>(self, f: impl FnOnce(T) -> U) -> Highlighted<'tcx, U> { + Highlighted { tcx: self.tcx, highlight: self.highlight, value: f(self.value) } + } + } + + impl<'tcx, T> fmt::Display for Highlighted<'tcx, T> + where + T: for<'a> Print< + 'tcx, + FmtPrinter<'a, 'tcx>, + Error = fmt::Error, + Output = FmtPrinter<'a, 'tcx>, + >, + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS); + printer.region_highlight_mode = self.highlight; + + let s = self.value.print(printer)?.into_buffer(); + f.write_str(&s) + } + } + + // The weird thing here with the `maybe_highlighting_region` calls and the + // the match inside is meant to be like this: + // + // - The match checks whether the given things (placeholders, etc) appear + // in the types are about to print + // - Meanwhile, the `maybe_highlighting_region` calls set up + // highlights so that, if they do appear, we will replace + // them `'0` and whatever. (This replacement takes place + // inside the closure given to `maybe_highlighting_region`.) + // + // There is some duplication between the calls -- i.e., the + // `maybe_highlighting_region` checks if (e.g.) `has_sub` is + // None, an then we check again inside the closure, but this + // setup sort of minimized the number of calls and so form. + + let highlight_trait_ref = |trait_ref| Highlighted { + tcx: self.tcx(), + highlight: RegionHighlightMode::new(self.tcx()), + value: trait_ref, + }; + + let same_self_type = actual_trait_ref.self_ty() == expected_trait_ref.self_ty(); + + let mut expected_trait_ref = highlight_trait_ref(expected_trait_ref); + expected_trait_ref.highlight.maybe_highlighting_region(sub_placeholder, has_sub); + expected_trait_ref.highlight.maybe_highlighting_region(sup_placeholder, has_sup); + err.note(&{ + let passive_voice = match (has_sub, has_sup) { + (Some(_), _) | (_, Some(_)) => any_self_ty_has_vid, + (None, None) => { + expected_trait_ref.highlight.maybe_highlighting_region(vid, expected_has_vid); + match expected_has_vid { + Some(_) => true, + None => any_self_ty_has_vid, + } + } + }; + + let mut note = if same_self_type { + let mut self_ty = expected_trait_ref.map(|tr| tr.self_ty()); + self_ty.highlight.maybe_highlighting_region(vid, actual_has_vid); + + if self_ty.value.is_closure() + && self + .tcx() + .fn_trait_kind_from_lang_item(expected_trait_ref.value.def_id) + .is_some() + { + let closure_sig = self_ty.map(|closure| { + if let ty::Closure(_, substs) = closure.kind() { + self.tcx().signature_unclosure( + substs.as_closure().sig(), + rustc_hir::Unsafety::Normal, + ) + } else { + bug!("type is not longer closure"); + } + }); + + format!( + "{}closure with signature `{}` must implement `{}`", + if leading_ellipsis { "..." } else { "" }, + closure_sig, + expected_trait_ref.map(|tr| tr.print_only_trait_path()), + ) + } else { + format!( + "{}`{}` must implement `{}`", + if leading_ellipsis { "..." } else { "" }, + self_ty, + expected_trait_ref.map(|tr| tr.print_only_trait_path()), + ) + } + } else if passive_voice { + format!( + "{}`{}` would have to be implemented for the type `{}`", + if leading_ellipsis { "..." } else { "" }, + expected_trait_ref.map(|tr| tr.print_only_trait_path()), + expected_trait_ref.map(|tr| tr.self_ty()), + ) + } else { + format!( + "{}`{}` must implement `{}`", + if leading_ellipsis { "..." } else { "" }, + expected_trait_ref.map(|tr| tr.self_ty()), + expected_trait_ref.map(|tr| tr.print_only_trait_path()), + ) + }; + + match (has_sub, has_sup) { + (Some(n1), Some(n2)) => { + let _ = write!( + note, + ", for any two lifetimes `'{}` and `'{}`...", + std::cmp::min(n1, n2), + std::cmp::max(n1, n2), + ); + } + (Some(n), _) | (_, Some(n)) => { + let _ = write!(note, ", for any lifetime `'{}`...", n,); + } + (None, None) => { + if let Some(n) = expected_has_vid { + let _ = write!(note, ", for some specific lifetime `'{}`...", n,); + } + } + } + + note + }); + + let mut actual_trait_ref = highlight_trait_ref(actual_trait_ref); + actual_trait_ref.highlight.maybe_highlighting_region(vid, actual_has_vid); + err.note(&{ + let passive_voice = match actual_has_vid { + Some(_) => any_self_ty_has_vid, + None => true, + }; + + let mut note = if same_self_type { + format!( + "...but it actually implements `{}`", + actual_trait_ref.map(|tr| tr.print_only_trait_path()), + ) + } else if passive_voice { + format!( + "...but `{}` is actually implemented for the type `{}`", + actual_trait_ref.map(|tr| tr.print_only_trait_path()), + actual_trait_ref.map(|tr| tr.self_ty()), + ) + } else { + format!( + "...but `{}` actually implements `{}`", + actual_trait_ref.map(|tr| tr.self_ty()), + actual_trait_ref.map(|tr| tr.print_only_trait_path()), + ) + }; + + if let Some(n) = actual_has_vid { + let _ = write!(note, ", for some specific lifetime `'{}`", n); + } + + note + }); + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs new file mode 100644 index 000000000..9886c572a --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -0,0 +1,577 @@ +//! Error Reporting for static impl Traits. + +use crate::infer::error_reporting::nice_region_error::NiceRegionError; +use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::{SubregionOrigin, TypeTrace}; +use crate::traits::{ObligationCauseCode, UnifyReceiverContext}; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{walk_ty, Visitor}; +use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind}; +use rustc_middle::ty::{ + self, AssocItemContainer, StaticLifetimeVisitor, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor, +}; +use rustc_span::symbol::Ident; +use rustc_span::Span; + +use std::ops::ControlFlow; + +impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { + /// Print the error message for lifetime errors when the return type is a static `impl Trait`, + /// `dyn Trait` or if a method call on a trait object introduces a static requirement. + pub(super) fn try_report_static_impl_trait(&self) -> Option<ErrorGuaranteed> { + debug!("try_report_static_impl_trait(error={:?})", self.error); + let tcx = self.tcx(); + let (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans) = match self.error.as_ref()? { + RegionResolutionError::SubSupConflict( + _, + var_origin, + sub_origin, + sub_r, + sup_origin, + sup_r, + spans, + ) if sub_r.is_static() => (var_origin, sub_origin, sub_r, sup_origin, sup_r, spans), + RegionResolutionError::ConcreteFailure( + SubregionOrigin::Subtype(box TypeTrace { cause, .. }), + sub_r, + sup_r, + ) if sub_r.is_static() => { + // This is for an implicit `'static` requirement coming from `impl dyn Trait {}`. + if let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() { + // This may have a closure and it would cause ICE + // through `find_param_with_region` (#78262). + let anon_reg_sup = tcx.is_suitable_region(*sup_r)?; + let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); + if fn_returns.is_empty() { + return None; + } + + let param = self.find_param_with_region(*sup_r, *sub_r)?; + let lifetime = if sup_r.has_name() { + format!("lifetime `{}`", sup_r) + } else { + "an anonymous lifetime `'_`".to_string() + }; + let mut err = struct_span_err!( + tcx.sess, + cause.span, + E0772, + "{} has {} but calling `{}` introduces an implicit `'static` lifetime \ + requirement", + param + .param + .pat + .simple_ident() + .map(|s| format!("`{}`", s)) + .unwrap_or_else(|| "`fn` parameter".to_string()), + lifetime, + ctxt.assoc_item.name, + ); + err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime)); + err.span_label( + cause.span, + &format!( + "...is used and required to live as long as `'static` here \ + because of an implicit lifetime bound on the {}", + match ctxt.assoc_item.container { + AssocItemContainer::TraitContainer => { + let id = ctxt.assoc_item.container_id(tcx); + format!("`impl` of `{}`", tcx.def_path_str(id)) + } + AssocItemContainer::ImplContainer => "inherent `impl`".to_string(), + }, + ), + ); + if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) { + let reported = err.emit(); + return Some(reported); + } else { + err.cancel(); + } + } + return None; + } + _ => return None, + }; + debug!( + "try_report_static_impl_trait(var={:?}, sub={:?} {:?} sup={:?} {:?})", + var_origin, sub_origin, sub_r, sup_origin, sup_r + ); + let anon_reg_sup = tcx.is_suitable_region(*sup_r)?; + debug!("try_report_static_impl_trait: anon_reg_sup={:?}", anon_reg_sup); + let sp = var_origin.span(); + let return_sp = sub_origin.span(); + let param = self.find_param_with_region(*sup_r, *sub_r)?; + let (lifetime_name, lifetime) = if sup_r.has_name() { + (sup_r.to_string(), format!("lifetime `{}`", sup_r)) + } else { + ("'_".to_owned(), "an anonymous lifetime `'_`".to_string()) + }; + let param_name = param + .param + .pat + .simple_ident() + .map(|s| format!("`{}`", s)) + .unwrap_or_else(|| "`fn` parameter".to_string()); + let mut err = struct_span_err!( + tcx.sess, + sp, + E0759, + "{} has {} but it needs to satisfy a `'static` lifetime requirement", + param_name, + lifetime, + ); + + let (mention_influencer, influencer_point) = + if sup_origin.span().overlaps(param.param_ty_span) { + // Account for `async fn` like in `async-await/issues/issue-62097.rs`. + // The desugaring of `async `fn`s causes `sup_origin` and `param` to point at the same + // place (but with different `ctxt`, hence `overlaps` instead of `==` above). + // + // This avoids the following: + // + // LL | pub async fn run_dummy_fn(&self) { + // | ^^^^^ + // | | + // | this data with an anonymous lifetime `'_`... + // | ...is captured here... + (false, sup_origin.span()) + } else { + (!sup_origin.span().overlaps(return_sp), param.param_ty_span) + }; + err.span_label(influencer_point, &format!("this data with {}...", lifetime)); + + debug!("try_report_static_impl_trait: param_info={:?}", param); + + let mut spans = spans.clone(); + + if mention_influencer { + spans.push(sup_origin.span()); + } + // We dedup the spans *ignoring* expansion context. + spans.sort(); + spans.dedup_by_key(|span| (span.lo(), span.hi())); + + // We try to make the output have fewer overlapping spans if possible. + let require_msg = if spans.is_empty() { + "...is used and required to live as long as `'static` here" + } else { + "...and is required to live as long as `'static` here" + }; + let require_span = + if sup_origin.span().overlaps(return_sp) { sup_origin.span() } else { return_sp }; + + for span in &spans { + err.span_label(*span, "...is used here..."); + } + + if spans.iter().any(|sp| sp.overlaps(return_sp) || *sp > return_sp) { + // If any of the "captured here" labels appears on the same line or after + // `require_span`, we put it on a note to ensure the text flows by appearing + // always at the end. + err.span_note(require_span, require_msg); + } else { + // We don't need a note, it's already at the end, it can be shown as a `span_label`. + err.span_label(require_span, require_msg); + } + + if let SubregionOrigin::RelateParamBound(_, _, Some(bound)) = sub_origin { + err.span_note(*bound, "`'static` lifetime requirement introduced by this bound"); + } + if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = sub_origin { + if let ObligationCauseCode::ReturnValue(hir_id) + | ObligationCauseCode::BlockTailExpression(hir_id) = cause.code() + { + let parent_id = tcx.hir().get_parent_item(*hir_id); + let parent_id = tcx.hir().local_def_id_to_hir_id(parent_id); + if let Some(fn_decl) = tcx.hir().fn_decl_by_hir_id(parent_id) { + let mut span: MultiSpan = fn_decl.output.span().into(); + let mut add_label = true; + if let hir::FnRetTy::Return(ty) = fn_decl.output { + let mut v = StaticLifetimeVisitor(vec![], tcx.hir()); + v.visit_ty(ty); + if !v.0.is_empty() { + span = v.0.clone().into(); + for sp in v.0 { + span.push_span_label(sp, "`'static` requirement introduced here"); + } + add_label = false; + } + } + if add_label { + span.push_span_label( + fn_decl.output.span(), + "requirement introduced by this return type", + ); + } + span.push_span_label(cause.span, "because of this returned expression"); + err.span_note( + span, + "`'static` lifetime requirement introduced by the return type", + ); + } + } + } + + let fn_returns = tcx.return_type_impl_or_dyn_traits(anon_reg_sup.def_id); + + let mut override_error_code = None; + if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sup_origin + && let ObligationCauseCode::UnifyReceiver(ctxt) = cause.code() + // Handle case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a + // `'static` lifetime when called as a method on a binding: `bar.qux()`. + && self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) + { + override_error_code = Some(ctxt.assoc_item.name); + } + + if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin + && let code = match cause.code() { + ObligationCauseCode::MatchImpl(parent, ..) => parent.code(), + _ => cause.code(), + } + && let (&ObligationCauseCode::ItemObligation(item_def_id), None) = (code, override_error_code) + { + // Same case of `impl Foo for dyn Bar { fn qux(&self) {} }` introducing a `'static` + // lifetime as above, but called using a fully-qualified path to the method: + // `Foo::qux(bar)`. + let mut v = TraitObjectVisitor(FxHashSet::default()); + v.visit_ty(param.param_ty); + if let Some((ident, self_ty)) = + self.get_impl_ident_and_self_ty_from_trait(item_def_id, &v.0) + && self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty) + { + override_error_code = Some(ident.name); + } + } + if let (Some(ident), true) = (override_error_code, fn_returns.is_empty()) { + // Provide a more targeted error code and description. + err.code(rustc_errors::error_code!(E0772)); + err.set_primary_message(&format!( + "{} has {} but calling `{}` introduces an implicit `'static` lifetime \ + requirement", + param_name, lifetime, ident, + )); + } + + let arg = match param.param.pat.simple_ident() { + Some(simple_ident) => format!("argument `{}`", simple_ident), + None => "the argument".to_string(), + }; + let captures = format!("captures data from {}", arg); + suggest_new_region_bound( + tcx, + &mut err, + fn_returns, + lifetime_name, + Some(arg), + captures, + Some((param.param_ty_span, param.param_ty.to_string())), + ); + + let reported = err.emit(); + Some(reported) + } +} + +pub fn suggest_new_region_bound( + tcx: TyCtxt<'_>, + err: &mut Diagnostic, + fn_returns: Vec<&rustc_hir::Ty<'_>>, + lifetime_name: String, + arg: Option<String>, + captures: String, + param: Option<(Span, String)>, +) { + debug!("try_report_static_impl_trait: fn_return={:?}", fn_returns); + // FIXME: account for the need of parens in `&(dyn Trait + '_)` + let consider = "consider changing the"; + let declare = "to declare that the"; + let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name); + let explicit_static = + arg.map(|arg| format!("explicit `'static` bound to the lifetime of {}", arg)); + let add_static_bound = "alternatively, add an explicit `'static` bound to this reference"; + let plus_lt = format!(" + {}", lifetime_name); + for fn_return in fn_returns { + if fn_return.span.desugaring_kind().is_some() { + // Skip `async` desugaring `impl Future`. + continue; + } + match fn_return.kind { + TyKind::OpaqueDef(item_id, _) => { + let item = tcx.hir().item(item_id); + let ItemKind::OpaqueTy(opaque) = &item.kind else { + return; + }; + + if let Some(span) = opaque + .bounds + .iter() + .filter_map(|arg| match arg { + GenericBound::Outlives(Lifetime { + name: LifetimeName::Static, + span, + .. + }) => Some(*span), + _ => None, + }) + .next() + { + if let Some(explicit_static) = &explicit_static { + err.span_suggestion_verbose( + span, + &format!("{} `impl Trait`'s {}", consider, explicit_static), + &lifetime_name, + Applicability::MaybeIncorrect, + ); + } + if let Some((param_span, param_ty)) = param.clone() { + err.span_suggestion_verbose( + param_span, + add_static_bound, + param_ty, + Applicability::MaybeIncorrect, + ); + } + } else if opaque + .bounds + .iter() + .filter_map(|arg| match arg { + GenericBound::Outlives(Lifetime { name, span, .. }) + if name.ident().to_string() == lifetime_name => + { + Some(*span) + } + _ => None, + }) + .next() + .is_some() + { + } else { + err.span_suggestion_verbose( + fn_return.span.shrink_to_hi(), + &format!( + "{declare} `impl Trait` {captures}, {explicit}", + declare = declare, + captures = captures, + explicit = explicit, + ), + &plus_lt, + Applicability::MaybeIncorrect, + ); + } + } + TyKind::TraitObject(_, lt, _) => match lt.name { + LifetimeName::ImplicitObjectLifetimeDefault => { + err.span_suggestion_verbose( + fn_return.span.shrink_to_hi(), + &format!( + "{declare} trait object {captures}, {explicit}", + declare = declare, + captures = captures, + explicit = explicit, + ), + &plus_lt, + Applicability::MaybeIncorrect, + ); + } + name if name.ident().to_string() != lifetime_name => { + // With this check we avoid suggesting redundant bounds. This + // would happen if there are nested impl/dyn traits and only + // one of them has the bound we'd suggest already there, like + // in `impl Foo<X = dyn Bar> + '_`. + if let Some(explicit_static) = &explicit_static { + err.span_suggestion_verbose( + lt.span, + &format!("{} trait object's {}", consider, explicit_static), + &lifetime_name, + Applicability::MaybeIncorrect, + ); + } + if let Some((param_span, param_ty)) = param.clone() { + err.span_suggestion_verbose( + param_span, + add_static_bound, + param_ty, + Applicability::MaybeIncorrect, + ); + } + } + _ => {} + }, + _ => {} + } + } +} + +impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { + fn get_impl_ident_and_self_ty_from_trait( + &self, + def_id: DefId, + trait_objects: &FxHashSet<DefId>, + ) -> Option<(Ident, &'tcx hir::Ty<'tcx>)> { + let tcx = self.tcx(); + match tcx.hir().get_if_local(def_id) { + Some(Node::ImplItem(impl_item)) => { + match tcx.hir().find_by_def_id(tcx.hir().get_parent_item(impl_item.hir_id())) { + Some(Node::Item(Item { + kind: ItemKind::Impl(hir::Impl { self_ty, .. }), + .. + })) => Some((impl_item.ident, self_ty)), + _ => None, + } + } + Some(Node::TraitItem(trait_item)) => { + let trait_did = tcx.hir().get_parent_item(trait_item.hir_id()); + match tcx.hir().find_by_def_id(trait_did) { + Some(Node::Item(Item { kind: ItemKind::Trait(..), .. })) => { + // The method being called is defined in the `trait`, but the `'static` + // obligation comes from the `impl`. Find that `impl` so that we can point + // at it in the suggestion. + let trait_did = trait_did.to_def_id(); + match tcx + .hir() + .trait_impls(trait_did) + .iter() + .filter_map(|&impl_did| { + match tcx.hir().get_if_local(impl_did.to_def_id()) { + Some(Node::Item(Item { + kind: ItemKind::Impl(hir::Impl { self_ty, .. }), + .. + })) if trait_objects.iter().all(|did| { + // FIXME: we should check `self_ty` against the receiver + // type in the `UnifyReceiver` context, but for now, use + // this imperfect proxy. This will fail if there are + // multiple `impl`s for the same trait like + // `impl Foo for Box<dyn Bar>` and `impl Foo for dyn Bar`. + // In that case, only the first one will get suggestions. + let mut traits = vec![]; + let mut hir_v = HirTraitObjectVisitor(&mut traits, *did); + hir_v.visit_ty(self_ty); + !traits.is_empty() + }) => + { + Some(self_ty) + } + _ => None, + } + }) + .next() + { + Some(self_ty) => Some((trait_item.ident, self_ty)), + _ => None, + } + } + _ => None, + } + } + _ => None, + } + } + + /// When we call a method coming from an `impl Foo for dyn Bar`, `dyn Bar` introduces a default + /// `'static` obligation. Suggest relaxing that implicit bound. + fn find_impl_on_dyn_trait( + &self, + err: &mut Diagnostic, + ty: Ty<'_>, + ctxt: &UnifyReceiverContext<'tcx>, + ) -> bool { + let tcx = self.tcx(); + + // Find the method being called. + let Ok(Some(instance)) = ty::Instance::resolve( + tcx, + ctxt.param_env, + ctxt.assoc_item.def_id, + self.infcx.resolve_vars_if_possible(ctxt.substs), + ) else { + return false; + }; + + let mut v = TraitObjectVisitor(FxHashSet::default()); + v.visit_ty(ty); + + // Get the `Ident` of the method being called and the corresponding `impl` (to point at + // `Bar` in `impl Foo for dyn Bar {}` and the definition of the method being called). + let Some((ident, self_ty)) = self.get_impl_ident_and_self_ty_from_trait(instance.def_id(), &v.0) else { + return false; + }; + + // Find the trait object types in the argument, so we point at *only* the trait object. + self.suggest_constrain_dyn_trait_in_impl(err, &v.0, ident, self_ty) + } + + fn suggest_constrain_dyn_trait_in_impl( + &self, + err: &mut Diagnostic, + found_dids: &FxHashSet<DefId>, + ident: Ident, + self_ty: &hir::Ty<'_>, + ) -> bool { + let mut suggested = false; + for found_did in found_dids { + let mut traits = vec![]; + let mut hir_v = HirTraitObjectVisitor(&mut traits, *found_did); + hir_v.visit_ty(&self_ty); + for span in &traits { + let mut multi_span: MultiSpan = vec![*span].into(); + multi_span + .push_span_label(*span, "this has an implicit `'static` lifetime requirement"); + multi_span.push_span_label( + ident.span, + "calling this method introduces the `impl`'s 'static` requirement", + ); + err.span_note(multi_span, "the used `impl` has a `'static` requirement"); + err.span_suggestion_verbose( + span.shrink_to_hi(), + "consider relaxing the implicit `'static` requirement", + " + '_", + Applicability::MaybeIncorrect, + ); + suggested = true; + } + } + suggested + } +} + +/// Collect all the trait objects in a type that could have received an implicit `'static` lifetime. +pub struct TraitObjectVisitor(pub FxHashSet<DefId>); + +impl<'tcx> TypeVisitor<'tcx> for TraitObjectVisitor { + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + match t.kind() { + ty::Dynamic(preds, re) if re.is_static() => { + if let Some(def_id) = preds.principal_def_id() { + self.0.insert(def_id); + } + ControlFlow::CONTINUE + } + _ => t.super_visit_with(self), + } + } +} + +/// Collect all `hir::Ty<'_>` `Span`s for trait objects with an implicit lifetime. +pub struct HirTraitObjectVisitor<'a>(pub &'a mut Vec<Span>, pub DefId); + +impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { + fn visit_ty(&mut self, t: &'tcx hir::Ty<'tcx>) { + if let TyKind::TraitObject( + poly_trait_refs, + Lifetime { name: LifetimeName::ImplicitObjectLifetimeDefault, .. }, + _, + ) = t.kind + { + for ptr in poly_trait_refs { + if Some(self.1) == ptr.trait_ref.trait_def_id() { + self.0.push(ptr.span); + } + } + } + walk_ty(self, t); + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs new file mode 100644 index 000000000..da465a764 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -0,0 +1,176 @@ +//! Error Reporting for `impl` items that do not match the obligations from their `trait`. + +use crate::infer::error_reporting::nice_region_error::NiceRegionError; +use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::Subtype; +use crate::traits::ObligationCauseCode::CompareImplItemObligation; +use rustc_errors::{ErrorGuaranteed, MultiSpan}; +use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor; +use rustc_middle::hir::nested_filter; +use rustc_middle::ty::print::RegionHighlightMode; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor}; +use rustc_span::Span; + +use std::ops::ControlFlow; + +impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { + /// Print the error message for lifetime errors when the `impl` doesn't conform to the `trait`. + pub(super) fn try_report_impl_not_conforming_to_trait(&self) -> Option<ErrorGuaranteed> { + let error = self.error.as_ref()?; + debug!("try_report_impl_not_conforming_to_trait {:?}", error); + if let RegionResolutionError::SubSupConflict( + _, + var_origin, + sub_origin, + _sub, + sup_origin, + _sup, + _, + ) = error.clone() + && let (Subtype(sup_trace), Subtype(sub_trace)) = (&sup_origin, &sub_origin) + && let sub_expected_found @ Some((sub_expected, sub_found)) = sub_trace.values.ty() + && let sup_expected_found @ Some(_) = sup_trace.values.ty() + && let CompareImplItemObligation { trait_item_def_id, .. } = sub_trace.cause.code() + && sup_expected_found == sub_expected_found + { + let guar = + self.emit_err(var_origin.span(), sub_expected, sub_found, *trait_item_def_id); + return Some(guar); + } + None + } + + fn emit_err( + &self, + sp: Span, + expected: Ty<'tcx>, + found: Ty<'tcx>, + trait_def_id: DefId, + ) -> ErrorGuaranteed { + let trait_sp = self.tcx().def_span(trait_def_id); + let mut err = self + .tcx() + .sess + .struct_span_err(sp, "`impl` item signature doesn't match `trait` item signature"); + + // Mark all unnamed regions in the type with a number. + // This diagnostic is called in response to lifetime errors, so be informative. + struct HighlightBuilder<'tcx> { + highlight: RegionHighlightMode<'tcx>, + counter: usize, + } + + impl<'tcx> HighlightBuilder<'tcx> { + fn build(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> RegionHighlightMode<'tcx> { + let mut builder = + HighlightBuilder { highlight: RegionHighlightMode::new(tcx), counter: 1 }; + builder.visit_ty(ty); + builder.highlight + } + } + + impl<'tcx> ty::visit::TypeVisitor<'tcx> for HighlightBuilder<'tcx> { + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + if !r.has_name() && self.counter <= 3 { + self.highlight.highlighting_region(r, self.counter); + self.counter += 1; + } + r.super_visit_with(self) + } + } + + let expected_highlight = HighlightBuilder::build(self.tcx(), expected); + let expected = self + .infcx + .extract_inference_diagnostics_data(expected.into(), Some(expected_highlight)) + .name; + let found_highlight = HighlightBuilder::build(self.tcx(), found); + let found = + self.infcx.extract_inference_diagnostics_data(found.into(), Some(found_highlight)).name; + + err.span_label(sp, &format!("found `{}`", found)); + err.span_label(trait_sp, &format!("expected `{}`", expected)); + + // Get the span of all the used type parameters in the method. + let assoc_item = self.tcx().associated_item(trait_def_id); + let mut visitor = TypeParamSpanVisitor { tcx: self.tcx(), types: vec![] }; + match assoc_item.kind { + ty::AssocKind::Fn => { + let hir = self.tcx().hir(); + if let Some(hir_id) = + assoc_item.def_id.as_local().map(|id| hir.local_def_id_to_hir_id(id)) + { + if let Some(decl) = hir.fn_decl_by_hir_id(hir_id) { + visitor.visit_fn_decl(decl); + } + } + } + _ => {} + } + let mut type_param_span: MultiSpan = visitor.types.to_vec().into(); + for &span in &visitor.types { + type_param_span + .push_span_label(span, "consider borrowing this type parameter in the trait"); + } + + err.note(&format!("expected `{}`\n found `{}`", expected, found)); + + err.span_help( + type_param_span, + "the lifetime requirements from the `impl` do not correspond to the requirements in \ + the `trait`", + ); + if visitor.types.is_empty() { + err.help( + "verify the lifetime relationships in the `trait` and `impl` between the `self` \ + argument, the other inputs and its output", + ); + } + err.emit() + } +} + +struct TypeParamSpanVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + types: Vec<Span>, +} + +impl<'tcx> Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { + type NestedFilter = nested_filter::OnlyBodies; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + + fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) { + match arg.kind { + hir::TyKind::Rptr(_, ref mut_ty) => { + // We don't want to suggest looking into borrowing `&T` or `&Self`. + hir::intravisit::walk_ty(self, mut_ty.ty); + return; + } + hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments { + [segment] + if segment + .res + .map(|res| { + matches!( + res, + Res::SelfTy { trait_: _, alias_to: _ } + | Res::Def(hir::def::DefKind::TyParam, _) + ) + }) + .unwrap_or(false) => + { + self.types.push(path.span); + } + _ => {} + }, + _ => {} + } + hir::intravisit::walk_ty(self, arg); + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs new file mode 100644 index 000000000..3e9d491af --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -0,0 +1,167 @@ +//! Helper functions corresponding to lifetime errors due to +//! anonymous regions. + +use crate::infer::error_reporting::nice_region_error::NiceRegionError; +use crate::infer::TyCtxt; +use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; +use rustc_middle::ty::{self, Binder, DefIdTree, Region, Ty, TypeVisitable}; +use rustc_span::Span; + +/// Information about the anonymous region we are searching for. +#[derive(Debug)] +pub struct AnonymousParamInfo<'tcx> { + /// The parameter corresponding to the anonymous region. + pub param: &'tcx hir::Param<'tcx>, + /// The type corresponding to the anonymous region parameter. + pub param_ty: Ty<'tcx>, + /// The ty::BoundRegionKind corresponding to the anonymous region. + pub bound_region: ty::BoundRegionKind, + /// The `Span` of the parameter type. + pub param_ty_span: Span, + /// Signals that the argument is the first parameter in the declaration. + pub is_first: bool, +} + +// This method walks the Type of the function body parameters using +// `fold_regions()` function and returns the +// &hir::Param of the function parameter corresponding to the anonymous +// region and the Ty corresponding to the named region. +// Currently only the case where the function declaration consists of +// one named region and one anonymous region is handled. +// Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32` +// Here, we would return the hir::Param for y, we return the type &'a +// i32, which is the type of y but with the anonymous region replaced +// with 'a, the corresponding bound region and is_first which is true if +// the hir::Param is the first parameter in the function declaration. +#[instrument(skip(tcx), level = "debug")] +pub fn find_param_with_region<'tcx>( + tcx: TyCtxt<'tcx>, + anon_region: Region<'tcx>, + replace_region: Region<'tcx>, +) -> Option<AnonymousParamInfo<'tcx>> { + let (id, bound_region) = match *anon_region { + ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region), + ty::ReEarlyBound(ebr) => { + (tcx.parent(ebr.def_id), ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name)) + } + _ => return None, // not a free region + }; + + let hir = &tcx.hir(); + let def_id = id.as_local()?; + let hir_id = hir.local_def_id_to_hir_id(def_id); + + // FIXME: use def_kind + // Don't perform this on closures + match hir.get(hir_id) { + hir::Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => { + return None; + } + _ => {} + } + + let body_id = hir.maybe_body_owned_by(def_id)?; + + let owner_id = hir.body_owner(body_id); + let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap(); + let poly_fn_sig = tcx.fn_sig(id); + + let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig); + let body = hir.body(body_id); + body.params + .iter() + .take(if fn_sig.c_variadic { + fn_sig.inputs().len() + } else { + assert_eq!(fn_sig.inputs().len(), body.params.len()); + body.params.len() + }) + .enumerate() + .find_map(|(index, param)| { + // May return None; sometimes the tables are not yet populated. + let ty = fn_sig.inputs()[index]; + let mut found_anon_region = false; + let new_param_ty = tcx.fold_regions(ty, |r, _| { + if r == anon_region { + found_anon_region = true; + replace_region + } else { + r + } + }); + if found_anon_region { + let ty_hir_id = fn_decl.inputs[index].hir_id; + let param_ty_span = hir.span(ty_hir_id); + let is_first = index == 0; + Some(AnonymousParamInfo { + param, + param_ty: new_param_ty, + param_ty_span, + bound_region, + is_first, + }) + } else { + None + } + }) +} + +impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { + pub(super) fn find_param_with_region( + &self, + anon_region: Region<'tcx>, + replace_region: Region<'tcx>, + ) -> Option<AnonymousParamInfo<'tcx>> { + find_param_with_region(self.tcx(), anon_region, replace_region) + } + + // Here, we check for the case where the anonymous region + // is in the return type as written by the user. + // FIXME(#42703) - Need to handle certain cases here. + pub(super) fn is_return_type_anon( + &self, + scope_def_id: LocalDefId, + br: ty::BoundRegionKind, + hir_sig: &hir::FnSig<'_>, + ) -> Option<Span> { + let fn_ty = self.tcx().type_of(scope_def_id); + if let ty::FnDef(_, _) = fn_ty.kind() { + let ret_ty = fn_ty.fn_sig(self.tcx()).output(); + let span = hir_sig.decl.output.span(); + let future_output = if hir_sig.header.is_async() { + ret_ty.map_bound(|ty| self.infcx.get_impl_future_output_ty(ty)).transpose() + } else { + None + }; + return match future_output { + Some(output) if self.includes_region(output, br) => Some(span), + None if self.includes_region(ret_ty, br) => Some(span), + _ => None, + }; + } + None + } + + fn includes_region( + &self, + ty: Binder<'tcx, impl TypeVisitable<'tcx>>, + region: ty::BoundRegionKind, + ) -> bool { + let late_bound_regions = self.tcx().collect_referenced_late_bound_regions(&ty); + late_bound_regions.iter().any(|r| *r == region) + } + + // Here we check for the case where anonymous region + // corresponds to self and if yes, we display E0312. + // FIXME(#42700) - Need to format self properly to + // enable E0621 for it. + pub(super) fn is_self_anon(&self, is_first: bool, scope_def_id: LocalDefId) -> bool { + is_first + && self + .tcx() + .opt_associated_item(scope_def_id.to_def_id()) + .map(|i| i.fn_has_self_parameter) + == Some(true) + } +} diff --git a/compiler/rustc_infer/src/infer/error_reporting/note.rs b/compiler/rustc_infer/src/infer/error_reporting/note.rs new file mode 100644 index 000000000..c1940c5c0 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -0,0 +1,414 @@ +use crate::infer::error_reporting::{note_and_explain_region, ObligationCauseExt}; +use crate::infer::{self, InferCtxt, SubregionOrigin}; +use rustc_errors::{struct_span_err, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_middle::traits::ObligationCauseCode; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::{self, Region}; + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + pub(super) fn note_region_origin(&self, err: &mut Diagnostic, origin: &SubregionOrigin<'tcx>) { + let mut label_or_note = |span, msg: &str| { + let sub_count = err.children.iter().filter(|d| d.span.is_dummy()).count(); + let expanded_sub_count = err.children.iter().filter(|d| !d.span.is_dummy()).count(); + let span_is_primary = err.span.primary_spans().iter().all(|&sp| sp == span); + if span_is_primary && sub_count == 0 && expanded_sub_count == 0 { + err.span_label(span, msg); + } else if span_is_primary && expanded_sub_count == 0 { + err.note(msg); + } else { + err.span_note(span, msg); + } + }; + match *origin { + infer::Subtype(ref trace) => { + if let Some((expected, found)) = self.values_str(trace.values) { + label_or_note( + trace.cause.span, + &format!("...so that the {}", trace.cause.as_requirement_str()), + ); + + err.note_expected_found(&"", expected, &"", found); + } else { + // FIXME: this really should be handled at some earlier stage. Our + // handling of region checking when type errors are present is + // *terrible*. + + label_or_note( + trace.cause.span, + &format!("...so that {}", trace.cause.as_requirement_str()), + ); + } + } + infer::Reborrow(span) => { + label_or_note(span, "...so that reference does not outlive borrowed content"); + } + infer::ReborrowUpvar(span, ref upvar_id) => { + let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); + label_or_note(span, &format!("...so that closure can access `{}`", var_name)); + } + infer::RelateObjectBound(span) => { + label_or_note(span, "...so that it can be closed over into an object"); + } + infer::DataBorrowed(ty, span) => { + label_or_note( + span, + &format!( + "...so that the type `{}` is not borrowed for too long", + self.ty_to_string(ty) + ), + ); + } + infer::ReferenceOutlivesReferent(ty, span) => { + label_or_note( + span, + &format!( + "...so that the reference type `{}` does not outlive the data it points at", + self.ty_to_string(ty) + ), + ); + } + infer::RelateParamBound(span, t, opt_span) => { + label_or_note( + span, + &format!( + "...so that the type `{}` will meet its required lifetime bounds{}", + self.ty_to_string(t), + if opt_span.is_some() { "..." } else { "" }, + ), + ); + if let Some(span) = opt_span { + err.span_note(span, "...that is required by this bound"); + } + } + infer::RelateRegionParamBound(span) => { + label_or_note( + span, + "...so that the declared lifetime parameter bounds are satisfied", + ); + } + infer::CompareImplItemObligation { span, .. } => { + label_or_note( + span, + "...so that the definition in impl matches the definition from the trait", + ); + } + infer::CheckAssociatedTypeBounds { ref parent, .. } => { + self.note_region_origin(err, &parent); + } + } + } + + pub(super) fn report_concrete_failure( + &self, + origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + match origin { + infer::Subtype(box trace) => { + let terr = TypeError::RegionsDoesNotOutlive(sup, sub); + let mut err = self.report_and_explain_type_error(trace, &terr); + match (*sub, *sup) { + (ty::RePlaceholder(_), ty::RePlaceholder(_)) => {} + (ty::RePlaceholder(_), _) => { + note_and_explain_region( + self.tcx, + &mut err, + "", + sup, + " doesn't meet the lifetime requirements", + None, + ); + } + (_, ty::RePlaceholder(_)) => { + note_and_explain_region( + self.tcx, + &mut err, + "the required lifetime does not necessarily outlive ", + sub, + "", + None, + ); + } + _ => { + note_and_explain_region(self.tcx, &mut err, "", sup, "...", None); + note_and_explain_region( + self.tcx, + &mut err, + "...does not necessarily outlive ", + sub, + "", + None, + ); + } + } + err + } + infer::Reborrow(span) => { + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0312, + "lifetime of reference outlives lifetime of borrowed content..." + ); + note_and_explain_region( + self.tcx, + &mut err, + "...the reference is valid for ", + sub, + "...", + None, + ); + note_and_explain_region( + self.tcx, + &mut err, + "...but the borrowed content is only valid for ", + sup, + "", + None, + ); + err + } + infer::ReborrowUpvar(span, ref upvar_id) => { + let var_name = self.tcx.hir().name(upvar_id.var_path.hir_id); + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0313, + "lifetime of borrowed pointer outlives lifetime of captured variable `{}`...", + var_name + ); + note_and_explain_region( + self.tcx, + &mut err, + "...the borrowed pointer is valid for ", + sub, + "...", + None, + ); + note_and_explain_region( + self.tcx, + &mut err, + &format!("...but `{}` is only valid for ", var_name), + sup, + "", + None, + ); + err + } + infer::RelateObjectBound(span) => { + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0476, + "lifetime of the source pointer does not outlive lifetime bound of the \ + object type" + ); + note_and_explain_region( + self.tcx, + &mut err, + "object type is valid for ", + sub, + "", + None, + ); + note_and_explain_region( + self.tcx, + &mut err, + "source pointer is only valid for ", + sup, + "", + None, + ); + err + } + infer::RelateParamBound(span, ty, opt_span) => { + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0477, + "the type `{}` does not fulfill the required lifetime", + self.ty_to_string(ty) + ); + match *sub { + ty::ReStatic => note_and_explain_region( + self.tcx, + &mut err, + "type must satisfy ", + sub, + if opt_span.is_some() { " as required by this binding" } else { "" }, + opt_span, + ), + _ => note_and_explain_region( + self.tcx, + &mut err, + "type must outlive ", + sub, + if opt_span.is_some() { " as required by this binding" } else { "" }, + opt_span, + ), + } + err + } + infer::RelateRegionParamBound(span) => { + let mut err = + struct_span_err!(self.tcx.sess, span, E0478, "lifetime bound not satisfied"); + note_and_explain_region( + self.tcx, + &mut err, + "lifetime parameter instantiated with ", + sup, + "", + None, + ); + note_and_explain_region( + self.tcx, + &mut err, + "but lifetime parameter must outlive ", + sub, + "", + None, + ); + err + } + infer::DataBorrowed(ty, span) => { + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0490, + "a value of type `{}` is borrowed for too long", + self.ty_to_string(ty) + ); + note_and_explain_region( + self.tcx, + &mut err, + "the type is valid for ", + sub, + "", + None, + ); + note_and_explain_region( + self.tcx, + &mut err, + "but the borrow lasts for ", + sup, + "", + None, + ); + err + } + infer::ReferenceOutlivesReferent(ty, span) => { + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0491, + "in type `{}`, reference has a longer lifetime than the data it references", + self.ty_to_string(ty) + ); + note_and_explain_region( + self.tcx, + &mut err, + "the pointer is valid for ", + sub, + "", + None, + ); + note_and_explain_region( + self.tcx, + &mut err, + "but the referenced data is only valid for ", + sup, + "", + None, + ); + err + } + infer::CompareImplItemObligation { span, impl_item_def_id, trait_item_def_id } => self + .report_extra_impl_obligation( + span, + impl_item_def_id, + trait_item_def_id, + &format!("`{}: {}`", sup, sub), + ), + infer::CheckAssociatedTypeBounds { impl_item_def_id, trait_item_def_id, parent } => { + let mut err = self.report_concrete_failure(*parent, sub, sup); + + let trait_item_span = self.tcx.def_span(trait_item_def_id); + let item_name = self.tcx.item_name(impl_item_def_id.to_def_id()); + err.span_label( + trait_item_span, + format!("definition of `{}` from trait", item_name), + ); + + let trait_predicates = self.tcx.explicit_predicates_of(trait_item_def_id); + let impl_predicates = self.tcx.explicit_predicates_of(impl_item_def_id); + + let impl_predicates: rustc_data_structures::fx::FxHashSet<_> = + impl_predicates.predicates.into_iter().map(|(pred, _)| pred).collect(); + let clauses: Vec<_> = trait_predicates + .predicates + .into_iter() + .filter(|&(pred, _)| !impl_predicates.contains(pred)) + .map(|(pred, _)| format!("{}", pred)) + .collect(); + + if !clauses.is_empty() { + let generics = self.tcx.hir().get_generics(impl_item_def_id).unwrap(); + let where_clause_span = generics.tail_span_for_predicate_suggestion(); + + let suggestion = format!( + "{} {}", + generics.add_where_or_trailing_comma(), + clauses.join(", "), + ); + err.span_suggestion( + where_clause_span, + &format!( + "try copying {} from the trait", + if clauses.len() > 1 { "these clauses" } else { "this clause" } + ), + suggestion, + rustc_errors::Applicability::MaybeIncorrect, + ); + } + + err + } + } + } + + pub(super) fn report_placeholder_failure( + &self, + placeholder_origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + // I can't think how to do better than this right now. -nikomatsakis + debug!(?placeholder_origin, ?sub, ?sup, "report_placeholder_failure"); + match placeholder_origin { + infer::Subtype(box ref trace) + if matches!( + &trace.cause.code().peel_derives(), + ObligationCauseCode::BindingObligation(..) + ) => + { + // Hack to get around the borrow checker because trace.cause has an `Rc`. + if let ObligationCauseCode::BindingObligation(_, span) = + &trace.cause.code().peel_derives() + { + let span = *span; + let mut err = self.report_concrete_failure(placeholder_origin, sub, sup); + err.span_note(span, "the lifetime requirement is introduced here"); + err + } else { + unreachable!() + } + } + infer::Subtype(box trace) => { + let terr = TypeError::RegionsPlaceholderMismatch; + return self.report_and_explain_type_error(trace, &terr); + } + _ => return self.report_concrete_failure(placeholder_origin, sub, sup), + } + } +} diff --git a/compiler/rustc_infer/src/infer/free_regions.rs b/compiler/rustc_infer/src/infer/free_regions.rs new file mode 100644 index 000000000..d566634a4 --- /dev/null +++ b/compiler/rustc_infer/src/infer/free_regions.rs @@ -0,0 +1,119 @@ +//! This module handles the relationships between "free regions", i.e., lifetime parameters. +//! Ordinarily, free regions are unrelated to one another, but they can be related via implied +//! or explicit bounds. In that case, we track the bounds using the `TransitiveRelation` type, +//! and use that to decide when one free region outlives another, and so forth. + +use rustc_data_structures::transitive_relation::TransitiveRelation; +use rustc_middle::ty::{Lift, Region, TyCtxt}; + +/// Combines a `FreeRegionMap` and a `TyCtxt`. +/// +/// This stuff is a bit convoluted and should be refactored, but as we +/// transition to NLL, it'll all go away anyhow. +pub(crate) struct RegionRelations<'a, 'tcx> { + pub tcx: TyCtxt<'tcx>, + + /// Free-region relationships. + pub free_regions: &'a FreeRegionMap<'tcx>, +} + +impl<'a, 'tcx> RegionRelations<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, free_regions: &'a FreeRegionMap<'tcx>) -> Self { + Self { tcx, free_regions } + } + + pub fn lub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> Region<'tcx> { + self.free_regions.lub_free_regions(self.tcx, r_a, r_b) + } +} + +#[derive(Clone, Debug, Default)] +pub struct FreeRegionMap<'tcx> { + // Stores the relation `a < b`, where `a` and `b` are regions. + // + // Invariant: only free regions like `'x` or `'static` are stored + // in this relation, not scopes. + relation: TransitiveRelation<Region<'tcx>>, +} + +impl<'tcx> FreeRegionMap<'tcx> { + pub fn elements(&self) -> impl Iterator<Item = Region<'tcx>> + '_ { + self.relation.elements().copied() + } + + pub fn is_empty(&self) -> bool { + self.relation.is_empty() + } + + // Record that `'sup:'sub`. Or, put another way, `'sub <= 'sup`. + // (with the exception that `'static: 'x` is not notable) + pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { + debug!("relate_regions(sub={:?}, sup={:?})", sub, sup); + if sub.is_free_or_static() && sup.is_free() { + self.relation.add(sub, sup) + } + } + + /// Tests whether `r_a <= r_b`. + /// + /// Both regions must meet `is_free_or_static`. + /// + /// Subtle: one tricky case that this code gets correct is as + /// follows. If we know that `r_b: 'static`, then this function + /// will return true, even though we don't know anything that + /// directly relates `r_a` and `r_b`. + pub fn sub_free_regions( + &self, + tcx: TyCtxt<'tcx>, + r_a: Region<'tcx>, + r_b: Region<'tcx>, + ) -> bool { + assert!(r_a.is_free_or_static() && r_b.is_free_or_static()); + let re_static = tcx.lifetimes.re_static; + if self.check_relation(re_static, r_b) { + // `'a <= 'static` is always true, and not stored in the + // relation explicitly, so check if `'b` is `'static` (or + // equivalent to it) + true + } else { + self.check_relation(r_a, r_b) + } + } + + /// Check whether `r_a <= r_b` is found in the relation. + fn check_relation(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool { + r_a == r_b || self.relation.contains(r_a, r_b) + } + + /// Computes the least-upper-bound of two free regions. In some + /// cases, this is more conservative than necessary, in order to + /// avoid making arbitrary choices. See + /// `TransitiveRelation::postdom_upper_bound` for more details. + pub fn lub_free_regions( + &self, + tcx: TyCtxt<'tcx>, + r_a: Region<'tcx>, + r_b: Region<'tcx>, + ) -> Region<'tcx> { + debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b); + assert!(r_a.is_free()); + assert!(r_b.is_free()); + let result = if r_a == r_b { + r_a + } else { + match self.relation.postdom_upper_bound(r_a, r_b) { + None => tcx.lifetimes.re_static, + Some(r) => r, + } + }; + debug!("lub_free_regions(r_a={:?}, r_b={:?}) = {:?}", r_a, r_b, result); + result + } +} + +impl<'a, 'tcx> Lift<'tcx> for FreeRegionMap<'a> { + type Lifted = FreeRegionMap<'tcx>; + fn lift_to_tcx(self, tcx: TyCtxt<'tcx>) -> Option<FreeRegionMap<'tcx>> { + self.relation.maybe_map(|fr| tcx.lift(fr)).map(|relation| FreeRegionMap { relation }) + } +} diff --git a/compiler/rustc_infer/src/infer/freshen.rs b/compiler/rustc_infer/src/infer/freshen.rs new file mode 100644 index 000000000..84004d2b2 --- /dev/null +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -0,0 +1,255 @@ +//! Freshening is the process of replacing unknown variables with fresh types. The idea is that +//! the type, after freshening, contains no inference variables but instead contains either a +//! value for each variable or fresh "arbitrary" types wherever a variable would have been. +//! +//! Freshening is used primarily to get a good type for inserting into a cache. The result +//! summarizes what the type inferencer knows "so far". The primary place it is used right now is +//! in the trait matching algorithm, which needs to be able to cache whether an `impl` self type +//! matches some other type X -- *without* affecting `X`. That means if that if the type `X` is in +//! fact an unbound type variable, we want the match to be regarded as ambiguous, because depending +//! on what type that type variable is ultimately assigned, the match may or may not succeed. +//! +//! To handle closures, freshened types also have to contain the signature and kind of any +//! closure in the local inference context, as otherwise the cache key might be invalidated. +//! The way this is done is somewhat hacky - the closure signature is appended to the substs, +//! as well as the closure kind "encoded" as a type. Also, special handling is needed when +//! the closure signature contains a reference to the original closure. +//! +//! Note that you should be careful not to allow the output of freshening to leak to the user in +//! error messages or in any other form. Freshening is only really useful as an internal detail. +//! +//! Because of the manipulation required to handle closures, doing arbitrary operations on +//! freshened types is not recommended. However, in addition to doing equality/hash +//! comparisons (for caching), it is possible to do a `ty::_match` operation between +//! 2 freshened types - this works even with the closure encoding. +//! +//! __An important detail concerning regions.__ The freshener also replaces *all* free regions with +//! 'erased. The reason behind this is that, in general, we do not take region relationships into +//! account when making type-overloaded decisions. This is important because of the design of the +//! region inferencer, which is not based on unification but rather on accumulating and then +//! solving a set of constraints. In contrast, the type inferencer assigns a value to each type +//! variable only once, and it does so as soon as it can, so it is reasonable to ask what the type +//! inferencer knows "so far". +use super::InferCtxt; +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::infer::unify_key::ToType; +use rustc_middle::ty::fold::TypeFolder; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitable}; +use std::collections::hash_map::Entry; + +pub struct TypeFreshener<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + ty_freshen_count: u32, + const_freshen_count: u32, + ty_freshen_map: FxHashMap<ty::InferTy, Ty<'tcx>>, + const_freshen_map: FxHashMap<ty::InferConst<'tcx>, ty::Const<'tcx>>, + keep_static: bool, +} + +impl<'a, 'tcx> TypeFreshener<'a, 'tcx> { + pub fn new(infcx: &'a InferCtxt<'a, 'tcx>, keep_static: bool) -> TypeFreshener<'a, 'tcx> { + TypeFreshener { + infcx, + ty_freshen_count: 0, + const_freshen_count: 0, + ty_freshen_map: Default::default(), + const_freshen_map: Default::default(), + keep_static, + } + } + + fn freshen_ty<F>( + &mut self, + opt_ty: Option<Ty<'tcx>>, + key: ty::InferTy, + freshener: F, + ) -> Ty<'tcx> + where + F: FnOnce(u32) -> ty::InferTy, + { + if let Some(ty) = opt_ty { + return ty.fold_with(self); + } + + match self.ty_freshen_map.entry(key) { + Entry::Occupied(entry) => *entry.get(), + Entry::Vacant(entry) => { + let index = self.ty_freshen_count; + self.ty_freshen_count += 1; + let t = self.infcx.tcx.mk_ty_infer(freshener(index)); + entry.insert(t); + t + } + } + } + + fn freshen_const<F>( + &mut self, + opt_ct: Option<ty::Const<'tcx>>, + key: ty::InferConst<'tcx>, + freshener: F, + ty: Ty<'tcx>, + ) -> ty::Const<'tcx> + where + F: FnOnce(u32) -> ty::InferConst<'tcx>, + { + if let Some(ct) = opt_ct { + return ct.fold_with(self); + } + + match self.const_freshen_map.entry(key) { + Entry::Occupied(entry) => *entry.get(), + Entry::Vacant(entry) => { + let index = self.const_freshen_count; + self.const_freshen_count += 1; + let ct = self.infcx.tcx.mk_const_infer(freshener(index), ty); + entry.insert(ct); + ct + } + } + } +} + +impl<'a, 'tcx> TypeFolder<'tcx> for TypeFreshener<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + ty::ReLateBound(..) => { + // leave bound regions alone + r + } + + ty::ReEarlyBound(..) + | ty::ReFree(_) + | ty::ReVar(_) + | ty::RePlaceholder(..) + | ty::ReEmpty(_) + | ty::ReErased => { + // replace all free regions with 'erased + self.tcx().lifetimes.re_erased + } + ty::ReStatic => { + if self.keep_static { + r + } else { + self.tcx().lifetimes.re_erased + } + } + } + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + if !t.needs_infer() && !t.has_erasable_regions() { + return t; + } + + let tcx = self.infcx.tcx; + + match *t.kind() { + ty::Infer(ty::TyVar(v)) => { + let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known(); + self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy) + } + + ty::Infer(ty::IntVar(v)) => self.freshen_ty( + self.infcx + .inner + .borrow_mut() + .int_unification_table() + .probe_value(v) + .map(|v| v.to_type(tcx)), + ty::IntVar(v), + ty::FreshIntTy, + ), + + ty::Infer(ty::FloatVar(v)) => self.freshen_ty( + self.infcx + .inner + .borrow_mut() + .float_unification_table() + .probe_value(v) + .map(|v| v.to_type(tcx)), + ty::FloatVar(v), + ty::FreshFloatTy, + ), + + ty::Infer(ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct)) => { + if ct >= self.ty_freshen_count { + bug!( + "Encountered a freshend type with id {} \ + but our counter is only at {}", + ct, + self.ty_freshen_count + ); + } + t + } + + ty::Generator(..) + | ty::Bool + | ty::Char + | ty::Int(..) + | ty::Uint(..) + | ty::Float(..) + | ty::Adt(..) + | ty::Str + | ty::Error(_) + | ty::Array(..) + | ty::Slice(..) + | ty::RawPtr(..) + | ty::Ref(..) + | ty::FnDef(..) + | ty::FnPtr(_) + | ty::Dynamic(..) + | ty::Never + | ty::Tuple(..) + | ty::Projection(..) + | ty::Foreign(..) + | ty::Param(..) + | ty::Closure(..) + | ty::GeneratorWitness(..) + | ty::Opaque(..) => t.super_fold_with(self), + + ty::Placeholder(..) | ty::Bound(..) => bug!("unexpected type {:?}", t), + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + match ct.kind() { + ty::ConstKind::Infer(ty::InferConst::Var(v)) => { + let opt_ct = self + .infcx + .inner + .borrow_mut() + .const_unification_table() + .probe_value(v) + .val + .known(); + self.freshen_const(opt_ct, ty::InferConst::Var(v), ty::InferConst::Fresh, ct.ty()) + } + ty::ConstKind::Infer(ty::InferConst::Fresh(i)) => { + if i >= self.const_freshen_count { + bug!( + "Encountered a freshend const with id {} \ + but our counter is only at {}", + i, + self.const_freshen_count, + ); + } + ct + } + + ty::ConstKind::Bound(..) | ty::ConstKind::Placeholder(_) => { + bug!("unexpected const {:?}", ct) + } + + ty::ConstKind::Param(_) + | ty::ConstKind::Value(_) + | ty::ConstKind::Unevaluated(..) + | ty::ConstKind::Error(_) => ct.super_fold_with(self), + } + } +} diff --git a/compiler/rustc_infer/src/infer/fudge.rs b/compiler/rustc_infer/src/infer/fudge.rs new file mode 100644 index 000000000..2f0eadce6 --- /dev/null +++ b/compiler/rustc_infer/src/infer/fudge.rs @@ -0,0 +1,246 @@ +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::{self, ConstVid, FloatVid, IntVid, RegionVid, Ty, TyCtxt, TyVid}; + +use super::type_variable::TypeVariableOrigin; +use super::InferCtxt; +use super::{ConstVariableOrigin, RegionVariableOrigin, UnificationTable}; + +use rustc_data_structures::snapshot_vec as sv; +use rustc_data_structures::unify as ut; +use ut::UnifyKey; + +use std::ops::Range; + +fn vars_since_snapshot<'tcx, T>( + table: &mut UnificationTable<'_, 'tcx, T>, + snapshot_var_len: usize, +) -> Range<T> +where + T: UnifyKey, + super::UndoLog<'tcx>: From<sv::UndoLog<ut::Delegate<T>>>, +{ + T::from_index(snapshot_var_len as u32)..T::from_index(table.len() as u32) +} + +fn const_vars_since_snapshot<'tcx>( + table: &mut UnificationTable<'_, 'tcx, ConstVid<'tcx>>, + snapshot_var_len: usize, +) -> (Range<ConstVid<'tcx>>, Vec<ConstVariableOrigin>) { + let range = vars_since_snapshot(table, snapshot_var_len); + ( + range.start..range.end, + (range.start.index..range.end.index) + .map(|index| table.probe_value(ConstVid::from_index(index)).origin) + .collect(), + ) +} + +struct VariableLengths { + type_var_len: usize, + const_var_len: usize, + int_var_len: usize, + float_var_len: usize, + region_constraints_len: usize, +} + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + fn variable_lengths(&self) -> VariableLengths { + let mut inner = self.inner.borrow_mut(); + VariableLengths { + type_var_len: inner.type_variables().num_vars(), + const_var_len: inner.const_unification_table().len(), + int_var_len: inner.int_unification_table().len(), + float_var_len: inner.float_unification_table().len(), + region_constraints_len: inner.unwrap_region_constraints().num_region_vars(), + } + } + + /// This rather funky routine is used while processing expected + /// types. What happens here is that we want to propagate a + /// coercion through the return type of a fn to its + /// argument. Consider the type of `Option::Some`, which is + /// basically `for<T> fn(T) -> Option<T>`. So if we have an + /// expression `Some(&[1, 2, 3])`, and that has the expected type + /// `Option<&[u32]>`, we would like to type check `&[1, 2, 3]` + /// with the expectation of `&[u32]`. This will cause us to coerce + /// from `&[u32; 3]` to `&[u32]` and make the users life more + /// pleasant. + /// + /// The way we do this is using `fudge_inference_if_ok`. What the + /// routine actually does is to start a snapshot and execute the + /// closure `f`. In our example above, what this closure will do + /// is to unify the expectation (`Option<&[u32]>`) with the actual + /// return type (`Option<?T>`, where `?T` represents the variable + /// instantiated for `T`). This will cause `?T` to be unified + /// with `&?a [u32]`, where `?a` is a fresh lifetime variable. The + /// input type (`?T`) is then returned by `f()`. + /// + /// At this point, `fudge_inference_if_ok` will normalize all type + /// variables, converting `?T` to `&?a [u32]` and end the + /// snapshot. The problem is that we can't just return this type + /// out, because it references the region variable `?a`, and that + /// region variable was popped when we popped the snapshot. + /// + /// So what we do is to keep a list (`region_vars`, in the code below) + /// of region variables created during the snapshot (here, `?a`). We + /// fold the return value and replace any such regions with a *new* + /// region variable (e.g., `?b`) and return the result (`&?b [u32]`). + /// This can then be used as the expectation for the fn argument. + /// + /// The important point here is that, for soundness purposes, the + /// regions in question are not particularly important. We will + /// use the expected types to guide coercions, but we will still + /// type-check the resulting types from those coercions against + /// the actual types (`?T`, `Option<?T>`) -- and remember that + /// after the snapshot is popped, the variable `?T` is no longer + /// unified. + #[instrument(skip(self, f), level = "debug")] + pub fn fudge_inference_if_ok<T, E, F>(&self, f: F) -> Result<T, E> + where + F: FnOnce() -> Result<T, E>, + T: TypeFoldable<'tcx>, + { + let variable_lengths = self.variable_lengths(); + let (mut fudger, value) = self.probe(|_| { + match f() { + Ok(value) => { + let value = self.resolve_vars_if_possible(value); + + // At this point, `value` could in principle refer + // to inference variables that have been created during + // the snapshot. Once we exit `probe()`, those are + // going to be popped, so we will have to + // eliminate any references to them. + + let mut inner = self.inner.borrow_mut(); + let type_vars = + inner.type_variables().vars_since_snapshot(variable_lengths.type_var_len); + let int_vars = vars_since_snapshot( + &mut inner.int_unification_table(), + variable_lengths.int_var_len, + ); + let float_vars = vars_since_snapshot( + &mut inner.float_unification_table(), + variable_lengths.float_var_len, + ); + let region_vars = inner + .unwrap_region_constraints() + .vars_since_snapshot(variable_lengths.region_constraints_len); + let const_vars = const_vars_since_snapshot( + &mut inner.const_unification_table(), + variable_lengths.const_var_len, + ); + + let fudger = InferenceFudger { + infcx: self, + type_vars, + int_vars, + float_vars, + region_vars, + const_vars, + }; + + Ok((fudger, value)) + } + Err(e) => Err(e), + } + })?; + + // At this point, we need to replace any of the now-popped + // type/region variables that appear in `value` with a fresh + // variable of the appropriate kind. We can't do this during + // the probe because they would just get popped then too. =) + + // Micro-optimization: if no variables have been created, then + // `value` can't refer to any of them. =) So we can just return it. + if fudger.type_vars.0.is_empty() + && fudger.int_vars.is_empty() + && fudger.float_vars.is_empty() + && fudger.region_vars.0.is_empty() + && fudger.const_vars.0.is_empty() + { + Ok(value) + } else { + Ok(value.fold_with(&mut fudger)) + } + } +} + +pub struct InferenceFudger<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + type_vars: (Range<TyVid>, Vec<TypeVariableOrigin>), + int_vars: Range<IntVid>, + float_vars: Range<FloatVid>, + region_vars: (Range<RegionVid>, Vec<RegionVariableOrigin>), + const_vars: (Range<ConstVid<'tcx>>, Vec<ConstVariableOrigin>), +} + +impl<'a, 'tcx> TypeFolder<'tcx> for InferenceFudger<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + match *ty.kind() { + ty::Infer(ty::InferTy::TyVar(vid)) => { + if self.type_vars.0.contains(&vid) { + // This variable was created during the fudging. + // Recreate it with a fresh variable here. + let idx = (vid.as_usize() - self.type_vars.0.start.as_usize()) as usize; + let origin = self.type_vars.1[idx]; + self.infcx.next_ty_var(origin) + } else { + // This variable was created before the + // "fudging". Since we refresh all type + // variables to their binding anyhow, we know + // that it is unbound, so we can just return + // it. + debug_assert!( + self.infcx.inner.borrow_mut().type_variables().probe(vid).is_unknown() + ); + ty + } + } + ty::Infer(ty::InferTy::IntVar(vid)) => { + if self.int_vars.contains(&vid) { + self.infcx.next_int_var() + } else { + ty + } + } + ty::Infer(ty::InferTy::FloatVar(vid)) => { + if self.float_vars.contains(&vid) { + self.infcx.next_float_var() + } else { + ty + } + } + _ => ty.super_fold_with(self), + } + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + if let ty::ReVar(vid) = *r && self.region_vars.0.contains(&vid) { + let idx = vid.index() - self.region_vars.0.start.index(); + let origin = self.region_vars.1[idx]; + return self.infcx.next_region_var(origin); + } + r + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if let ty::ConstKind::Infer(ty::InferConst::Var(vid)) = ct.kind() { + if self.const_vars.0.contains(&vid) { + // This variable was created during the fudging. + // Recreate it with a fresh variable here. + let idx = (vid.index - self.const_vars.0.start.index) as usize; + let origin = self.const_vars.1[idx]; + self.infcx.next_const_var(ct.ty(), origin) + } else { + ct + } + } else { + ct.super_fold_with(self) + } + } +} diff --git a/compiler/rustc_infer/src/infer/glb.rs b/compiler/rustc_infer/src/infer/glb.rs new file mode 100644 index 000000000..1570a08f3 --- /dev/null +++ b/compiler/rustc_infer/src/infer/glb.rs @@ -0,0 +1,144 @@ +//! Greatest lower bound. See [`lattice`]. + +use super::combine::CombineFields; +use super::lattice::{self, LatticeDir}; +use super::InferCtxt; +use super::Subtype; + +use crate::infer::combine::ConstEquateRelation; +use crate::traits::{ObligationCause, PredicateObligation}; +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +/// "Greatest lower bound" (common subtype) +pub struct Glb<'combine, 'infcx, 'tcx> { + fields: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, +} + +impl<'combine, 'infcx, 'tcx> Glb<'combine, 'infcx, 'tcx> { + pub fn new( + fields: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, + ) -> Glb<'combine, 'infcx, 'tcx> { + Glb { fields, a_is_expected } + } +} + +impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> { + fn tag(&self) -> &'static str { + "Glb" + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.fields.tcx() + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.fields.param_env + } + + fn a_is_expected(&self) -> bool { + self.a_is_expected + } + + fn relate_with_variance<T: Relate<'tcx>>( + &mut self, + variance: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + match variance { + ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), + ty::Covariant => self.relate(a, b), + // FIXME(#41044) -- not correct, need test + ty::Bivariant => Ok(a), + ty::Contravariant => self.fields.lub(self.a_is_expected).relate(a, b), + } + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + lattice::super_lattice_tys(self, a, b) + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("{}.regions({:?}, {:?})", self.tag(), a, b); + + let origin = Subtype(Box::new(self.fields.trace.clone())); + Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().glb_regions( + self.tcx(), + origin, + a, + b, + )) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + self.fields.infcx.super_combine_consts(self, a, b) + } + + fn binders<T>( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + debug!("binders(a={:?}, b={:?})", a, b); + if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { + // When higher-ranked types are involved, computing the GLB is + // very challenging, switch to invariance. This is obviously + // overly conservative but works ok in practice. + self.relate_with_variance( + ty::Variance::Invariant, + ty::VarianceDiagInfo::default(), + a, + b, + )?; + Ok(a) + } else { + Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) + } + } +} + +impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx, 'tcx> { + fn infcx(&self) -> &'infcx InferCtxt<'infcx, 'tcx> { + self.fields.infcx + } + + fn cause(&self) -> &ObligationCause<'tcx> { + &self.fields.trace.cause + } + + fn add_obligations(&mut self, obligations: Vec<PredicateObligation<'tcx>>) { + self.fields.obligations.extend(obligations) + } + + fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { + let mut sub = self.fields.sub(self.a_is_expected); + sub.relate(v, a)?; + sub.relate(v, b)?; + Ok(()) + } + + fn define_opaque_types(&self) -> bool { + self.fields.define_opaque_types + } +} + +impl<'tcx> ConstEquateRelation<'tcx> for Glb<'_, '_, 'tcx> { + fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { + self.fields.add_const_equate_obligation(self.a_is_expected, a, b); + } +} diff --git a/compiler/rustc_infer/src/infer/higher_ranked/README.md b/compiler/rustc_infer/src/infer/higher_ranked/README.md new file mode 100644 index 000000000..533d0ef7e --- /dev/null +++ b/compiler/rustc_infer/src/infer/higher_ranked/README.md @@ -0,0 +1,8 @@ +To learn more about how Higher-ranked trait bounds work in the _old_ trait +solver, see [this chapter][oldhrtb] of the rustc-dev-guide. + +To learn more about how they work in the _new_ trait solver, see [this +chapter][newhrtb]. + +[oldhrtb]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html +[newhrtb]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference.html#placeholders-and-universes diff --git a/compiler/rustc_infer/src/infer/higher_ranked/mod.rs b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs new file mode 100644 index 000000000..d0d9efe15 --- /dev/null +++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs @@ -0,0 +1,137 @@ +//! Helper routines for higher-ranked things. See the `doc` module at +//! the end of the file for details. + +use super::combine::CombineFields; +use super::{HigherRankedType, InferCtxt}; +use crate::infer::CombinedSnapshot; +use rustc_middle::ty::fold::FnMutDelegate; +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, Binder, TypeFoldable}; + +impl<'a, 'tcx> CombineFields<'a, 'tcx> { + /// Checks whether `for<..> sub <: for<..> sup` holds. + /// + /// For this to hold, **all** instantiations of the super type + /// have to be a super type of **at least one** instantiation of + /// the subtype. + /// + /// This is implemented by first entering a new universe. + /// We then replace all bound variables in `sup` with placeholders, + /// and all bound variables in `sub` with inference vars. + /// We can then just relate the two resulting types as normal. + /// + /// Note: this is a subtle algorithm. For a full explanation, please see + /// the [rustc dev guide][rd] + /// + /// [rd]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html + #[instrument(skip(self), level = "debug")] + pub fn higher_ranked_sub<T>( + &mut self, + sub: Binder<'tcx, T>, + sup: Binder<'tcx, T>, + sub_is_expected: bool, + ) -> RelateResult<'tcx, ()> + where + T: Relate<'tcx>, + { + let span = self.trace.cause.span; + // First, we instantiate each bound region in the supertype with a + // fresh placeholder region. Note that this automatically creates + // a new universe if needed. + let sup_prime = self.infcx.replace_bound_vars_with_placeholders(sup); + + // Next, we instantiate each bound region in the subtype + // with a fresh region variable. These region variables -- + // but no other pre-existing region variables -- can name + // the placeholders. + let sub_prime = self.infcx.replace_bound_vars_with_fresh_vars(span, HigherRankedType, sub); + + debug!("a_prime={:?}", sub_prime); + debug!("b_prime={:?}", sup_prime); + + // Compare types now that bound regions have been replaced. + let result = self.sub(sub_is_expected).relate(sub_prime, sup_prime)?; + + debug!("OK result={result:?}"); + // NOTE: returning the result here would be dangerous as it contains + // placeholders which **must not** be named afterwards. + Ok(()) + } +} + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + /// Replaces all bound variables (lifetimes, types, and constants) bound by + /// `binder` with placeholder variables in a new universe. This means that the + /// new placeholders can only be named by inference variables created after + /// this method has been called. + /// + /// This is the first step of checking subtyping when higher-ranked things are involved. + /// For more details visit the relevant sections of the [rustc dev guide]. + /// + /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html + #[instrument(level = "debug", skip(self))] + pub fn replace_bound_vars_with_placeholders<T>(&self, binder: ty::Binder<'tcx, T>) -> T + where + T: TypeFoldable<'tcx> + Copy, + { + if let Some(inner) = binder.no_bound_vars() { + return inner; + } + + let next_universe = self.create_next_universe(); + + let delegate = FnMutDelegate { + regions: |br: ty::BoundRegion| { + self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion { + universe: next_universe, + name: br.kind, + })) + }, + types: |bound_ty: ty::BoundTy| { + self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + universe: next_universe, + name: bound_ty.var, + })) + }, + consts: |bound_var: ty::BoundVar, ty| { + self.tcx.mk_const(ty::ConstS { + kind: ty::ConstKind::Placeholder(ty::PlaceholderConst { + universe: next_universe, + name: bound_var, + }), + ty, + }) + }, + }; + + let result = self.tcx.replace_bound_vars_uncached(binder, delegate); + debug!(?next_universe, ?result); + result + } + + /// See [RegionConstraintCollector::leak_check][1]. + /// + /// [1]: crate::infer::region_constraints::RegionConstraintCollector::leak_check + pub fn leak_check( + &self, + overly_polymorphic: bool, + snapshot: &CombinedSnapshot<'_, 'tcx>, + ) -> RelateResult<'tcx, ()> { + // If the user gave `-Zno-leak-check`, or we have been + // configured to skip the leak check, then skip the leak check + // completely. The leak check is deprecated. Any legitimate + // subtyping errors that it would have caught will now be + // caught later on, during region checking. However, we + // continue to use it for a transition period. + if self.tcx.sess.opts.unstable_opts.no_leak_check || self.skip_leak_check.get() { + return Ok(()); + } + + self.inner.borrow_mut().unwrap_region_constraints().leak_check( + self.tcx, + overly_polymorphic, + self.universe(), + snapshot, + ) + } +} diff --git a/compiler/rustc_infer/src/infer/lattice.rs b/compiler/rustc_infer/src/infer/lattice.rs new file mode 100644 index 000000000..1e3293efa --- /dev/null +++ b/compiler/rustc_infer/src/infer/lattice.rs @@ -0,0 +1,124 @@ +//! # Lattice variables +//! +//! Generic code for operating on [lattices] of inference variables +//! that are characterized by an upper- and lower-bound. +//! +//! The code is defined quite generically so that it can be +//! applied both to type variables, which represent types being inferred, +//! and fn variables, which represent function types being inferred. +//! (It may eventually be applied to their types as well.) +//! In some cases, the functions are also generic with respect to the +//! operation on the lattice (GLB vs LUB). +//! +//! ## Note +//! +//! Although all the functions are generic, for simplicity, comments in the source code +//! generally refer to type variables and the LUB operation. +//! +//! [lattices]: https://en.wikipedia.org/wiki/Lattice_(order) + +use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use super::InferCtxt; + +use crate::traits::{ObligationCause, PredicateObligation}; +use rustc_middle::ty::relate::{RelateResult, TypeRelation}; +use rustc_middle::ty::TyVar; +use rustc_middle::ty::{self, Ty}; + +/// Trait for returning data about a lattice, and for abstracting +/// over the "direction" of the lattice operation (LUB/GLB). +/// +/// GLB moves "down" the lattice (to smaller values); LUB moves +/// "up" the lattice (to bigger values). +pub trait LatticeDir<'f, 'tcx>: TypeRelation<'tcx> { + fn infcx(&self) -> &'f InferCtxt<'f, 'tcx>; + + fn cause(&self) -> &ObligationCause<'tcx>; + + fn add_obligations(&mut self, obligations: Vec<PredicateObligation<'tcx>>); + + fn define_opaque_types(&self) -> bool; + + // Relates the type `v` to `a` and `b` such that `v` represents + // the LUB/GLB of `a` and `b` as appropriate. + // + // Subtle hack: ordering *may* be significant here. This method + // relates `v` to `a` first, which may help us to avoid unnecessary + // type variable obligations. See caller for details. + fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()>; +} + +/// Relates two types using a given lattice. +#[instrument(skip(this), level = "debug")] +pub fn super_lattice_tys<'a, 'tcx: 'a, L>( + this: &mut L, + a: Ty<'tcx>, + b: Ty<'tcx>, +) -> RelateResult<'tcx, Ty<'tcx>> +where + L: LatticeDir<'a, 'tcx>, +{ + debug!("{}", this.tag()); + + if a == b { + return Ok(a); + } + + let infcx = this.infcx(); + + let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); + let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); + + match (a.kind(), b.kind()) { + // If one side is known to be a variable and one is not, + // create a variable (`v`) to represent the LUB. Make sure to + // relate `v` to the non-type-variable first (by passing it + // first to `relate_bound`). Otherwise, we would produce a + // subtype obligation that must then be processed. + // + // Example: if the LHS is a type variable, and RHS is + // `Box<i32>`, then we current compare `v` to the RHS first, + // which will instantiate `v` with `Box<i32>`. Then when `v` + // is compared to the LHS, we instantiate LHS with `Box<i32>`. + // But if we did in reverse order, we would create a `v <: + // LHS` (or vice versa) constraint and then instantiate + // `v`. This would require further processing to achieve same + // end-result; in particular, this screws up some of the logic + // in coercion, which expects LUB to figure out that the LHS + // is (e.g.) `Box<i32>`. A more obvious solution might be to + // iterate on the subtype obligations that are returned, but I + // think this suffices. -nmatsakis + (&ty::Infer(TyVar(..)), _) => { + let v = infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::LatticeVariable, + span: this.cause().span, + }); + this.relate_bound(v, b, a)?; + Ok(v) + } + (_, &ty::Infer(TyVar(..))) => { + let v = infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::LatticeVariable, + span: this.cause().span, + }); + this.relate_bound(v, a, b)?; + Ok(v) + } + + (&ty::Opaque(a_def_id, _), &ty::Opaque(b_def_id, _)) if a_def_id == b_def_id => { + infcx.super_combine_tys(this, a, b) + } + (&ty::Opaque(did, ..), _) | (_, &ty::Opaque(did, ..)) + if this.define_opaque_types() && did.is_local() => + { + this.add_obligations( + infcx + .handle_opaque_type(a, b, this.a_is_expected(), this.cause(), this.param_env())? + .obligations, + ); + Ok(a) + } + + _ => infcx.super_combine_tys(this, a, b), + } +} diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/README.md b/compiler/rustc_infer/src/infer/lexical_region_resolve/README.md new file mode 100644 index 000000000..0a7da8c80 --- /dev/null +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/README.md @@ -0,0 +1,6 @@ +Lexical Region Resolution was removed in https://github.com/rust-lang/rust/pull/64790. + +Rust now uses Non-lexical lifetimes. For more info, please see the [borrowck +chapter][bc] in the rustc-dev-guide. + +[bc]: https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference.html diff --git a/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs new file mode 100644 index 000000000..3783cfb4c --- /dev/null +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -0,0 +1,891 @@ +//! Lexical region resolution. + +use crate::infer::region_constraints::Constraint; +use crate::infer::region_constraints::GenericKind; +use crate::infer::region_constraints::RegionConstraintData; +use crate::infer::region_constraints::VarInfos; +use crate::infer::region_constraints::VerifyBound; +use crate::infer::RegionRelations; +use crate::infer::RegionVariableOrigin; +use crate::infer::SubregionOrigin; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::graph::implementation::{ + Direction, Graph, NodeIndex, INCOMING, OUTGOING, +}; +use rustc_data_structures::intern::Interned; +use rustc_index::vec::{Idx, IndexVec}; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{ReEarlyBound, ReEmpty, ReErased, ReFree, ReStatic}; +use rustc_middle::ty::{ReLateBound, RePlaceholder, ReVar}; +use rustc_middle::ty::{Region, RegionVid}; +use rustc_span::Span; +use std::fmt; + +use super::outlives::test_type_match; + +/// This function performs lexical region resolution given a complete +/// set of constraints and variable origins. It performs a fixed-point +/// iteration to find region values which satisfy all constraints, +/// assuming such values can be found. It returns the final values of +/// all the variables as well as a set of errors that must be reported. +#[instrument(level = "debug", skip(region_rels, var_infos, data))] +pub(crate) fn resolve<'tcx>( + param_env: ty::ParamEnv<'tcx>, + region_rels: &RegionRelations<'_, 'tcx>, + var_infos: VarInfos, + data: RegionConstraintData<'tcx>, +) -> (LexicalRegionResolutions<'tcx>, Vec<RegionResolutionError<'tcx>>) { + let mut errors = vec![]; + let mut resolver = LexicalResolver { param_env, region_rels, var_infos, data }; + let values = resolver.infer_variable_values(&mut errors); + (values, errors) +} + +/// Contains the result of lexical region resolution. Offers methods +/// to lookup up the final value of a region variable. +#[derive(Clone)] +pub struct LexicalRegionResolutions<'tcx> { + pub(crate) values: IndexVec<RegionVid, VarValue<'tcx>>, +} + +#[derive(Copy, Clone, Debug)] +pub(crate) enum VarValue<'tcx> { + Value(Region<'tcx>), + ErrorValue, +} + +#[derive(Clone, Debug)] +pub enum RegionResolutionError<'tcx> { + /// `ConcreteFailure(o, a, b)`: + /// + /// `o` requires that `a <= b`, but this does not hold + ConcreteFailure(SubregionOrigin<'tcx>, Region<'tcx>, Region<'tcx>), + + /// `GenericBoundFailure(p, s, a) + /// + /// The parameter/associated-type `p` must be known to outlive the lifetime + /// `a` (but none of the known bounds are sufficient). + GenericBoundFailure(SubregionOrigin<'tcx>, GenericKind<'tcx>, Region<'tcx>), + + /// `SubSupConflict(v, v_origin, sub_origin, sub_r, sup_origin, sup_r)`: + /// + /// Could not infer a value for `v` (which has origin `v_origin`) + /// because `sub_r <= v` (due to `sub_origin`) but `v <= sup_r` (due to `sup_origin`) and + /// `sub_r <= sup_r` does not hold. + SubSupConflict( + RegionVid, + RegionVariableOrigin, + SubregionOrigin<'tcx>, + Region<'tcx>, + SubregionOrigin<'tcx>, + Region<'tcx>, + Vec<Span>, // All the influences on a given value that didn't meet its constraints. + ), + + /// Indicates a `'b: 'a` constraint where `'a` is in a universe that + /// cannot name the placeholder `'b`. + UpperBoundUniverseConflict( + RegionVid, + RegionVariableOrigin, + ty::UniverseIndex, // the universe index of the region variable + SubregionOrigin<'tcx>, // cause of the constraint + Region<'tcx>, // the placeholder `'b` + ), +} + +struct RegionAndOrigin<'tcx> { + region: Region<'tcx>, + origin: SubregionOrigin<'tcx>, +} + +type RegionGraph<'tcx> = Graph<(), Constraint<'tcx>>; + +struct LexicalResolver<'cx, 'tcx> { + param_env: ty::ParamEnv<'tcx>, + region_rels: &'cx RegionRelations<'cx, 'tcx>, + var_infos: VarInfos, + data: RegionConstraintData<'tcx>, +} + +impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.region_rels.tcx + } + + fn infer_variable_values( + &mut self, + errors: &mut Vec<RegionResolutionError<'tcx>>, + ) -> LexicalRegionResolutions<'tcx> { + let mut var_data = self.construct_var_data(self.tcx()); + + if cfg!(debug_assertions) { + self.dump_constraints(); + } + + let graph = self.construct_graph(); + self.expand_givens(&graph); + self.expansion(&mut var_data); + self.collect_errors(&mut var_data, errors); + self.collect_var_errors(&var_data, &graph, errors); + var_data + } + + fn num_vars(&self) -> usize { + self.var_infos.len() + } + + /// Initially, the value for all variables is set to `'empty`, the + /// empty region. The `expansion` phase will grow this larger. + fn construct_var_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> { + LexicalRegionResolutions { + values: IndexVec::from_fn_n( + |vid| { + let vid_universe = self.var_infos[vid].universe; + let re_empty = tcx.mk_region(ty::ReEmpty(vid_universe)); + VarValue::Value(re_empty) + }, + self.num_vars(), + ), + } + } + + #[instrument(level = "debug", skip(self))] + fn dump_constraints(&self) { + for (idx, (constraint, _)) in self.data.constraints.iter().enumerate() { + debug!("Constraint {} => {:?}", idx, constraint); + } + } + + fn expand_givens(&mut self, graph: &RegionGraph<'_>) { + // Givens are a kind of horrible hack to account for + // constraints like 'c <= '0 that are known to hold due to + // closure signatures (see the comment above on the `givens` + // field). They should go away. But until they do, the role + // of this fn is to account for the transitive nature: + // + // Given 'c <= '0 + // and '0 <= '1 + // then 'c <= '1 + + let seeds: Vec<_> = self.data.givens.iter().cloned().collect(); + for (r, vid) in seeds { + // While all things transitively reachable in the graph + // from the variable (`'0` in the example above). + let seed_index = NodeIndex(vid.index() as usize); + for succ_index in graph.depth_traverse(seed_index, OUTGOING) { + let succ_index = succ_index.0; + + // The first N nodes correspond to the region + // variables. Other nodes correspond to constant + // regions. + if succ_index < self.num_vars() { + let succ_vid = RegionVid::new(succ_index); + + // Add `'c <= '1`. + self.data.givens.insert((r, succ_vid)); + } + } + } + } + + fn expansion(&self, var_values: &mut LexicalRegionResolutions<'tcx>) { + let mut constraints = IndexVec::from_elem_n(Vec::new(), var_values.values.len()); + let mut changes = Vec::new(); + for constraint in self.data.constraints.keys() { + let (a_vid, a_region, b_vid, b_data) = match *constraint { + Constraint::RegSubVar(a_region, b_vid) => { + let b_data = var_values.value_mut(b_vid); + (None, a_region, b_vid, b_data) + } + Constraint::VarSubVar(a_vid, b_vid) => match *var_values.value(a_vid) { + VarValue::ErrorValue => continue, + VarValue::Value(a_region) => { + let b_data = var_values.value_mut(b_vid); + (Some(a_vid), a_region, b_vid, b_data) + } + }, + Constraint::RegSubReg(..) | Constraint::VarSubReg(..) => { + // These constraints are checked after expansion + // is done, in `collect_errors`. + continue; + } + }; + if self.expand_node(a_region, b_vid, b_data) { + changes.push(b_vid); + } + if let Some(a_vid) = a_vid { + match b_data { + VarValue::Value(Region(Interned(ReStatic, _))) | VarValue::ErrorValue => (), + _ => { + constraints[a_vid].push((a_vid, b_vid)); + constraints[b_vid].push((a_vid, b_vid)); + } + } + } + } + + while let Some(vid) = changes.pop() { + constraints[vid].retain(|&(a_vid, b_vid)| { + let VarValue::Value(a_region) = *var_values.value(a_vid) else { + return false; + }; + let b_data = var_values.value_mut(b_vid); + if self.expand_node(a_region, b_vid, b_data) { + changes.push(b_vid); + } + !matches!( + b_data, + VarValue::Value(Region(Interned(ReStatic, _))) | VarValue::ErrorValue + ) + }); + } + } + + fn expand_node( + &self, + a_region: Region<'tcx>, + b_vid: RegionVid, + b_data: &mut VarValue<'tcx>, + ) -> bool { + debug!("expand_node({:?}, {:?} == {:?})", a_region, b_vid, b_data); + + match *a_region { + // Check if this relationship is implied by a given. + ty::ReEarlyBound(_) | ty::ReFree(_) => { + if self.data.givens.contains(&(a_region, b_vid)) { + debug!("given"); + return false; + } + } + + _ => {} + } + + match *b_data { + VarValue::Value(cur_region) => { + // This is a specialized version of the `lub_concrete_regions` + // check below for a common case, here purely as an + // optimization. + let b_universe = self.var_infos[b_vid].universe; + if let ReEmpty(a_universe) = *a_region && a_universe == b_universe { + return false; + } + + let mut lub = self.lub_concrete_regions(a_region, cur_region); + if lub == cur_region { + return false; + } + + // Watch out for `'b: !1` relationships, where the + // universe of `'b` can't name the placeholder `!1`. In + // that case, we have to grow `'b` to be `'static` for the + // relationship to hold. This is obviously a kind of sub-optimal + // choice -- in the future, when we incorporate a knowledge + // of the parameter environment, we might be able to find a + // tighter bound than `'static`. + // + // (This might e.g. arise from being asked to prove `for<'a> { 'b: 'a }`.) + if let ty::RePlaceholder(p) = *lub && b_universe.cannot_name(p.universe) { + lub = self.tcx().lifetimes.re_static; + } + + debug!("Expanding value of {:?} from {:?} to {:?}", b_vid, cur_region, lub); + + *b_data = VarValue::Value(lub); + true + } + + VarValue::ErrorValue => false, + } + } + + /// True if `a <= b`, but not defined over inference variables. + #[instrument(level = "trace", skip(self))] + fn sub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> bool { + let tcx = self.tcx(); + let sub_free_regions = |r1, r2| self.region_rels.free_regions.sub_free_regions(tcx, r1, r2); + + // Check for the case where we know that `'b: 'static` -- in that case, + // `a <= b` for all `a`. + let b_free_or_static = b.is_free_or_static(); + if b_free_or_static && sub_free_regions(tcx.lifetimes.re_static, b) { + return true; + } + + // If both `a` and `b` are free, consult the declared + // relationships. Note that this can be more precise than the + // `lub` relationship defined below, since sometimes the "lub" + // is actually the `postdom_upper_bound` (see + // `TransitiveRelation` for more details). + let a_free_or_static = a.is_free_or_static(); + if a_free_or_static && b_free_or_static { + return sub_free_regions(a, b); + } + + // For other cases, leverage the LUB code to find the LUB and + // check if it is equal to `b`. + self.lub_concrete_regions(a, b) == b + } + + /// Returns the least-upper-bound of `a` and `b`; i.e., the + /// smallest region `c` such that `a <= c` and `b <= c`. + /// + /// Neither `a` nor `b` may be an inference variable (hence the + /// term "concrete regions"). + #[instrument(level = "trace", skip(self))] + fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> { + let r = match (*a, *b) { + (ReLateBound(..), _) | (_, ReLateBound(..)) | (ReErased, _) | (_, ReErased) => { + bug!("cannot relate region: LUB({:?}, {:?})", a, b); + } + + (ReVar(v_id), _) | (_, ReVar(v_id)) => { + span_bug!( + self.var_infos[v_id].origin.span(), + "lub_concrete_regions invoked with non-concrete \ + regions: {:?}, {:?}", + a, + b + ); + } + + (ReStatic, _) | (_, ReStatic) => { + // nothing lives longer than `'static` + self.tcx().lifetimes.re_static + } + + (ReEmpty(_), ReEarlyBound(_) | ReFree(_)) => { + // All empty regions are less than early-bound, free, + // and scope regions. + b + } + + (ReEarlyBound(_) | ReFree(_), ReEmpty(_)) => { + // All empty regions are less than early-bound, free, + // and scope regions. + a + } + + (ReEmpty(a_ui), ReEmpty(b_ui)) => { + // Empty regions are ordered according to the universe + // they are associated with. + let ui = a_ui.min(b_ui); + self.tcx().mk_region(ReEmpty(ui)) + } + + (ReEmpty(empty_ui), RePlaceholder(placeholder)) + | (RePlaceholder(placeholder), ReEmpty(empty_ui)) => { + // If this empty region is from a universe that can + // name the placeholder, then the placeholder is + // larger; otherwise, the only ancestor is `'static`. + if empty_ui.can_name(placeholder.universe) { + self.tcx().mk_region(RePlaceholder(placeholder)) + } else { + self.tcx().lifetimes.re_static + } + } + + (ReEarlyBound(_) | ReFree(_), ReEarlyBound(_) | ReFree(_)) => { + self.region_rels.lub_free_regions(a, b) + } + + // For these types, we cannot define any additional + // relationship: + (RePlaceholder(..), _) | (_, RePlaceholder(..)) => { + if a == b { + a + } else { + self.tcx().lifetimes.re_static + } + } + }; + + debug!("lub_concrete_regions({:?}, {:?}) = {:?}", a, b, r); + + r + } + + /// After expansion is complete, go and check upper bounds (i.e., + /// cases where the region cannot grow larger than a fixed point) + /// and check that they are satisfied. + #[instrument(skip(self, var_data, errors))] + fn collect_errors( + &self, + var_data: &mut LexicalRegionResolutions<'tcx>, + errors: &mut Vec<RegionResolutionError<'tcx>>, + ) { + for (constraint, origin) in &self.data.constraints { + debug!(?constraint, ?origin); + match *constraint { + Constraint::RegSubVar(..) | Constraint::VarSubVar(..) => { + // Expansion will ensure that these constraints hold. Ignore. + } + + Constraint::RegSubReg(sub, sup) => { + if self.sub_concrete_regions(sub, sup) { + continue; + } + + debug!( + "region error at {:?}: \ + cannot verify that {:?} <= {:?}", + origin, sub, sup + ); + + errors.push(RegionResolutionError::ConcreteFailure( + (*origin).clone(), + sub, + sup, + )); + } + + Constraint::VarSubReg(a_vid, b_region) => { + let a_data = var_data.value_mut(a_vid); + debug!("contraction: {:?} == {:?}, {:?}", a_vid, a_data, b_region); + + let VarValue::Value(a_region) = *a_data else { + continue; + }; + + // Do not report these errors immediately: + // instead, set the variable value to error and + // collect them later. + if !self.sub_concrete_regions(a_region, b_region) { + debug!( + "region error at {:?}: \ + cannot verify that {:?}={:?} <= {:?}", + origin, a_vid, a_region, b_region + ); + *a_data = VarValue::ErrorValue; + } + } + } + } + + for verify in &self.data.verifys { + debug!("collect_errors: verify={:?}", verify); + let sub = var_data.normalize(self.tcx(), verify.region); + + let verify_kind_ty = verify.kind.to_ty(self.tcx()); + let verify_kind_ty = var_data.normalize(self.tcx(), verify_kind_ty); + if self.bound_is_met(&verify.bound, var_data, verify_kind_ty, sub) { + continue; + } + + debug!( + "collect_errors: region error at {:?}: \ + cannot verify that {:?} <= {:?}", + verify.origin, verify.region, verify.bound + ); + + errors.push(RegionResolutionError::GenericBoundFailure( + verify.origin.clone(), + verify.kind, + sub, + )); + } + } + + /// Go over the variables that were declared to be error variables + /// and create a `RegionResolutionError` for each of them. + fn collect_var_errors( + &self, + var_data: &LexicalRegionResolutions<'tcx>, + graph: &RegionGraph<'tcx>, + errors: &mut Vec<RegionResolutionError<'tcx>>, + ) { + debug!("collect_var_errors, var_data = {:#?}", var_data.values); + + // This is the best way that I have found to suppress + // duplicate and related errors. Basically we keep a set of + // flags for every node. Whenever an error occurs, we will + // walk some portion of the graph looking to find pairs of + // conflicting regions to report to the user. As we walk, we + // trip the flags from false to true, and if we find that + // we've already reported an error involving any particular + // node we just stop and don't report the current error. The + // idea is to report errors that derive from independent + // regions of the graph, but not those that derive from + // overlapping locations. + let mut dup_vec = IndexVec::from_elem_n(None, self.num_vars()); + + for (node_vid, value) in var_data.values.iter_enumerated() { + match *value { + VarValue::Value(_) => { /* Inference successful */ } + VarValue::ErrorValue => { + // Inference impossible: this value contains + // inconsistent constraints. + // + // I think that in this case we should report an + // error now -- unlike the case above, we can't + // wait to see whether the user needs the result + // of this variable. The reason is that the mere + // existence of this variable implies that the + // region graph is inconsistent, whether or not it + // is used. + // + // For example, we may have created a region + // variable that is the GLB of two other regions + // which do not have a GLB. Even if that variable + // is not used, it implies that those two regions + // *should* have a GLB. + // + // At least I think this is true. It may be that + // the mere existence of a conflict in a region + // variable that is not used is not a problem, so + // if this rule starts to create problems we'll + // have to revisit this portion of the code and + // think hard about it. =) -- nikomatsakis + + // Obtain the spans for all the places that can + // influence the constraints on this value for + // richer diagnostics in `static_impl_trait`. + let influences: Vec<Span> = self + .data + .constraints + .iter() + .filter_map(|(constraint, origin)| match (constraint, origin) { + ( + Constraint::VarSubVar(_, sup), + SubregionOrigin::DataBorrowed(_, sp), + ) if sup == &node_vid => Some(*sp), + _ => None, + }) + .collect(); + + self.collect_error_for_expanding_node( + graph, + &mut dup_vec, + node_vid, + errors, + influences, + ); + } + } + } + } + + fn construct_graph(&self) -> RegionGraph<'tcx> { + let num_vars = self.num_vars(); + + let mut graph = Graph::new(); + + for _ in 0..num_vars { + graph.add_node(()); + } + + // Issue #30438: two distinct dummy nodes, one for incoming + // edges (dummy_source) and another for outgoing edges + // (dummy_sink). In `dummy -> a -> b -> dummy`, using one + // dummy node leads one to think (erroneously) there exists a + // path from `b` to `a`. Two dummy nodes sidesteps the issue. + let dummy_source = graph.add_node(()); + let dummy_sink = graph.add_node(()); + + for constraint in self.data.constraints.keys() { + match *constraint { + Constraint::VarSubVar(a_id, b_id) => { + graph.add_edge( + NodeIndex(a_id.index() as usize), + NodeIndex(b_id.index() as usize), + *constraint, + ); + } + Constraint::RegSubVar(_, b_id) => { + graph.add_edge(dummy_source, NodeIndex(b_id.index() as usize), *constraint); + } + Constraint::VarSubReg(a_id, _) => { + graph.add_edge(NodeIndex(a_id.index() as usize), dummy_sink, *constraint); + } + Constraint::RegSubReg(..) => { + // this would be an edge from `dummy_source` to + // `dummy_sink`; just ignore it. + } + } + } + + graph + } + + fn collect_error_for_expanding_node( + &self, + graph: &RegionGraph<'tcx>, + dup_vec: &mut IndexVec<RegionVid, Option<RegionVid>>, + node_idx: RegionVid, + errors: &mut Vec<RegionResolutionError<'tcx>>, + influences: Vec<Span>, + ) { + // Errors in expanding nodes result from a lower-bound that is + // not contained by an upper-bound. + let (mut lower_bounds, lower_vid_bounds, lower_dup) = + self.collect_bounding_regions(graph, node_idx, INCOMING, Some(dup_vec)); + let (mut upper_bounds, _, upper_dup) = + self.collect_bounding_regions(graph, node_idx, OUTGOING, Some(dup_vec)); + + if lower_dup || upper_dup { + return; + } + + // We place free regions first because we are special casing + // SubSupConflict(ReFree, ReFree) when reporting error, and so + // the user will more likely get a specific suggestion. + fn region_order_key(x: &RegionAndOrigin<'_>) -> u8 { + match *x.region { + ReEarlyBound(_) => 0, + ReFree(_) => 1, + _ => 2, + } + } + lower_bounds.sort_by_key(region_order_key); + upper_bounds.sort_by_key(region_order_key); + + let node_universe = self.var_infos[node_idx].universe; + + for lower_bound in &lower_bounds { + let effective_lower_bound = if let ty::RePlaceholder(p) = *lower_bound.region { + if node_universe.cannot_name(p.universe) { + self.tcx().lifetimes.re_static + } else { + lower_bound.region + } + } else { + lower_bound.region + }; + + for upper_bound in &upper_bounds { + if !self.sub_concrete_regions(effective_lower_bound, upper_bound.region) { + let origin = self.var_infos[node_idx].origin; + debug!( + "region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \ + sup: {:?}", + origin, node_idx, lower_bound.region, upper_bound.region + ); + + errors.push(RegionResolutionError::SubSupConflict( + node_idx, + origin, + lower_bound.origin.clone(), + lower_bound.region, + upper_bound.origin.clone(), + upper_bound.region, + influences, + )); + return; + } + } + } + + // If we have a scenario like `exists<'a> { forall<'b> { 'b: + // 'a } }`, we wind up without any lower-bound -- all we have + // are placeholders as upper bounds, but the universe of the + // variable `'a`, or some variable that `'a` has to outlive, doesn't + // permit those placeholders. + let min_universe = lower_vid_bounds + .into_iter() + .map(|vid| self.var_infos[vid].universe) + .min() + .expect("lower_vid_bounds should at least include `node_idx`"); + + for upper_bound in &upper_bounds { + if let ty::RePlaceholder(p) = *upper_bound.region { + if min_universe.cannot_name(p.universe) { + let origin = self.var_infos[node_idx].origin; + errors.push(RegionResolutionError::UpperBoundUniverseConflict( + node_idx, + origin, + min_universe, + upper_bound.origin.clone(), + upper_bound.region, + )); + return; + } + } + } + + // Errors in earlier passes can yield error variables without + // resolution errors here; delay ICE in favor of those errors. + self.tcx().sess.delay_span_bug( + self.var_infos[node_idx].origin.span(), + &format!( + "collect_error_for_expanding_node() could not find \ + error for var {:?} in universe {:?}, lower_bounds={:#?}, \ + upper_bounds={:#?}", + node_idx, node_universe, lower_bounds, upper_bounds + ), + ); + } + + /// Collects all regions that "bound" the variable `orig_node_idx` in the + /// given direction. + /// + /// If `dup_vec` is `Some` it's used to track duplicates between successive + /// calls of this function. + /// + /// The return tuple fields are: + /// - a list of all concrete regions bounding the given region. + /// - the set of all region variables bounding the given region. + /// - a `bool` that's true if the returned region variables overlap with + /// those returned by a previous call for another region. + fn collect_bounding_regions( + &self, + graph: &RegionGraph<'tcx>, + orig_node_idx: RegionVid, + dir: Direction, + mut dup_vec: Option<&mut IndexVec<RegionVid, Option<RegionVid>>>, + ) -> (Vec<RegionAndOrigin<'tcx>>, FxHashSet<RegionVid>, bool) { + struct WalkState<'tcx> { + set: FxHashSet<RegionVid>, + stack: Vec<RegionVid>, + result: Vec<RegionAndOrigin<'tcx>>, + dup_found: bool, + } + let mut state = WalkState { + set: Default::default(), + stack: vec![orig_node_idx], + result: Vec::new(), + dup_found: false, + }; + state.set.insert(orig_node_idx); + + // to start off the process, walk the source node in the + // direction specified + process_edges(&self.data, &mut state, graph, orig_node_idx, dir); + + while let Some(node_idx) = state.stack.pop() { + // check whether we've visited this node on some previous walk + if let Some(dup_vec) = &mut dup_vec { + if dup_vec[node_idx].is_none() { + dup_vec[node_idx] = Some(orig_node_idx); + } else if dup_vec[node_idx] != Some(orig_node_idx) { + state.dup_found = true; + } + + debug!( + "collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})", + orig_node_idx, node_idx + ); + } + + process_edges(&self.data, &mut state, graph, node_idx, dir); + } + + let WalkState { result, dup_found, set, .. } = state; + return (result, set, dup_found); + + fn process_edges<'tcx>( + this: &RegionConstraintData<'tcx>, + state: &mut WalkState<'tcx>, + graph: &RegionGraph<'tcx>, + source_vid: RegionVid, + dir: Direction, + ) { + debug!("process_edges(source_vid={:?}, dir={:?})", source_vid, dir); + + let source_node_index = NodeIndex(source_vid.index() as usize); + for (_, edge) in graph.adjacent_edges(source_node_index, dir) { + match edge.data { + Constraint::VarSubVar(from_vid, to_vid) => { + let opp_vid = if from_vid == source_vid { to_vid } else { from_vid }; + if state.set.insert(opp_vid) { + state.stack.push(opp_vid); + } + } + + Constraint::RegSubVar(region, _) | Constraint::VarSubReg(_, region) => { + state.result.push(RegionAndOrigin { + region, + origin: this.constraints.get(&edge.data).unwrap().clone(), + }); + } + + Constraint::RegSubReg(..) => panic!( + "cannot reach reg-sub-reg edge in region inference \ + post-processing" + ), + } + } + } + } + + fn bound_is_met( + &self, + bound: &VerifyBound<'tcx>, + var_values: &LexicalRegionResolutions<'tcx>, + generic_ty: Ty<'tcx>, + min: ty::Region<'tcx>, + ) -> bool { + match bound { + VerifyBound::IfEq(verify_if_eq_b) => { + let verify_if_eq_b = var_values.normalize(self.region_rels.tcx, *verify_if_eq_b); + match test_type_match::extract_verify_if_eq( + self.tcx(), + self.param_env, + &verify_if_eq_b, + generic_ty, + ) { + Some(r) => { + self.bound_is_met(&VerifyBound::OutlivedBy(r), var_values, generic_ty, min) + } + + None => false, + } + } + + VerifyBound::OutlivedBy(r) => { + self.sub_concrete_regions(min, var_values.normalize(self.tcx(), *r)) + } + + VerifyBound::IsEmpty => { + matches!(*min, ty::ReEmpty(_)) + } + + VerifyBound::AnyBound(bs) => { + bs.iter().any(|b| self.bound_is_met(b, var_values, generic_ty, min)) + } + + VerifyBound::AllBounds(bs) => { + bs.iter().all(|b| self.bound_is_met(b, var_values, generic_ty, min)) + } + } + } +} + +impl<'tcx> fmt::Debug for RegionAndOrigin<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RegionAndOrigin({:?},{:?})", self.region, self.origin) + } +} + +impl<'tcx> LexicalRegionResolutions<'tcx> { + fn normalize<T>(&self, tcx: TyCtxt<'tcx>, value: T) -> T + where + T: TypeFoldable<'tcx>, + { + tcx.fold_regions(value, |r, _db| self.resolve_region(tcx, r)) + } + + fn value(&self, rid: RegionVid) -> &VarValue<'tcx> { + &self.values[rid] + } + + fn value_mut(&mut self, rid: RegionVid) -> &mut VarValue<'tcx> { + &mut self.values[rid] + } + + pub(crate) fn resolve_region( + &self, + tcx: TyCtxt<'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + let result = match *r { + ty::ReVar(rid) => match self.values[rid] { + VarValue::Value(r) => r, + VarValue::ErrorValue => tcx.lifetimes.re_static, + }, + _ => r, + }; + debug!("resolve_region({:?}) = {:?}", r, result); + result + } +} diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs new file mode 100644 index 000000000..9f96d52c8 --- /dev/null +++ b/compiler/rustc_infer/src/infer/lub.rs @@ -0,0 +1,144 @@ +//! Least upper bound. See [`lattice`]. + +use super::combine::CombineFields; +use super::lattice::{self, LatticeDir}; +use super::InferCtxt; +use super::Subtype; + +use crate::infer::combine::ConstEquateRelation; +use crate::traits::{ObligationCause, PredicateObligation}; +use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +/// "Least upper bound" (common supertype) +pub struct Lub<'combine, 'infcx, 'tcx> { + fields: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, +} + +impl<'combine, 'infcx, 'tcx> Lub<'combine, 'infcx, 'tcx> { + pub fn new( + fields: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, + ) -> Lub<'combine, 'infcx, 'tcx> { + Lub { fields, a_is_expected } + } +} + +impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> { + fn tag(&self) -> &'static str { + "Lub" + } + + fn tcx(&self) -> TyCtxt<'tcx> { + self.fields.tcx() + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.fields.param_env + } + + fn a_is_expected(&self) -> bool { + self.a_is_expected + } + + fn relate_with_variance<T: Relate<'tcx>>( + &mut self, + variance: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + match variance { + ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), + ty::Covariant => self.relate(a, b), + // FIXME(#41044) -- not correct, need test + ty::Bivariant => Ok(a), + ty::Contravariant => self.fields.glb(self.a_is_expected).relate(a, b), + } + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + lattice::super_lattice_tys(self, a, b) + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("{}.regions({:?}, {:?})", self.tag(), a, b); + + let origin = Subtype(Box::new(self.fields.trace.clone())); + Ok(self.fields.infcx.inner.borrow_mut().unwrap_region_constraints().lub_regions( + self.tcx(), + origin, + a, + b, + )) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + self.fields.infcx.super_combine_consts(self, a, b) + } + + fn binders<T>( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + debug!("binders(a={:?}, b={:?})", a, b); + if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { + // When higher-ranked types are involved, computing the LUB is + // very challenging, switch to invariance. This is obviously + // overly conservative but works ok in practice. + self.relate_with_variance( + ty::Variance::Invariant, + ty::VarianceDiagInfo::default(), + a, + b, + )?; + Ok(a) + } else { + Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?)) + } + } +} + +impl<'tcx> ConstEquateRelation<'tcx> for Lub<'_, '_, 'tcx> { + fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { + self.fields.add_const_equate_obligation(self.a_is_expected, a, b); + } +} + +impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx, 'tcx> { + fn infcx(&self) -> &'infcx InferCtxt<'infcx, 'tcx> { + self.fields.infcx + } + + fn cause(&self) -> &ObligationCause<'tcx> { + &self.fields.trace.cause + } + + fn add_obligations(&mut self, obligations: Vec<PredicateObligation<'tcx>>) { + self.fields.obligations.extend(obligations) + } + + fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> { + let mut sub = self.fields.sub(self.a_is_expected); + sub.relate(a, v)?; + sub.relate(b, v)?; + Ok(()) + } + + fn define_opaque_types(&self) -> bool { + self.fields.define_opaque_types + } +} diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs new file mode 100644 index 000000000..d7d1b5fa2 --- /dev/null +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -0,0 +1,2066 @@ +pub use self::freshen::TypeFreshener; +pub use self::lexical_region_resolve::RegionResolutionError; +pub use self::LateBoundRegionConversionTime::*; +pub use self::RegionVariableOrigin::*; +pub use self::SubregionOrigin::*; +pub use self::ValuePairs::*; + +use self::opaque_types::OpaqueTypeStorage; +pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog}; + +use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine, TraitEngineExt}; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::sync::Lrc; +use rustc_data_structures::undo_log::Rollback; +use rustc_data_structures::unify as ut; +use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::infer::canonical::{Canonical, CanonicalVarValues}; +use rustc_middle::infer::unify_key::{ConstVarValue, ConstVariableValue}; +use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind, ToType}; +use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult}; +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}; +use rustc_middle::ty::relate::RelateResult; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef}; +use rustc_middle::ty::visit::TypeVisitable; +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; + +use std::cell::{Cell, Ref, RefCell}; +use std::fmt; + +use self::combine::CombineFields; +use self::free_regions::RegionRelations; +use self::lexical_region_resolve::LexicalRegionResolutions; +use self::outlives::env::OutlivesEnvironment; +use self::region_constraints::{GenericKind, RegionConstraintData, VarInfos, VerifyBound}; +use self::region_constraints::{ + RegionConstraintCollector, RegionConstraintStorage, RegionSnapshot, +}; +use self::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; + +pub mod at; +pub mod canonical; +mod combine; +mod equate; +pub mod error_reporting; +pub mod free_regions; +mod freshen; +mod fudge; +mod glb; +mod higher_ranked; +pub mod lattice; +mod lexical_region_resolve; +mod lub; +pub mod nll_relate; +pub mod opaque_types; +pub mod outlives; +mod projection; +pub mod region_constraints; +pub mod resolve; +mod sub; +pub mod type_variable; +mod undo_log; + +#[must_use] +#[derive(Debug)] +pub struct InferOk<'tcx, T> { + pub value: T, + pub obligations: PredicateObligations<'tcx>, +} +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" + +pub(crate) type UnificationTable<'a, 'tcx, T> = ut::UnificationTable< + ut::InPlace<T, &'a mut ut::UnificationStorage<T>, &'a mut InferCtxtUndoLogs<'tcx>>, +>; + +/// This type contains all the things within `InferCtxt` that sit within a +/// `RefCell` and are involved with taking/rolling back snapshots. Snapshot +/// operations are hot enough that we want only one call to `borrow_mut` per +/// call to `start_snapshot` and `rollback_to`. +#[derive(Clone)] +pub struct InferCtxtInner<'tcx> { + /// Cache for projections. This cache is snapshotted along with the infcx. + /// + /// Public so that `traits::project` can use it. + pub projection_cache: traits::ProjectionCacheStorage<'tcx>, + + /// We instantiate `UnificationTable` with `bounds<Ty>` because the types + /// that might instantiate a general type variable have an order, + /// represented by its upper and lower bounds. + type_variable_storage: type_variable::TypeVariableStorage<'tcx>, + + /// Map from const parameter variable to the kind of const it represents. + const_unification_storage: ut::UnificationTableStorage<ty::ConstVid<'tcx>>, + + /// Map from integral variable to the kind of integer it represents. + int_unification_storage: ut::UnificationTableStorage<ty::IntVid>, + + /// Map from floating variable to the kind of float it represents. + float_unification_storage: ut::UnificationTableStorage<ty::FloatVid>, + + /// Tracks the set of region variables and the constraints between them. + /// This is initially `Some(_)` but when + /// `resolve_regions_and_report_errors` is invoked, this gets set to `None` + /// -- further attempts to perform unification, etc., may fail if new + /// region constraints would've been added. + region_constraint_storage: Option<RegionConstraintStorage<'tcx>>, + + /// A set of constraints that regionck must validate. Each + /// constraint has the form `T:'a`, meaning "some type `T` must + /// outlive the lifetime 'a". These constraints derive from + /// instantiated type parameters. So if you had a struct defined + /// like + /// ```ignore (illustrative) + /// struct Foo<T:'static> { ... } + /// ``` + /// then in some expression `let x = Foo { ... }` it will + /// instantiate the type parameter `T` with a fresh type `$0`. At + /// the same time, it will record a region obligation of + /// `$0:'static`. This will get checked later by regionck. (We + /// can't generally check these things right away because we have + /// to wait until types are resolved.) + /// + /// These are stored in a map keyed to the id of the innermost + /// enclosing fn body / static initializer expression. This is + /// because the location where the obligation was incurred can be + /// relevant with respect to which sublifetime assumptions are in + /// place. The reason that we store under the fn-id, and not + /// something more fine-grained, is so that it is easier for + /// regionck to be sure that it has found *all* the region + /// obligations (otherwise, it's easy to fail to walk to a + /// particular node-id). + /// + /// Before running `resolve_regions_and_report_errors`, the creator + /// of the inference context is expected to invoke + /// [`InferCtxt::process_registered_region_obligations`] + /// for each body-id in this map, which will process the + /// obligations within. This is expected to be done 'late enough' + /// that all type inference variables have been bound and so forth. + region_obligations: Vec<RegionObligation<'tcx>>, + + undo_log: InferCtxtUndoLogs<'tcx>, + + /// Caches for opaque type inference. + pub opaque_type_storage: OpaqueTypeStorage<'tcx>, +} + +impl<'tcx> InferCtxtInner<'tcx> { + fn new() -> InferCtxtInner<'tcx> { + InferCtxtInner { + projection_cache: Default::default(), + type_variable_storage: type_variable::TypeVariableStorage::new(), + undo_log: InferCtxtUndoLogs::default(), + const_unification_storage: ut::UnificationTableStorage::new(), + int_unification_storage: ut::UnificationTableStorage::new(), + float_unification_storage: ut::UnificationTableStorage::new(), + region_constraint_storage: Some(RegionConstraintStorage::new()), + region_obligations: vec![], + opaque_type_storage: Default::default(), + } + } + + #[inline] + pub fn region_obligations(&self) -> &[RegionObligation<'tcx>] { + &self.region_obligations + } + + #[inline] + pub fn projection_cache(&mut self) -> traits::ProjectionCache<'_, 'tcx> { + self.projection_cache.with_log(&mut self.undo_log) + } + + #[inline] + fn type_variables(&mut self) -> type_variable::TypeVariableTable<'_, 'tcx> { + self.type_variable_storage.with_log(&mut self.undo_log) + } + + #[inline] + pub fn opaque_types(&mut self) -> opaque_types::OpaqueTypeTable<'_, 'tcx> { + self.opaque_type_storage.with_log(&mut self.undo_log) + } + + #[inline] + fn int_unification_table( + &mut self, + ) -> ut::UnificationTable< + ut::InPlace< + ty::IntVid, + &mut ut::UnificationStorage<ty::IntVid>, + &mut InferCtxtUndoLogs<'tcx>, + >, + > { + self.int_unification_storage.with_log(&mut self.undo_log) + } + + #[inline] + fn float_unification_table( + &mut self, + ) -> ut::UnificationTable< + ut::InPlace< + ty::FloatVid, + &mut ut::UnificationStorage<ty::FloatVid>, + &mut InferCtxtUndoLogs<'tcx>, + >, + > { + self.float_unification_storage.with_log(&mut self.undo_log) + } + + #[inline] + fn const_unification_table( + &mut self, + ) -> ut::UnificationTable< + ut::InPlace< + ty::ConstVid<'tcx>, + &mut ut::UnificationStorage<ty::ConstVid<'tcx>>, + &mut InferCtxtUndoLogs<'tcx>, + >, + > { + self.const_unification_storage.with_log(&mut self.undo_log) + } + + #[inline] + pub fn unwrap_region_constraints(&mut self) -> RegionConstraintCollector<'_, 'tcx> { + self.region_constraint_storage + .as_mut() + .expect("region constraints already solved") + .with_log(&mut self.undo_log) + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum DefiningAnchor { + /// `DefId` of the item. + Bind(LocalDefId), + /// When opaque types are not resolved, we `Bubble` up, meaning + /// return the opaque/hidden type pair from query, for caller of query to handle it. + Bubble, + /// Used to catch type mismatch errors when handling opaque types. + Error, +} + +pub struct InferCtxt<'a, 'tcx> { + pub tcx: TyCtxt<'tcx>, + + /// The `DefId` of the item in whose context we are performing inference or typeck. + /// It is used to check whether an opaque type use is a defining use. + /// + /// If it is `DefiningAnchor::Bubble`, we can't resolve opaque types here and need to bubble up + /// the obligation. This frequently happens for + /// short lived InferCtxt within queries. The opaque type obligations are forwarded + /// to the outside until the end up in an `InferCtxt` for typeck or borrowck. + /// + /// It is default value is `DefiningAnchor::Error`, this way it is easier to catch errors that + /// might come up during inference or typeck. + pub defining_use_anchor: DefiningAnchor, + + /// Whether this inference context should care about region obligations in + /// the root universe. Most notably, this is used during hir typeck as region + /// solving is left to borrowck instead. + pub considering_regions: bool, + + /// During type-checking/inference of a body, `in_progress_typeck_results` + /// contains a reference to the typeck results being built up, which are + /// used for reading closure kinds/signatures as they are inferred, + /// and for error reporting logic to read arbitrary node types. + pub in_progress_typeck_results: Option<&'a RefCell<ty::TypeckResults<'tcx>>>, + + pub inner: RefCell<InferCtxtInner<'tcx>>, + + /// If set, this flag causes us to skip the 'leak check' during + /// higher-ranked subtyping operations. This flag is a temporary one used + /// to manage the removal of the leak-check: for the time being, we still run the + /// leak-check, but we issue warnings. This flag can only be set to true + /// when entering a snapshot. + skip_leak_check: Cell<bool>, + + /// Once region inference is done, the values for each variable. + lexical_region_resolutions: RefCell<Option<LexicalRegionResolutions<'tcx>>>, + + /// Caches the results of trait selection. This cache is used + /// for things that have to do with the parameters in scope. + pub selection_cache: select::SelectionCache<'tcx>, + + /// Caches the results of trait evaluation. + pub evaluation_cache: select::EvaluationCache<'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_closure_mismatch: RefCell<FxHashSet<(Span, Option<Span>)>>, + + /// When an error occurs, we want to avoid reporting "derived" + /// errors that are due to this original failure. Normally, we + /// handle this with the `err_count_on_creation` count, which + /// basically just tracks how many errors were reported when we + /// started type-checking a fn and checks to see if any new errors + /// have been reported since then. Not great, but it works. + /// + /// However, when errors originated in other passes -- notably + /// resolve -- this heuristic breaks down. Therefore, we have this + /// auxiliary flag that one can set whenever one creates a + /// type-error that is due to an error in a prior pass. + /// + /// Don't read this flag directly, call `is_tainted_by_errors()` + /// and `set_tainted_by_errors()`. + tainted_by_errors_flag: Cell<bool>, + + /// Track how many errors were reported when this infcx is created. + /// If the number of errors increases, that's also a sign (line + /// `tainted_by_errors`) to avoid reporting certain kinds of errors. + // FIXME(matthewjasper) Merge into `tainted_by_errors_flag` + err_count_on_creation: usize, + + /// This flag is true while there is an active snapshot. + in_snapshot: Cell<bool>, + + /// What is the innermost universe we have created? Starts out as + /// `UniverseIndex::root()` but grows from there as we enter + /// universal quantifiers. + /// + /// N.B., at present, we exclude the universal quantifiers on the + /// item we are type-checking, and just consider those names as + /// part of the root universe. So this would only get incremented + /// when we enter into a higher-ranked (`for<..>`) type or trait + /// bound. + universe: Cell<ty::UniverseIndex>, +} + +/// See the `error_reporting` module for more details. +#[derive(Clone, Copy, Debug, PartialEq, Eq, TypeFoldable, TypeVisitable)] +pub enum ValuePairs<'tcx> { + Regions(ExpectedFound<ty::Region<'tcx>>), + Terms(ExpectedFound<ty::Term<'tcx>>), + TraitRefs(ExpectedFound<ty::TraitRef<'tcx>>), + PolyTraitRefs(ExpectedFound<ty::PolyTraitRef<'tcx>>), +} + +impl<'tcx> ValuePairs<'tcx> { + pub fn ty(&self) -> Option<(Ty<'tcx>, Ty<'tcx>)> { + if let ValuePairs::Terms(ExpectedFound { + expected: ty::Term::Ty(expected), + found: ty::Term::Ty(found), + }) = self + { + Some((*expected, *found)) + } else { + None + } + } +} + +/// The trace designates the path through inference that we took to +/// encounter an error or subtyping constraint. +/// +/// See the `error_reporting` module for more details. +#[derive(Clone, Debug)] +pub struct TypeTrace<'tcx> { + pub cause: ObligationCause<'tcx>, + pub values: ValuePairs<'tcx>, +} + +/// The origin of a `r1 <= r2` constraint. +/// +/// See `error_reporting` module for more details +#[derive(Clone, Debug)] +pub enum SubregionOrigin<'tcx> { + /// Arose from a subtyping relation + Subtype(Box<TypeTrace<'tcx>>), + + /// When casting `&'a T` to an `&'b Trait` object, + /// relating `'a` to `'b` + RelateObjectBound(Span), + + /// Some type parameter was instantiated with the given type, + /// and that type must outlive some region. + RelateParamBound(Span, Ty<'tcx>, Option<Span>), + + /// The given region parameter was instantiated with a region + /// that must outlive some other region. + RelateRegionParamBound(Span), + + /// Creating a pointer `b` to contents of another reference + Reborrow(Span), + + /// Creating a pointer `b` to contents of an upvar + ReborrowUpvar(Span, ty::UpvarId), + + /// Data with type `Ty<'tcx>` was borrowed + DataBorrowed(Ty<'tcx>, Span), + + /// (&'a &'b T) where a >= b + ReferenceOutlivesReferent(Ty<'tcx>, Span), + + /// Comparing the signature and requirements of an impl method against + /// the containing trait. + CompareImplItemObligation { span: Span, impl_item_def_id: LocalDefId, trait_item_def_id: DefId }, + + /// Checking that the bounds of a trait's associated type hold for a given impl + CheckAssociatedTypeBounds { + parent: Box<SubregionOrigin<'tcx>>, + impl_item_def_id: LocalDefId, + trait_item_def_id: DefId, + }, +} + +// `SubregionOrigin` is used a lot. Make sure it doesn't unintentionally get bigger. +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +static_assert_size!(SubregionOrigin<'_>, 32); + +/// Times when we replace late-bound regions with variables: +#[derive(Clone, Copy, Debug)] +pub enum LateBoundRegionConversionTime { + /// when a fn is called + FnCall, + + /// when two higher-ranked types are compared + HigherRankedType, + + /// when projecting an associated type + AssocTypeProjection(DefId), +} + +/// Reasons to create a region inference variable +/// +/// See `error_reporting` module for more details +#[derive(Copy, Clone, Debug)] +pub enum RegionVariableOrigin { + /// Region variables created for ill-categorized reasons, + /// mostly indicates places in need of refactoring + MiscVariable(Span), + + /// Regions created by a `&P` or `[...]` pattern + PatternRegion(Span), + + /// Regions created by `&` operator + AddrOfRegion(Span), + + /// Regions created as part of an autoref of a method receiver + Autoref(Span), + + /// Regions created as part of an automatic coercion + Coercion(Span), + + /// Region variables created as the values for early-bound regions + EarlyBoundRegion(Span, Symbol), + + /// Region variables created for bound regions + /// in a function or method that is called + LateBoundRegion(Span, ty::BoundRegionKind, LateBoundRegionConversionTime), + + UpvarRegion(ty::UpvarId, Span), + + /// This origin is used for the inference variables that we create + /// during NLL region processing. + Nll(NllRegionVariableOrigin), +} + +#[derive(Copy, Clone, Debug)] +pub enum NllRegionVariableOrigin { + /// During NLL region processing, we create variables for free + /// regions that we encounter in the function signature and + /// elsewhere. This origin indices we've got one of those. + FreeRegion, + + /// "Universal" instantiation of a higher-ranked region (e.g., + /// from a `for<'a> T` binder). Meant to represent "any region". + Placeholder(ty::PlaceholderRegion), + + Existential { + /// If this is true, then this variable was created to represent a lifetime + /// bound in a `for` binder. For example, it might have been created to + /// represent the lifetime `'a` in a type like `for<'a> fn(&'a u32)`. + /// Such variables are created when we are trying to figure out if there + /// is any valid instantiation of `'a` that could fit into some scenario. + /// + /// This is used to inform error reporting: in the case that we are trying to + /// determine whether there is any valid instantiation of a `'a` variable that meets + /// some constraint C, we want to blame the "source" of that `for` type, + /// rather than blaming the source of the constraint C. + from_forall: bool, + }, +} + +// FIXME(eddyb) investigate overlap between this and `TyOrConstInferVar`. +#[derive(Copy, Clone, Debug)] +pub enum FixupError<'tcx> { + UnresolvedIntTy(IntVid), + UnresolvedFloatTy(FloatVid), + UnresolvedTy(TyVid), + UnresolvedConst(ConstVid<'tcx>), +} + +/// See the `region_obligations` field for more information. +#[derive(Clone)] +pub struct RegionObligation<'tcx> { + pub sub_region: ty::Region<'tcx>, + pub sup_type: Ty<'tcx>, + pub origin: SubregionOrigin<'tcx>, +} + +impl<'tcx> fmt::Display for FixupError<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use self::FixupError::*; + + match *self { + UnresolvedIntTy(_) => write!( + f, + "cannot determine the type of this integer; \ + add a suffix to specify the type explicitly" + ), + UnresolvedFloatTy(_) => write!( + f, + "cannot determine the type of this number; \ + add a suffix to specify the type explicitly" + ), + UnresolvedTy(_) => write!(f, "unconstrained type"), + UnresolvedConst(_) => write!(f, "unconstrained const value"), + } + } +} + +/// A temporary returned by `tcx.infer_ctxt()`. This is necessary +/// for multiple `InferCtxt` to share the same `in_progress_typeck_results` +/// without using `Rc` or something similar. +pub struct InferCtxtBuilder<'tcx> { + tcx: TyCtxt<'tcx>, + defining_use_anchor: DefiningAnchor, + considering_regions: bool, + fresh_typeck_results: Option<RefCell<ty::TypeckResults<'tcx>>>, +} + +pub trait TyCtxtInferExt<'tcx> { + fn infer_ctxt(self) -> InferCtxtBuilder<'tcx>; +} + +impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { + fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> { + InferCtxtBuilder { + tcx: self, + defining_use_anchor: DefiningAnchor::Error, + considering_regions: true, + fresh_typeck_results: None, + } + } +} + +impl<'tcx> InferCtxtBuilder<'tcx> { + /// Used only by `rustc_typeck` during body type-checking/inference, + /// will initialize `in_progress_typeck_results` with fresh `TypeckResults`. + /// Will also change the scope for opaque type defining use checks to the given owner. + pub fn with_fresh_in_progress_typeck_results(mut self, table_owner: LocalDefId) -> Self { + self.fresh_typeck_results = Some(RefCell::new(ty::TypeckResults::new(table_owner))); + self.with_opaque_type_inference(DefiningAnchor::Bind(table_owner)) + } + + /// Whenever the `InferCtxt` should be able to handle defining uses of opaque types, + /// you need to call this function. Otherwise the opaque type will be treated opaquely. + /// + /// It is only meant to be called in two places, for typeck + /// (via `with_fresh_in_progress_typeck_results`) and for the inference context used + /// in mir borrowck. + pub fn with_opaque_type_inference(mut self, defining_use_anchor: DefiningAnchor) -> Self { + self.defining_use_anchor = defining_use_anchor; + self + } + + pub fn ignoring_regions(mut self) -> Self { + self.considering_regions = false; + self + } + + /// Given a canonical value `C` as a starting point, create an + /// inference context that contains each of the bound values + /// within instantiated as a fresh variable. The `f` closure is + /// invoked with the new infcx, along with the instantiated value + /// `V` and a substitution `S`. This substitution `S` maps from + /// the bound values in `C` to their instantiated values in `V` + /// (in other words, `S(C) = V`). + pub fn enter_with_canonical<T, R>( + &mut self, + span: Span, + canonical: &Canonical<'tcx, T>, + f: impl for<'a> FnOnce(InferCtxt<'a, 'tcx>, T, CanonicalVarValues<'tcx>) -> R, + ) -> R + where + T: TypeFoldable<'tcx>, + { + self.enter(|infcx| { + let (value, subst) = + infcx.instantiate_canonical_with_fresh_inference_vars(span, canonical); + f(infcx, value, subst) + }) + } + + pub fn enter<R>(&mut self, f: impl for<'a> FnOnce(InferCtxt<'a, 'tcx>) -> R) -> R { + let InferCtxtBuilder { + tcx, + defining_use_anchor, + considering_regions, + ref fresh_typeck_results, + } = *self; + let in_progress_typeck_results = fresh_typeck_results.as_ref(); + f(InferCtxt { + tcx, + defining_use_anchor, + considering_regions, + in_progress_typeck_results, + inner: RefCell::new(InferCtxtInner::new()), + lexical_region_resolutions: RefCell::new(None), + selection_cache: Default::default(), + evaluation_cache: Default::default(), + reported_trait_errors: Default::default(), + reported_closure_mismatch: Default::default(), + tainted_by_errors_flag: Cell::new(false), + err_count_on_creation: tcx.sess.err_count(), + in_snapshot: Cell::new(false), + skip_leak_check: Cell::new(false), + universe: Cell::new(ty::UniverseIndex::ROOT), + }) + } +} + +impl<'tcx, T> InferOk<'tcx, T> { + pub fn unit(self) -> InferOk<'tcx, ()> { + InferOk { value: (), obligations: self.obligations } + } + + /// Extracts `value`, registering any obligations into `fulfill_cx`. + pub fn into_value_registering_obligations( + self, + infcx: &InferCtxt<'_, 'tcx>, + fulfill_cx: &mut dyn TraitEngine<'tcx>, + ) -> T { + let InferOk { value, obligations } = self; + fulfill_cx.register_predicate_obligations(infcx, obligations); + value + } +} + +impl<'tcx> InferOk<'tcx, ()> { + pub fn into_obligations(self) -> PredicateObligations<'tcx> { + self.obligations + } +} + +#[must_use = "once you start a snapshot, you should always consume it"] +pub struct CombinedSnapshot<'a, 'tcx> { + undo_snapshot: Snapshot<'tcx>, + region_constraints_snapshot: RegionSnapshot, + universe: ty::UniverseIndex, + was_in_snapshot: bool, + _in_progress_typeck_results: Option<Ref<'a, ty::TypeckResults<'tcx>>>, +} + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + /// calls `tcx.try_unify_abstract_consts` after + /// canonicalizing the consts. + #[instrument(skip(self), level = "debug")] + pub fn try_unify_abstract_consts( + &self, + a: ty::Unevaluated<'tcx, ()>, + b: ty::Unevaluated<'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_infer_types_or_consts() + || b.substs.has_infer_types_or_consts() + || param_env.has_infer_types_or_consts() + { + debug!("a or b or param_env contain infer vars in its substs -> cannot unify"); + return false; + } + + 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 { + self.in_snapshot.get() + } + + pub fn freshen<T: TypeFoldable<'tcx>>(&self, t: T) -> T { + t.fold_with(&mut self.freshener()) + } + + /// Returns the origin of the type variable identified by `vid`, or `None` + /// if this is not a type variable. + /// + /// No attempt is made to resolve `ty`. + pub fn type_var_origin(&'a self, ty: Ty<'tcx>) -> Option<TypeVariableOrigin> { + match *ty.kind() { + ty::Infer(ty::TyVar(vid)) => { + Some(*self.inner.borrow_mut().type_variables().var_origin(vid)) + } + _ => None, + } + } + + pub fn freshener<'b>(&'b self) -> TypeFreshener<'b, 'tcx> { + freshen::TypeFreshener::new(self, false) + } + + /// Like `freshener`, but does not replace `'static` regions. + pub fn freshener_keep_static<'b>(&'b self) -> TypeFreshener<'b, 'tcx> { + freshen::TypeFreshener::new(self, true) + } + + pub fn unsolved_variables(&self) -> Vec<Ty<'tcx>> { + let mut inner = self.inner.borrow_mut(); + let mut vars: Vec<Ty<'_>> = inner + .type_variables() + .unsolved_variables() + .into_iter() + .map(|t| self.tcx.mk_ty_var(t)) + .collect(); + vars.extend( + (0..inner.int_unification_table().len()) + .map(|i| ty::IntVid { index: i as u32 }) + .filter(|&vid| inner.int_unification_table().probe_value(vid).is_none()) + .map(|v| self.tcx.mk_int_var(v)), + ); + vars.extend( + (0..inner.float_unification_table().len()) + .map(|i| ty::FloatVid { index: i as u32 }) + .filter(|&vid| inner.float_unification_table().probe_value(vid).is_none()) + .map(|v| self.tcx.mk_float_var(v)), + ); + vars + } + + fn combine_fields( + &'a self, + trace: TypeTrace<'tcx>, + param_env: ty::ParamEnv<'tcx>, + define_opaque_types: bool, + ) -> CombineFields<'a, 'tcx> { + CombineFields { + infcx: self, + trace, + cause: None, + param_env, + obligations: PredicateObligations::new(), + define_opaque_types, + } + } + + /// 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<'a, 'tcx> { + debug!("start_snapshot()"); + + let in_snapshot = self.in_snapshot.replace(true); + + let mut inner = self.inner.borrow_mut(); + + CombinedSnapshot { + undo_snapshot: inner.undo_log.start_snapshot(), + region_constraints_snapshot: inner.unwrap_region_constraints().start_snapshot(), + universe: self.universe(), + was_in_snapshot: in_snapshot, + // Borrow typeck results "in progress" (i.e., during typeck) + // to ban writes from within a snapshot to them. + _in_progress_typeck_results: self + .in_progress_typeck_results + .map(|typeck_results| typeck_results.borrow()), + } + } + + #[instrument(skip(self, snapshot), level = "debug")] + fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'a, 'tcx>) { + let CombinedSnapshot { + undo_snapshot, + region_constraints_snapshot, + universe, + was_in_snapshot, + _in_progress_typeck_results, + } = snapshot; + + self.in_snapshot.set(was_in_snapshot); + self.universe.set(universe); + + let mut inner = self.inner.borrow_mut(); + inner.rollback_to(undo_snapshot); + inner.unwrap_region_constraints().rollback_to(region_constraints_snapshot); + } + + #[instrument(skip(self, snapshot), level = "debug")] + fn commit_from(&self, snapshot: CombinedSnapshot<'a, 'tcx>) { + let CombinedSnapshot { + undo_snapshot, + region_constraints_snapshot: _, + universe: _, + was_in_snapshot, + _in_progress_typeck_results, + } = snapshot; + + self.in_snapshot.set(was_in_snapshot); + + self.inner.borrow_mut().commit(undo_snapshot); + } + + /// Execute `f` and commit the bindings if closure `f` returns `Ok(_)`. + #[instrument(skip(self, f), level = "debug")] + pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E> + where + F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> Result<T, E>, + { + let snapshot = self.start_snapshot(); + let r = f(&snapshot); + debug!("commit_if_ok() -- r.is_ok() = {}", r.is_ok()); + match r { + Ok(_) => { + self.commit_from(snapshot); + } + Err(_) => { + self.rollback_to("commit_if_ok -- error", snapshot); + } + } + r + } + + /// Execute `f` then unroll any bindings it creates. + #[instrument(skip(self, f), level = "debug")] + pub fn probe<R, F>(&self, f: F) -> R + where + F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R, + { + let snapshot = self.start_snapshot(); + let r = f(&snapshot); + self.rollback_to("probe", snapshot); + r + } + + /// If `should_skip` is true, then execute `f` then unroll any bindings it creates. + #[instrument(skip(self, f), level = "debug")] + pub fn probe_maybe_skip_leak_check<R, F>(&self, should_skip: bool, f: F) -> R + where + F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R, + { + let snapshot = self.start_snapshot(); + let was_skip_leak_check = self.skip_leak_check.get(); + if should_skip { + self.skip_leak_check.set(true); + } + let r = f(&snapshot); + self.rollback_to("probe", snapshot); + self.skip_leak_check.set(was_skip_leak_check); + r + } + + /// Scan the constraints produced since `snapshot` began and returns: + /// + /// - `None` -- if none of them involve "region outlives" constraints + /// - `Some(true)` -- if there are `'a: 'b` constraints where `'a` or `'b` is a placeholder + /// - `Some(false)` -- if there are `'a: 'b` constraints but none involve placeholders + pub fn region_constraints_added_in_snapshot( + &self, + snapshot: &CombinedSnapshot<'a, 'tcx>, + ) -> Option<bool> { + self.inner + .borrow_mut() + .unwrap_region_constraints() + .region_constraints_added_in_snapshot(&snapshot.undo_snapshot) + } + + pub fn opaque_types_added_in_snapshot(&self, snapshot: &CombinedSnapshot<'a, 'tcx>) -> bool { + self.inner.borrow().undo_log.opaque_types_in_snapshot(&snapshot.undo_snapshot) + } + + pub fn add_given(&self, sub: ty::Region<'tcx>, sup: ty::RegionVid) { + self.inner.borrow_mut().unwrap_region_constraints().add_given(sub, sup); + } + + pub fn can_sub<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> UnitResult<'tcx> + where + T: at::ToTrace<'tcx>, + { + let origin = &ObligationCause::dummy(); + self.probe(|_| { + self.at(origin, param_env).sub(a, b).map(|InferOk { obligations: _, .. }| { + // Ignore obligations, since we are unrolling + // everything anyway. + }) + }) + } + + pub fn can_eq<T>(&self, param_env: ty::ParamEnv<'tcx>, a: T, b: T) -> UnitResult<'tcx> + where + T: at::ToTrace<'tcx>, + { + let origin = &ObligationCause::dummy(); + self.probe(|_| { + self.at(origin, param_env).eq(a, b).map(|InferOk { obligations: _, .. }| { + // Ignore obligations, since we are unrolling + // everything anyway. + }) + }) + } + + #[instrument(skip(self), level = "debug")] + pub fn sub_regions( + &self, + origin: SubregionOrigin<'tcx>, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) { + self.inner.borrow_mut().unwrap_region_constraints().make_subregion(origin, a, b); + } + + /// Require that the region `r` be equal to one of the regions in + /// the set `regions`. + #[instrument(skip(self), level = "debug")] + pub fn member_constraint( + &self, + key: ty::OpaqueTypeKey<'tcx>, + definition_span: Span, + hidden_ty: Ty<'tcx>, + region: ty::Region<'tcx>, + in_regions: &Lrc<Vec<ty::Region<'tcx>>>, + ) { + self.inner.borrow_mut().unwrap_region_constraints().member_constraint( + key, + definition_span, + hidden_ty, + region, + in_regions, + ); + } + + /// Processes a `Coerce` predicate from the fulfillment context. + /// This is NOT the preferred way to handle coercion, which is to + /// invoke `FnCtxt::coerce` or a similar method (see `coercion.rs`). + /// + /// This method here is actually a fallback that winds up being + /// invoked when `FnCtxt::coerce` encounters unresolved type variables + /// and records a coercion predicate. Presently, this method is equivalent + /// to `subtype_predicate` -- that is, "coercing" `a` to `b` winds up + /// actually requiring `a <: b`. This is of course a valid coercion, + /// but it's not as flexible as `FnCtxt::coerce` would be. + /// + /// (We may refactor this in the future, but there are a number of + /// practical obstacles. Among other things, `FnCtxt::coerce` presently + /// records adjustments that are required on the HIR in order to perform + /// the coercion, and we don't currently have a way to manage that.) + pub fn coerce_predicate( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + predicate: ty::PolyCoercePredicate<'tcx>, + ) -> Option<InferResult<'tcx, ()>> { + let subtype_predicate = predicate.map_bound(|p| ty::SubtypePredicate { + a_is_expected: false, // when coercing from `a` to `b`, `b` is expected + a: p.a, + b: p.b, + }); + self.subtype_predicate(cause, param_env, subtype_predicate) + } + + pub fn subtype_predicate( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + predicate: ty::PolySubtypePredicate<'tcx>, + ) -> Option<InferResult<'tcx, ()>> { + // Check for two unresolved inference variables, in which case we can + // make no progress. This is partly a micro-optimization, but it's + // also an opportunity to "sub-unify" the variables. This isn't + // *necessary* to prevent cycles, because they would eventually be sub-unified + // anyhow during generalization, but it helps with diagnostics (we can detect + // earlier that they are sub-unified). + // + // Note that we can just skip the binders here because + // type variables can't (at present, at + // least) capture any of the things bound by this binder. + // + // Note that this sub here is not just for diagnostics - it has semantic + // effects as well. + let r_a = self.shallow_resolve(predicate.skip_binder().a); + let r_b = self.shallow_resolve(predicate.skip_binder().b); + match (r_a.kind(), r_b.kind()) { + (&ty::Infer(ty::TyVar(a_vid)), &ty::Infer(ty::TyVar(b_vid))) => { + self.inner.borrow_mut().type_variables().sub(a_vid, b_vid); + return None; + } + _ => {} + } + + Some(self.commit_if_ok(|_snapshot| { + let ty::SubtypePredicate { a_is_expected, a, b } = + self.replace_bound_vars_with_placeholders(predicate); + + let ok = self.at(cause, param_env).sub_exp(a_is_expected, a, b)?; + + Ok(ok.unit()) + })) + } + + pub fn region_outlives_predicate( + &self, + cause: &traits::ObligationCause<'tcx>, + predicate: ty::PolyRegionOutlivesPredicate<'tcx>, + ) { + let ty::OutlivesPredicate(r_a, r_b) = self.replace_bound_vars_with_placeholders(predicate); + let origin = + SubregionOrigin::from_obligation_cause(cause, || RelateRegionParamBound(cause.span)); + self.sub_regions(origin, r_b, r_a); // `b : a` ==> `a <= b` + } + + /// Number of type variables created so far. + pub fn num_ty_vars(&self) -> usize { + self.inner.borrow_mut().type_variables().num_vars() + } + + pub fn next_ty_var_id(&self, origin: TypeVariableOrigin) -> TyVid { + self.inner.borrow_mut().type_variables().new_var(self.universe(), origin) + } + + pub fn next_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> { + self.tcx.mk_ty_var(self.next_ty_var_id(origin)) + } + + pub fn next_ty_var_id_in_universe( + &self, + origin: TypeVariableOrigin, + universe: ty::UniverseIndex, + ) -> TyVid { + self.inner.borrow_mut().type_variables().new_var(universe, origin) + } + + pub fn next_ty_var_in_universe( + &self, + origin: TypeVariableOrigin, + universe: ty::UniverseIndex, + ) -> Ty<'tcx> { + let vid = self.next_ty_var_id_in_universe(origin, universe); + self.tcx.mk_ty_var(vid) + } + + 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) + } + + pub fn next_const_var_in_universe( + &self, + ty: Ty<'tcx>, + origin: ConstVariableOrigin, + universe: ty::UniverseIndex, + ) -> ty::Const<'tcx> { + let vid = self + .inner + .borrow_mut() + .const_unification_table() + .new_key(ConstVarValue { origin, val: ConstVariableValue::Unknown { universe } }); + self.tcx.mk_const_var(vid, ty) + } + + pub fn next_const_var_id(&self, origin: ConstVariableOrigin) -> ConstVid<'tcx> { + self.inner.borrow_mut().const_unification_table().new_key(ConstVarValue { + origin, + val: ConstVariableValue::Unknown { universe: self.universe() }, + }) + } + + fn next_int_var_id(&self) -> IntVid { + self.inner.borrow_mut().int_unification_table().new_key(None) + } + + pub fn next_int_var(&self) -> Ty<'tcx> { + self.tcx.mk_int_var(self.next_int_var_id()) + } + + fn next_float_var_id(&self) -> FloatVid { + self.inner.borrow_mut().float_unification_table().new_key(None) + } + + pub fn next_float_var(&self) -> Ty<'tcx> { + self.tcx.mk_float_var(self.next_float_var_id()) + } + + /// Creates a fresh region variable with the next available index. + /// The variable will be created in the maximum universe created + /// thus far, allowing it to name any region created thus far. + pub fn next_region_var(&self, origin: RegionVariableOrigin) -> ty::Region<'tcx> { + self.next_region_var_in_universe(origin, self.universe()) + } + + /// Creates a fresh region variable with the next available index + /// in the given universe; typically, you can use + /// `next_region_var` and just use the maximal universe. + pub fn next_region_var_in_universe( + &self, + origin: RegionVariableOrigin, + universe: ty::UniverseIndex, + ) -> ty::Region<'tcx> { + let region_var = + self.inner.borrow_mut().unwrap_region_constraints().new_region_var(universe, origin); + self.tcx.mk_region(ty::ReVar(region_var)) + } + + /// Return the universe that the region `r` was created in. For + /// most regions (e.g., `'static`, named regions from the user, + /// etc) this is the root universe U0. For inference variables or + /// placeholders, however, it will return the universe which which + /// they are associated. + pub fn universe_of_region(&self, r: ty::Region<'tcx>) -> ty::UniverseIndex { + self.inner.borrow_mut().unwrap_region_constraints().universe(r) + } + + /// Number of region variables created so far. + pub fn num_region_vars(&self) -> usize { + self.inner.borrow_mut().unwrap_region_constraints().num_region_vars() + } + + /// Just a convenient wrapper of `next_region_var` for using during NLL. + pub fn next_nll_region_var(&self, origin: NllRegionVariableOrigin) -> ty::Region<'tcx> { + self.next_region_var(RegionVariableOrigin::Nll(origin)) + } + + /// Just a convenient wrapper of `next_region_var` for using during NLL. + pub fn next_nll_region_var_in_universe( + &self, + origin: NllRegionVariableOrigin, + universe: ty::UniverseIndex, + ) -> ty::Region<'tcx> { + self.next_region_var_in_universe(RegionVariableOrigin::Nll(origin), universe) + } + + pub fn var_for_def(&self, span: Span, param: &ty::GenericParamDef) -> GenericArg<'tcx> { + match param.kind { + GenericParamDefKind::Lifetime => { + // Create a region inference variable for the given + // region parameter definition. + self.next_region_var(EarlyBoundRegion(span, param.name)).into() + } + GenericParamDefKind::Type { .. } => { + // Create a type inference variable for the given + // type parameter definition. The substitutions are + // for actual parameters that may be referred to by + // the default of this type parameter, if it exists. + // e.g., `struct Foo<A, B, C = (A, B)>(...);` when + // used in a path such as `Foo::<T, U>::new()` will + // use an inference variable for `C` with `[T, U]` + // as the substitutions for the default, `(T, U)`. + let ty_var_id = self.inner.borrow_mut().type_variables().new_var( + self.universe(), + TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeParameterDefinition( + param.name, + Some(param.def_id), + ), + span, + }, + ); + + self.tcx.mk_ty_var(ty_var_id).into() + } + GenericParamDefKind::Const { .. } => { + let origin = ConstVariableOrigin { + kind: ConstVariableOriginKind::ConstParameterDefinition( + param.name, + param.def_id, + ), + span, + }; + let const_var_id = + self.inner.borrow_mut().const_unification_table().new_key(ConstVarValue { + origin, + val: ConstVariableValue::Unknown { universe: self.universe() }, + }); + self.tcx.mk_const_var(const_var_id, self.tcx.type_of(param.def_id)).into() + } + } + } + + /// Given a set of generics defined on a type or impl, returns a substitution mapping each + /// type/region parameter to a fresh inference variable. + pub fn fresh_substs_for_item(&self, span: Span, def_id: DefId) -> SubstsRef<'tcx> { + InternalSubsts::for_item(self.tcx, def_id, |param, _| self.var_for_def(span, param)) + } + + /// Returns `true` if errors have been reported since this infcx was + /// created. This is sometimes used as a heuristic to skip + /// 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 { + debug!( + "is_tainted_by_errors(err_count={}, err_count_on_creation={}, \ + tainted_by_errors_flag={})", + self.tcx.sess.err_count(), + self.err_count_on_creation, + self.tainted_by_errors_flag.get() + ); + + if self.tcx.sess.err_count() > self.err_count_on_creation { + return true; // errors reported since this infcx was made + } + self.tainted_by_errors_flag.get() + } + + /// 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_flag.set(true) + } + + pub fn skip_region_resolution(&self) { + let (var_infos, _) = { + let mut inner = self.inner.borrow_mut(); + let inner = &mut *inner; + // Note: `inner.region_obligations` may not be empty, because we + // didn't necessarily call `process_registered_region_obligations`. + // This is okay, because that doesn't introduce new vars. + inner + .region_constraint_storage + .take() + .expect("regions already resolved") + .with_log(&mut inner.undo_log) + .into_infos_and_data() + }; + + let lexical_region_resolutions = LexicalRegionResolutions { + values: rustc_index::vec::IndexVec::from_elem_n( + crate::infer::lexical_region_resolve::VarValue::Value(self.tcx.lifetimes.re_erased), + var_infos.len(), + ), + }; + + let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); + assert!(old_value.is_none()); + } + + /// Process the region constraints and return any any errors that + /// result. After this, no more unification operations should be + /// done -- or the compiler will panic -- but it is legal to use + /// `resolve_vars_if_possible` as well as `fully_resolve`. + pub fn resolve_regions( + &self, + outlives_env: &OutlivesEnvironment<'tcx>, + ) -> Vec<RegionResolutionError<'tcx>> { + let (var_infos, data) = { + let mut inner = self.inner.borrow_mut(); + let inner = &mut *inner; + assert!( + self.is_tainted_by_errors() || inner.region_obligations.is_empty(), + "region_obligations not empty: {:#?}", + inner.region_obligations + ); + inner + .region_constraint_storage + .take() + .expect("regions already resolved") + .with_log(&mut inner.undo_log) + .into_infos_and_data() + }; + + let region_rels = &RegionRelations::new(self.tcx, outlives_env.free_region_map()); + + let (lexical_region_resolutions, errors) = + lexical_region_resolve::resolve(outlives_env.param_env, region_rels, var_infos, data); + + let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); + assert!(old_value.is_none()); + + errors + } + + /// Process the region constraints and report any errors that + /// result. After this, no more unification operations should be + /// done -- or the compiler will panic -- but it is legal to use + /// `resolve_vars_if_possible` as well as `fully_resolve`. + /// + /// Make sure to call [`InferCtxt::process_registered_region_obligations`] + /// first, or preferrably use [`InferCtxt::check_region_obligations_and_report_errors`] + /// to do both of these operations together. + pub fn resolve_regions_and_report_errors( + &self, + generic_param_scope: LocalDefId, + outlives_env: &OutlivesEnvironment<'tcx>, + ) { + let errors = self.resolve_regions(outlives_env); + + if !self.is_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 + // otherwise we have a hard time separating legit region + // errors from silly ones. + self.report_region_errors(generic_param_scope, &errors); + } + } + + /// Obtains (and clears) the current set of region + /// constraints. The inference context is still usable: further + /// unifications will simply add new constraints. + /// + /// This method is not meant to be used with normal lexical region + /// resolution. Rather, it is used in the NLL mode as a kind of + /// interim hack: basically we run normal type-check and generate + /// region constraints as normal, but then we take them and + /// translate them into the form that the NLL solver + /// understands. See the NLL module for mode details. + pub fn take_and_reset_region_constraints(&self) -> RegionConstraintData<'tcx> { + assert!( + self.inner.borrow().region_obligations.is_empty(), + "region_obligations not empty: {:#?}", + self.inner.borrow().region_obligations + ); + + self.inner.borrow_mut().unwrap_region_constraints().take_and_reset_data() + } + + /// Gives temporary access to the region constraint data. + pub fn with_region_constraints<R>( + &self, + op: impl FnOnce(&RegionConstraintData<'tcx>) -> R, + ) -> R { + let mut inner = self.inner.borrow_mut(); + op(inner.unwrap_region_constraints().data()) + } + + pub fn region_var_origin(&self, vid: ty::RegionVid) -> RegionVariableOrigin { + let mut inner = self.inner.borrow_mut(); + let inner = &mut *inner; + inner + .region_constraint_storage + .as_mut() + .expect("regions already resolved") + .with_log(&mut inner.undo_log) + .var_origin(vid) + } + + /// Takes ownership of the list of variable regions. This implies + /// that all the region constraints have already been taken, and + /// hence that `resolve_regions_and_report_errors` can never be + /// called. This is used only during NLL processing to "hand off" ownership + /// of the set of region variables into the NLL region context. + pub fn take_region_var_origins(&self) -> VarInfos { + let mut inner = self.inner.borrow_mut(); + let (var_infos, data) = inner + .region_constraint_storage + .take() + .expect("regions already resolved") + .with_log(&mut inner.undo_log) + .into_infos_and_data(); + assert!(data.is_empty()); + var_infos + } + + pub fn ty_to_string(&self, t: Ty<'tcx>) -> String { + self.resolve_vars_if_possible(t).to_string() + } + + /// If `TyVar(vid)` resolves to a type, return that type. Else, return the + /// universe index of `TyVar(vid)`. + pub fn probe_ty_var(&self, vid: TyVid) -> Result<Ty<'tcx>, ty::UniverseIndex> { + use self::type_variable::TypeVariableValue; + + match self.inner.borrow_mut().type_variables().probe(vid) { + TypeVariableValue::Known { value } => Ok(value), + TypeVariableValue::Unknown { universe } => Err(universe), + } + } + + /// Resolve any type variables found in `value` -- but only one + /// level. So, if the variable `?X` is bound to some type + /// `Foo<?Y>`, then this would return `Foo<?Y>` (but `?Y` may + /// itself be bound to a type). + /// + /// Useful when you only need to inspect the outermost level of + /// the type and don't care about nested types (or perhaps you + /// will be resolving them as well, e.g. in a loop). + pub fn shallow_resolve<T>(&self, value: T) -> T + where + T: TypeFoldable<'tcx>, + { + value.fold_with(&mut ShallowResolver { infcx: self }) + } + + pub fn root_var(&self, var: ty::TyVid) -> ty::TyVid { + self.inner.borrow_mut().type_variables().root_var(var) + } + + /// Where possible, replaces type/const variables in + /// `value` with their final value. Note that region variables + /// are unaffected. If a type/const variable has not been unified, it + /// is left as is. This is an idempotent operation that does + /// not affect inference state in any way and so you can do it + /// at will. + pub fn resolve_vars_if_possible<T>(&self, value: T) -> T + where + T: TypeFoldable<'tcx>, + { + if !value.needs_infer() { + return value; // Avoid duplicated subst-folding. + } + let mut r = resolve::OpportunisticVarResolver::new(self); + value.fold_with(&mut r) + } + + pub fn resolve_numeric_literals_with_default<T>(&self, value: T) -> T + where + T: TypeFoldable<'tcx>, + { + if !value.needs_infer() { + return value; // Avoid duplicated subst-folding. + } + let mut r = InferenceLiteralEraser { tcx: self.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>)> + where + T: TypeVisitable<'tcx>, + { + value.visit_with(&mut resolve::UnresolvedTypeFinder::new(self)).break_value() + } + + pub fn probe_const_var( + &self, + vid: ty::ConstVid<'tcx>, + ) -> Result<ty::Const<'tcx>, ty::UniverseIndex> { + match self.inner.borrow_mut().const_unification_table().probe_value(vid).val { + ConstVariableValue::Known { value } => Ok(value), + ConstVariableValue::Unknown { universe } => Err(universe), + } + } + + pub fn fully_resolve<T: TypeFoldable<'tcx>>(&self, value: T) -> FixupResult<'tcx, T> { + /*! + * Attempts to resolve all type/region/const variables in + * `value`. Region inference must have been run already (e.g., + * by calling `resolve_regions_and_report_errors`). If some + * variable was never unified, an `Err` results. + * + * This method is idempotent, but it not typically not invoked + * except during the writeback phase. + */ + + resolve::fully_resolve(self, value) + } + + // [Note-Type-error-reporting] + // An invariant is that anytime the expected or actual type is Error (the special + // error type, meaning that an error occurred when typechecking this expression), + // this is a derived error. The error cascaded from another error (that was already + // reported), so it's not useful to display it to the user. + // The following methods implement this logic. + // They check if either the actual or expected type is Error, and don't print the error + // in this case. The typechecker should only ever report type errors involving mismatched + // types using one of these methods, and should not call span_err directly for such + // errors. + + pub fn type_error_struct_with_diag<M>( + &self, + sp: Span, + mk_diag: M, + actual_ty: Ty<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> + where + M: FnOnce(String) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>, + { + let actual_ty = self.resolve_vars_if_possible(actual_ty); + debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty); + + let mut err = mk_diag(self.ty_to_string(actual_ty)); + + // Don't report an error if actual type is `Error`. + if actual_ty.references_error() { + err.downgrade_to_delayed_bug(); + } + + err + } + + pub fn report_mismatched_types( + &self, + cause: &ObligationCause<'tcx>, + expected: Ty<'tcx>, + actual: Ty<'tcx>, + err: TypeError<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let trace = TypeTrace::types(cause, true, expected, actual); + self.report_and_explain_type_error(trace, &err) + } + + pub fn report_mismatched_consts( + &self, + cause: &ObligationCause<'tcx>, + expected: ty::Const<'tcx>, + actual: ty::Const<'tcx>, + err: TypeError<'tcx>, + ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { + let trace = TypeTrace::consts(cause, true, expected, actual); + self.report_and_explain_type_error(trace, &err) + } + + pub fn replace_bound_vars_with_fresh_vars<T>( + &self, + span: Span, + lbrct: LateBoundRegionConversionTime, + value: ty::Binder<'tcx, T>, + ) -> T + where + T: TypeFoldable<'tcx> + Copy, + { + if let Some(inner) = value.no_bound_vars() { + return inner; + } + + struct ToFreshVars<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + span: Span, + lbrct: LateBoundRegionConversionTime, + map: FxHashMap<ty::BoundVar, ty::GenericArg<'tcx>>, + } + + impl<'tcx> BoundVarReplacerDelegate<'tcx> for ToFreshVars<'_, 'tcx> { + fn replace_region(&mut self, br: ty::BoundRegion) -> ty::Region<'tcx> { + self.map + .entry(br.var) + .or_insert_with(|| { + self.infcx + .next_region_var(LateBoundRegion(self.span, br.kind, self.lbrct)) + .into() + }) + .expect_region() + } + fn replace_ty(&mut self, bt: ty::BoundTy) -> Ty<'tcx> { + self.map + .entry(bt.var) + .or_insert_with(|| { + self.infcx + .next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: self.span, + }) + .into() + }) + .expect_ty() + } + fn replace_const(&mut self, bv: ty::BoundVar, ty: Ty<'tcx>) -> ty::Const<'tcx> { + self.map + .entry(bv) + .or_insert_with(|| { + self.infcx + .next_const_var( + ty, + ConstVariableOrigin { + kind: ConstVariableOriginKind::MiscVariable, + span: self.span, + }, + ) + .into() + }) + .expect_const() + } + } + let delegate = ToFreshVars { infcx: self, span, lbrct, map: Default::default() }; + self.tcx.replace_bound_vars_uncached(value, delegate) + } + + /// See the [`region_constraints::RegionConstraintCollector::verify_generic_bound`] method. + pub fn verify_generic_bound( + &self, + origin: SubregionOrigin<'tcx>, + kind: GenericKind<'tcx>, + a: ty::Region<'tcx>, + bound: VerifyBound<'tcx>, + ) { + debug!("verify_generic_bound({:?}, {:?} <: {:?})", kind, a, bound); + + self.inner + .borrow_mut() + .unwrap_region_constraints() + .verify_generic_bound(origin, kind, a, bound); + } + + /// Obtains the latest type of the given closure; this may be a + /// closure in the current function, in which case its + /// `ClosureKind` may not yet be known. + pub fn closure_kind(&self, closure_substs: SubstsRef<'tcx>) -> Option<ty::ClosureKind> { + let closure_kind_ty = closure_substs.as_closure().kind_ty(); + let closure_kind_ty = self.shallow_resolve(closure_kind_ty); + closure_kind_ty.to_opt_closure_kind() + } + + /// Clears the selection, evaluation, and projection caches. This is useful when + /// repeatedly attempting to select an `Obligation` while changing only + /// its `ParamEnv`, since `FulfillmentContext` doesn't use probing. + pub fn clear_caches(&self) { + self.selection_cache.clear(); + self.evaluation_cache.clear(); + self.inner.borrow_mut().projection_cache().clear(); + } + + pub fn universe(&self) -> ty::UniverseIndex { + self.universe.get() + } + + /// Creates and return a fresh universe that extends all previous + /// universes. Updates `self.universe` to that new universe. + pub fn create_next_universe(&self) -> ty::UniverseIndex { + let u = self.universe.get().next_universe(); + self.universe.set(u); + u + } + + pub fn try_const_eval_resolve( + &self, + param_env: ty::ParamEnv<'tcx>, + unevaluated: ty::Unevaluated<'tcx>, + ty: Ty<'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(None) => { + let tcx = self.tcx; + let def_id = unevaluated.def.did; + span_bug!( + tcx.def_span(def_id), + "unable to construct a constant value for the unevaluated constant {:?}", + unevaluated + ); + } + Err(err) => Err(err), + } + } + + /// Resolves and evaluates a constant. + /// + /// The constant can be located on a trait like `<A as B>::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<T>() { let _ = [0; bar::<T>()]; }` the repeat count + /// constant `bar::<T>()` 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. + /// + /// This handles inferences variables within both `param_env` and `substs` by + /// performing the operation on their respective canonical forms. + #[instrument(skip(self), level = "debug")] + pub fn const_eval_resolve( + &self, + mut param_env: ty::ParamEnv<'tcx>, + unevaluated: ty::Unevaluated<'tcx>, + span: Option<Span>, + ) -> EvalToValTreeResult<'tcx> { + let mut substs = self.resolve_vars_if_possible(unevaluated.substs); + debug!(?substs); + + // Postpone the evaluation of constants whose substs depend on inference + // variables + if substs.has_infer_types_or_consts() { + let ac = AbstractConst::new(self.tcx, unevaluated.shrink()); + 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); + } + } + Err(guar) => return Err(ErrorHandled::Reported(guar)), + } + } + + let param_env_erased = self.tcx.erase_regions(param_env); + let substs_erased = self.tcx.erase_regions(substs); + debug!(?param_env_erased); + debug!(?substs_erased); + + let unevaluated = ty::Unevaluated { + def: unevaluated.def, + substs: substs_erased, + promoted: unevaluated.promoted, + }; + + // 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) + } + + /// `ty_or_const_infer_var_changed` is equivalent to one of these two: + /// * `shallow_resolve(ty) != ty` (where `ty.kind = ty::Infer(_)`) + /// * `shallow_resolve(ct) != ct` (where `ct.kind = ty::ConstKind::Infer(_)`) + /// + /// However, `ty_or_const_infer_var_changed` is more efficient. It's always + /// inlined, despite being large, because it has only two call sites that + /// are extremely hot (both in `traits::fulfill`'s checking of `stalled_on` + /// inference variables), and it handles both `Ty` and `ty::Const` without + /// having to resort to storing full `GenericArg`s in `stalled_on`. + #[inline(always)] + pub fn ty_or_const_infer_var_changed(&self, infer_var: TyOrConstInferVar<'tcx>) -> bool { + match infer_var { + TyOrConstInferVar::Ty(v) => { + use self::type_variable::TypeVariableValue; + + // If `inlined_probe` returns a `Known` value, it never equals + // `ty::Infer(ty::TyVar(v))`. + match self.inner.borrow_mut().type_variables().inlined_probe(v) { + TypeVariableValue::Unknown { .. } => false, + TypeVariableValue::Known { .. } => true, + } + } + + TyOrConstInferVar::TyInt(v) => { + // If `inlined_probe_value` returns a value it's always a + // `ty::Int(_)` or `ty::UInt(_)`, which never matches a + // `ty::Infer(_)`. + self.inner.borrow_mut().int_unification_table().inlined_probe_value(v).is_some() + } + + TyOrConstInferVar::TyFloat(v) => { + // If `probe_value` returns a value it's always a + // `ty::Float(_)`, which never matches a `ty::Infer(_)`. + // + // Not `inlined_probe_value(v)` because this call site is colder. + self.inner.borrow_mut().float_unification_table().probe_value(v).is_some() + } + + TyOrConstInferVar::Const(v) => { + // If `probe_value` returns a `Known` value, it never equals + // `ty::ConstKind::Infer(ty::InferConst::Var(v))`. + // + // Not `inlined_probe_value(v)` because this call site is colder. + match self.inner.borrow_mut().const_unification_table().probe_value(v).val { + ConstVariableValue::Unknown { .. } => false, + ConstVariableValue::Known { .. } => true, + } + } + } + } +} + +/// Helper for `ty_or_const_infer_var_changed` (see comment on that), currently +/// used only for `traits::fulfill`'s list of `stalled_on` inference variables. +#[derive(Copy, Clone, Debug)] +pub enum TyOrConstInferVar<'tcx> { + /// Equivalent to `ty::Infer(ty::TyVar(_))`. + Ty(TyVid), + /// Equivalent to `ty::Infer(ty::IntVar(_))`. + TyInt(IntVid), + /// Equivalent to `ty::Infer(ty::FloatVar(_))`. + TyFloat(FloatVid), + + /// Equivalent to `ty::ConstKind::Infer(ty::InferConst::Var(_))`. + Const(ConstVid<'tcx>), +} + +impl<'tcx> TyOrConstInferVar<'tcx> { + /// Tries to extract an inference variable from a type or a constant, returns `None` + /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`) and + /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). + pub fn maybe_from_generic_arg(arg: GenericArg<'tcx>) -> Option<Self> { + match arg.unpack() { + GenericArgKind::Type(ty) => Self::maybe_from_ty(ty), + GenericArgKind::Const(ct) => Self::maybe_from_const(ct), + GenericArgKind::Lifetime(_) => None, + } + } + + /// Tries to extract an inference variable from a type, returns `None` + /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`). + pub fn maybe_from_ty(ty: Ty<'tcx>) -> Option<Self> { + match *ty.kind() { + ty::Infer(ty::TyVar(v)) => Some(TyOrConstInferVar::Ty(v)), + ty::Infer(ty::IntVar(v)) => Some(TyOrConstInferVar::TyInt(v)), + ty::Infer(ty::FloatVar(v)) => Some(TyOrConstInferVar::TyFloat(v)), + _ => None, + } + } + + /// Tries to extract an inference variable from a constant, returns `None` + /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). + pub fn maybe_from_const(ct: ty::Const<'tcx>) -> Option<Self> { + match ct.kind() { + ty::ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)), + _ => None, + } + } +} + +/// Replace `{integer}` with `i32` and `{float}` with `f64`. +/// Used only for diagnostics. +struct InferenceLiteralEraser<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> TypeFolder<'tcx> for InferenceLiteralEraser<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + match ty.kind() { + ty::Infer(ty::IntVar(_) | ty::FreshIntTy(_)) => self.tcx.types.i32, + ty::Infer(ty::FloatVar(_) | ty::FreshFloatTy(_)) => self.tcx.types.f64, + _ => ty.super_fold_with(self), + } + } +} + +struct ShallowResolver<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, +} + +impl<'a, 'tcx> TypeFolder<'tcx> for ShallowResolver<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + /// If `ty` is a type variable of some kind, resolve it one level + /// (but do not resolve types found in the result). If `typ` is + /// not a type variable, just return it unmodified. + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + match *ty.kind() { + ty::Infer(ty::TyVar(v)) => { + // Not entirely obvious: if `typ` is a type variable, + // it can be resolved to an int/float variable, which + // can then be recursively resolved, hence the + // recursion. Note though that we prevent type + // variables from unifying to other type variables + // directly (though they may be embedded + // structurally), and we prevent cycles in any case, + // so this recursion should always be of very limited + // depth. + // + // Note: if these two lines are combined into one we get + // dynamic borrow errors on `self.inner`. + let known = self.infcx.inner.borrow_mut().type_variables().probe(v).known(); + known.map_or(ty, |t| self.fold_ty(t)) + } + + ty::Infer(ty::IntVar(v)) => self + .infcx + .inner + .borrow_mut() + .int_unification_table() + .probe_value(v) + .map_or(ty, |v| v.to_type(self.infcx.tcx)), + + ty::Infer(ty::FloatVar(v)) => self + .infcx + .inner + .borrow_mut() + .float_unification_table() + .probe_value(v) + .map_or(ty, |v| v.to_type(self.infcx.tcx)), + + _ => ty, + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() { + self.infcx + .inner + .borrow_mut() + .const_unification_table() + .probe_value(vid) + .val + .known() + .unwrap_or(ct) + } else { + ct + } + } +} + +impl<'tcx> TypeTrace<'tcx> { + pub fn span(&self) -> Span { + self.cause.span + } + + pub fn types( + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: Ty<'tcx>, + b: Ty<'tcx>, + ) -> TypeTrace<'tcx> { + TypeTrace { + cause: cause.clone(), + values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + } + } + + pub fn consts( + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> TypeTrace<'tcx> { + TypeTrace { + cause: cause.clone(), + values: Terms(ExpectedFound::new(a_is_expected, a.into(), b.into())), + } + } +} + +impl<'tcx> SubregionOrigin<'tcx> { + pub fn span(&self) -> Span { + match *self { + Subtype(ref a) => a.span(), + RelateObjectBound(a) => a, + RelateParamBound(a, ..) => a, + RelateRegionParamBound(a) => a, + Reborrow(a) => a, + ReborrowUpvar(a, _) => a, + DataBorrowed(_, a) => a, + ReferenceOutlivesReferent(_, a) => a, + CompareImplItemObligation { span, .. } => span, + CheckAssociatedTypeBounds { ref parent, .. } => parent.span(), + } + } + + pub fn from_obligation_cause<F>(cause: &traits::ObligationCause<'tcx>, default: F) -> Self + where + F: FnOnce() -> Self, + { + match *cause.code() { + traits::ObligationCauseCode::ReferenceOutlivesReferent(ref_type) => { + SubregionOrigin::ReferenceOutlivesReferent(ref_type, cause.span) + } + + traits::ObligationCauseCode::CompareImplItemObligation { + impl_item_def_id, + trait_item_def_id, + kind: _, + } => SubregionOrigin::CompareImplItemObligation { + span: cause.span, + impl_item_def_id, + trait_item_def_id, + }, + + traits::ObligationCauseCode::CheckAssociatedTypeBounds { + impl_item_def_id, + trait_item_def_id, + } => SubregionOrigin::CheckAssociatedTypeBounds { + impl_item_def_id, + trait_item_def_id, + parent: Box::new(default()), + }, + + _ => default(), + } + } +} + +impl RegionVariableOrigin { + pub fn span(&self) -> Span { + match *self { + MiscVariable(a) + | PatternRegion(a) + | AddrOfRegion(a) + | Autoref(a) + | Coercion(a) + | EarlyBoundRegion(a, ..) + | LateBoundRegion(a, ..) + | UpvarRegion(_, a) => a, + Nll(..) => bug!("NLL variable used with `span`"), + } + } +} + +impl<'tcx> fmt::Debug for RegionObligation<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "RegionObligation(sub_region={:?}, sup_type={:?})", + self.sub_region, self.sup_type + ) + } +} + +/// Replaces substs that reference param or infer variables with suitable +/// placeholders. This function is meant to remove these param and infer +/// substs when they're not actually needed to evaluate a constant. +fn replace_param_and_infer_substs_with_placeholder<'tcx>( + tcx: TyCtxt<'tcx>, + substs: SubstsRef<'tcx>, +) -> SubstsRef<'tcx> { + tcx.mk_substs(substs.iter().enumerate().map(|(idx, arg)| { + match arg.unpack() { + GenericArgKind::Type(_) + if arg.has_param_types_or_consts() || arg.has_infer_types_or_consts() => + { + tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + universe: ty::UniverseIndex::ROOT, + name: ty::BoundVar::from_usize(idx), + })) + .into() + } + GenericArgKind::Const(ct) + if ct.has_infer_types_or_consts() || ct.has_param_types_or_consts() => + { + let ty = ct.ty(); + // If the type references param or infer, replace that too... + if ty.has_param_types_or_consts() || ty.has_infer_types_or_consts() { + bug!("const `{ct}`'s type should not reference params or types"); + } + tcx.mk_const(ty::ConstS { + ty, + kind: ty::ConstKind::Placeholder(ty::PlaceholderConst { + universe: ty::UniverseIndex::ROOT, + name: ty::BoundVar::from_usize(idx), + }), + }) + .into() + } + _ => arg, + } + })) +} diff --git a/compiler/rustc_infer/src/infer/nll_relate/mod.rs b/compiler/rustc_infer/src/infer/nll_relate/mod.rs new file mode 100644 index 000000000..bab4f3e9e --- /dev/null +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -0,0 +1,1080 @@ +//! This code is kind of an alternate way of doing subtyping, +//! supertyping, and type equating, distinct from the `combine.rs` +//! code but very similar in its effect and design. Eventually the two +//! ought to be merged. This code is intended for use in NLL and chalk. +//! +//! Here are the key differences: +//! +//! - This code may choose to bypass some checks (e.g., the occurs check) +//! in the case where we know that there are no unbound type inference +//! variables. This is the case for NLL, because at NLL time types are fully +//! inferred up-to regions. +//! - This code uses "universes" to handle higher-ranked regions and +//! not the leak-check. This is "more correct" than what rustc does +//! and we are generally migrating in this direction, but NLL had to +//! get there first. +//! +//! Also, this code assumes that there are no bound types at all, not even +//! free ones. This is ok because: +//! - we are not relating anything quantified over some type variable +//! - we will have instantiated all the bound type vars already (the one +//! thing we relate in chalk are basically domain goals and their +//! constituents) + +use crate::infer::combine::ConstEquateRelation; +use crate::infer::InferCtxt; +use crate::infer::{ConstVarValue, ConstVariableValue}; +use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; +use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; +use rustc_span::Span; +use std::fmt::Debug; +use std::ops::ControlFlow; + +#[derive(PartialEq)] +pub enum NormalizationStrategy { + Lazy, + Eager, +} + +pub struct TypeRelating<'me, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + infcx: &'me InferCtxt<'me, 'tcx>, + + /// Callback to use when we deduce an outlives relationship. + delegate: D, + + /// How are we relating `a` and `b`? + /// + /// - Covariant means `a <: b`. + /// - Contravariant means `b <: a`. + /// - Invariant means `a == b. + /// - Bivariant means that it doesn't matter. + ambient_variance: ty::Variance, + + ambient_variance_info: ty::VarianceDiagInfo<'tcx>, + + /// When we pass through a set of binders (e.g., when looking into + /// a `fn` type), we push a new bound region scope onto here. This + /// will contain the instantiated region for each region in those + /// binders. When we then encounter a `ReLateBound(d, br)`, we can + /// use the De Bruijn index `d` to find the right scope, and then + /// bound region name `br` to find the specific instantiation from + /// within that scope. See `replace_bound_region`. + /// + /// This field stores the instantiations for late-bound regions in + /// the `a` type. + a_scopes: Vec<BoundRegionScope<'tcx>>, + + /// Same as `a_scopes`, but for the `b` type. + b_scopes: Vec<BoundRegionScope<'tcx>>, +} + +pub trait TypeRelatingDelegate<'tcx> { + fn param_env(&self) -> ty::ParamEnv<'tcx>; + fn span(&self) -> Span; + + /// Push a constraint `sup: sub` -- this constraint must be + /// satisfied for the two types to be related. `sub` and `sup` may + /// be regions from the type or new variables created through the + /// delegate. + fn push_outlives( + &mut self, + sup: ty::Region<'tcx>, + sub: ty::Region<'tcx>, + info: ty::VarianceDiagInfo<'tcx>, + ); + + fn const_equate(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>); + fn register_opaque_type( + &mut self, + a: Ty<'tcx>, + b: Ty<'tcx>, + a_is_expected: bool, + ) -> Result<(), TypeError<'tcx>>; + + /// Creates a new universe index. Used when instantiating placeholders. + fn create_next_universe(&mut self) -> ty::UniverseIndex; + + /// Creates a new region variable representing a higher-ranked + /// region that is instantiated existentially. This creates an + /// inference variable, typically. + /// + /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then + /// we will invoke this method to instantiate `'a` with an + /// inference variable (though `'b` would be instantiated first, + /// as a placeholder). + fn next_existential_region_var(&mut self, was_placeholder: bool) -> ty::Region<'tcx>; + + /// Creates a new region variable representing a + /// higher-ranked region that is instantiated universally. + /// This creates a new region placeholder, typically. + /// + /// So e.g., if you have `for<'a> fn(..) <: for<'b> fn(..)`, then + /// we will invoke this method to instantiate `'b` with a + /// placeholder region. + fn next_placeholder_region(&mut self, placeholder: ty::PlaceholderRegion) -> ty::Region<'tcx>; + + /// Creates a new existential region in the given universe. This + /// is used when handling subtyping and type variables -- if we + /// have that `?X <: Foo<'a>`, for example, we would instantiate + /// `?X` with a type like `Foo<'?0>` where `'?0` is a fresh + /// existential variable created by this function. We would then + /// relate `Foo<'?0>` with `Foo<'a>` (and probably add an outlives + /// relation stating that `'?0: 'a`). + fn generalize_existential(&mut self, universe: ty::UniverseIndex) -> ty::Region<'tcx>; + + /// Define the normalization strategy to use, eager or lazy. + fn normalization() -> NormalizationStrategy; + + /// Enables some optimizations if we do not expect inference variables + /// in the RHS of the relation. + fn forbid_inference_vars() -> bool; +} + +#[derive(Clone, Debug, Default)] +struct BoundRegionScope<'tcx> { + map: FxHashMap<ty::BoundRegion, ty::Region<'tcx>>, +} + +#[derive(Copy, Clone)] +struct UniversallyQuantified(bool); + +impl<'me, 'tcx, D> TypeRelating<'me, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + pub fn new( + infcx: &'me InferCtxt<'me, 'tcx>, + delegate: D, + ambient_variance: ty::Variance, + ) -> Self { + Self { + infcx, + delegate, + ambient_variance, + ambient_variance_info: ty::VarianceDiagInfo::default(), + a_scopes: vec![], + b_scopes: vec![], + } + } + + fn ambient_covariance(&self) -> bool { + match self.ambient_variance { + ty::Variance::Covariant | ty::Variance::Invariant => true, + ty::Variance::Contravariant | ty::Variance::Bivariant => false, + } + } + + fn ambient_contravariance(&self) -> bool { + match self.ambient_variance { + ty::Variance::Contravariant | ty::Variance::Invariant => true, + ty::Variance::Covariant | ty::Variance::Bivariant => false, + } + } + + fn create_scope( + &mut self, + value: ty::Binder<'tcx, impl Relate<'tcx>>, + universally_quantified: UniversallyQuantified, + ) -> BoundRegionScope<'tcx> { + let mut scope = BoundRegionScope::default(); + + // Create a callback that creates (via the delegate) either an + // existential or placeholder region as needed. + let mut next_region = { + let delegate = &mut self.delegate; + let mut lazy_universe = None; + move |br: ty::BoundRegion| { + if universally_quantified.0 { + // The first time this closure is called, create a + // new universe for the placeholders we will make + // from here out. + let universe = lazy_universe.unwrap_or_else(|| { + let universe = delegate.create_next_universe(); + lazy_universe = Some(universe); + universe + }); + + let placeholder = ty::PlaceholderRegion { universe, name: br.kind }; + delegate.next_placeholder_region(placeholder) + } else { + delegate.next_existential_region_var(true) + } + } + }; + + value.skip_binder().visit_with(&mut ScopeInstantiator { + next_region: &mut next_region, + target_index: ty::INNERMOST, + bound_region_scope: &mut scope, + }); + + scope + } + + /// When we encounter binders during the type traversal, we record + /// the value to substitute for each of the things contained in + /// that binder. (This will be either a universal placeholder or + /// an existential inference variable.) Given the De Bruijn index + /// `debruijn` (and name `br`) of some binder we have now + /// encountered, this routine finds the value that we instantiated + /// the region with; to do so, it indexes backwards into the list + /// of ambient scopes `scopes`. + fn lookup_bound_region( + debruijn: ty::DebruijnIndex, + br: &ty::BoundRegion, + first_free_index: ty::DebruijnIndex, + scopes: &[BoundRegionScope<'tcx>], + ) -> ty::Region<'tcx> { + // The debruijn index is a "reverse index" into the + // scopes listing. So when we have INNERMOST (0), we + // want the *last* scope pushed, and so forth. + let debruijn_index = debruijn.index() - first_free_index.index(); + let scope = &scopes[scopes.len() - debruijn_index - 1]; + + // Find this bound region in that scope to map to a + // particular region. + scope.map[br] + } + + /// If `r` is a bound region, find the scope in which it is bound + /// (from `scopes`) and return the value that we instantiated it + /// with. Otherwise just return `r`. + fn replace_bound_region( + &self, + r: ty::Region<'tcx>, + first_free_index: ty::DebruijnIndex, + scopes: &[BoundRegionScope<'tcx>], + ) -> ty::Region<'tcx> { + debug!("replace_bound_regions(scopes={:?})", scopes); + if let ty::ReLateBound(debruijn, br) = *r { + Self::lookup_bound_region(debruijn, &br, first_free_index, scopes) + } else { + r + } + } + + /// Push a new outlives requirement into our output set of + /// constraints. + fn push_outlives( + &mut self, + sup: ty::Region<'tcx>, + sub: ty::Region<'tcx>, + info: ty::VarianceDiagInfo<'tcx>, + ) { + debug!("push_outlives({:?}: {:?})", sup, sub); + + self.delegate.push_outlives(sup, sub, info); + } + + /// Relate a projection type and some value type lazily. This will always + /// succeed, but we push an additional `ProjectionEq` goal depending + /// on the value type: + /// - if the value type is any type `T` which is not a projection, we push + /// `ProjectionEq(projection = T)`. + /// - if the value type is another projection `other_projection`, we create + /// a new inference variable `?U` and push the two goals + /// `ProjectionEq(projection = ?U)`, `ProjectionEq(other_projection = ?U)`. + fn relate_projection_ty( + &mut self, + projection_ty: ty::ProjectionTy<'tcx>, + value_ty: Ty<'tcx>, + ) -> Ty<'tcx> { + use rustc_span::DUMMY_SP; + + match *value_ty.kind() { + ty::Projection(other_projection_ty) => { + let var = self.infcx.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: DUMMY_SP, + }); + // FIXME(lazy-normalization): This will always ICE, because the recursive + // call will end up in the _ arm below. + self.relate_projection_ty(projection_ty, var); + self.relate_projection_ty(other_projection_ty, var); + var + } + + _ => bug!("should never be invoked with eager normalization"), + } + } + + /// Relate a type inference variable with a value type. This works + /// by creating a "generalization" G of the value where all the + /// lifetimes are replaced with fresh inference values. This + /// generalization G becomes the value of the inference variable, + /// and is then related in turn to the value. So e.g. if you had + /// `vid = ?0` and `value = &'a u32`, we might first instantiate + /// `?0` to a type like `&'0 u32` where `'0` is a fresh variable, + /// and then relate `&'0 u32` with `&'a u32` (resulting in + /// relations between `'0` and `'a`). + /// + /// The variable `pair` can be either a `(vid, ty)` or `(ty, vid)` + /// -- in other words, it is always an (unresolved) inference + /// variable `vid` and a type `ty` that are being related, but the + /// vid may appear either as the "a" type or the "b" type, + /// depending on where it appears in the tuple. The trait + /// `VidValuePair` lets us work with the vid/type while preserving + /// the "sidedness" when necessary -- the sidedness is relevant in + /// particular for the variance and set of in-scope things. + fn relate_ty_var<PAIR: VidValuePair<'tcx>>( + &mut self, + pair: PAIR, + ) -> RelateResult<'tcx, Ty<'tcx>> { + debug!("relate_ty_var({:?})", pair); + + let vid = pair.vid(); + let value_ty = pair.value_ty(); + + // FIXME(invariance) -- this logic assumes invariance, but that is wrong. + // This only presently applies to chalk integration, as NLL + // doesn't permit type variables to appear on both sides (and + // doesn't use lazy norm). + match *value_ty.kind() { + ty::Infer(ty::TyVar(value_vid)) => { + // Two type variables: just equate them. + self.infcx.inner.borrow_mut().type_variables().equate(vid, value_vid); + return Ok(value_ty); + } + + ty::Projection(projection_ty) if D::normalization() == NormalizationStrategy::Lazy => { + return Ok(self.relate_projection_ty(projection_ty, self.infcx.tcx.mk_ty_var(vid))); + } + + _ => (), + } + + let generalized_ty = self.generalize_value(value_ty, vid)?; + debug!("relate_ty_var: generalized_ty = {:?}", generalized_ty); + + if D::forbid_inference_vars() { + // In NLL, we don't have type inference variables + // floating around, so we can do this rather imprecise + // variant of the occurs-check. + assert!(!generalized_ty.has_infer_types_or_consts()); + } + + self.infcx.inner.borrow_mut().type_variables().instantiate(vid, generalized_ty); + + // The generalized values we extract from `canonical_var_values` have + // been fully instantiated and hence the set of scopes we have + // doesn't matter -- just to be sure, put an empty vector + // in there. + let old_a_scopes = std::mem::take(pair.vid_scopes(self)); + + // Relate the generalized kind to the original one. + let result = pair.relate_generalized_ty(self, generalized_ty); + + // Restore the old scopes now. + *pair.vid_scopes(self) = old_a_scopes; + + debug!("relate_ty_var: complete, result = {:?}", result); + result + } + + fn generalize_value<T: Relate<'tcx>>( + &mut self, + value: T, + for_vid: ty::TyVid, + ) -> RelateResult<'tcx, T> { + let universe = self.infcx.probe_ty_var(for_vid).unwrap_err(); + + let mut generalizer = TypeGeneralizer { + infcx: self.infcx, + delegate: &mut self.delegate, + first_free_index: ty::INNERMOST, + ambient_variance: self.ambient_variance, + for_vid_sub_root: self.infcx.inner.borrow_mut().type_variables().sub_root_var(for_vid), + universe, + }; + + generalizer.relate(value, value) + } +} + +/// When we instantiate an inference variable with a value in +/// `relate_ty_var`, we always have the pair of a `TyVid` and a `Ty`, +/// but the ordering may vary (depending on whether the inference +/// variable was found on the `a` or `b` sides). Therefore, this trait +/// allows us to factor out common code, while preserving the order +/// when needed. +trait VidValuePair<'tcx>: Debug { + /// Extract the inference variable (which could be either the + /// first or second part of the tuple). + fn vid(&self) -> ty::TyVid; + + /// Extract the value it is being related to (which will be the + /// opposite part of the tuple from the vid). + fn value_ty(&self) -> Ty<'tcx>; + + /// Extract the scopes that apply to whichever side of the tuple + /// the vid was found on. See the comment where this is called + /// for more details on why we want them. + fn vid_scopes<'r, D: TypeRelatingDelegate<'tcx>>( + &self, + relate: &'r mut TypeRelating<'_, 'tcx, D>, + ) -> &'r mut Vec<BoundRegionScope<'tcx>>; + + /// Given a generalized type G that should replace the vid, relate + /// G to the value, putting G on whichever side the vid would have + /// appeared. + fn relate_generalized_ty<D>( + &self, + relate: &mut TypeRelating<'_, 'tcx, D>, + generalized_ty: Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> + where + D: TypeRelatingDelegate<'tcx>; +} + +impl<'tcx> VidValuePair<'tcx> for (ty::TyVid, Ty<'tcx>) { + fn vid(&self) -> ty::TyVid { + self.0 + } + + fn value_ty(&self) -> Ty<'tcx> { + self.1 + } + + fn vid_scopes<'r, D>( + &self, + relate: &'r mut TypeRelating<'_, 'tcx, D>, + ) -> &'r mut Vec<BoundRegionScope<'tcx>> + where + D: TypeRelatingDelegate<'tcx>, + { + &mut relate.a_scopes + } + + fn relate_generalized_ty<D>( + &self, + relate: &mut TypeRelating<'_, 'tcx, D>, + generalized_ty: Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> + where + D: TypeRelatingDelegate<'tcx>, + { + relate.relate(generalized_ty, self.value_ty()) + } +} + +// In this case, the "vid" is the "b" type. +impl<'tcx> VidValuePair<'tcx> for (Ty<'tcx>, ty::TyVid) { + fn vid(&self) -> ty::TyVid { + self.1 + } + + fn value_ty(&self) -> Ty<'tcx> { + self.0 + } + + fn vid_scopes<'r, D>( + &self, + relate: &'r mut TypeRelating<'_, 'tcx, D>, + ) -> &'r mut Vec<BoundRegionScope<'tcx>> + where + D: TypeRelatingDelegate<'tcx>, + { + &mut relate.b_scopes + } + + fn relate_generalized_ty<D>( + &self, + relate: &mut TypeRelating<'_, 'tcx, D>, + generalized_ty: Ty<'tcx>, + ) -> RelateResult<'tcx, Ty<'tcx>> + where + D: TypeRelatingDelegate<'tcx>, + { + relate.relate(self.value_ty(), generalized_ty) + } +} + +impl<'tcx, D> TypeRelation<'tcx> for TypeRelating<'_, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.delegate.param_env() + } + + fn tag(&self) -> &'static str { + "nll::subtype" + } + + fn a_is_expected(&self) -> bool { + true + } + + #[instrument(skip(self, info), level = "trace")] + fn relate_with_variance<T: Relate<'tcx>>( + &mut self, + variance: ty::Variance, + info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + self.ambient_variance_info = self.ambient_variance_info.xform(info); + + debug!(?self.ambient_variance); + + let r = self.relate(a, b)?; + + self.ambient_variance = old_ambient_variance; + + debug!(?r); + + Ok(r) + } + + #[instrument(skip(self), level = "debug")] + fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + let infcx = self.infcx; + + let a = self.infcx.shallow_resolve(a); + + if !D::forbid_inference_vars() { + b = self.infcx.shallow_resolve(b); + } + + if a == b { + // Subtle: if a or b has a bound variable that we are lazily + // substituting, then even if a == b, it could be that the values we + // will substitute for those bound variables are *not* the same, and + // hence returning `Ok(a)` is incorrect. + if !a.has_escaping_bound_vars() && !b.has_escaping_bound_vars() { + return Ok(a); + } + } + + match (a.kind(), b.kind()) { + (_, &ty::Infer(ty::TyVar(vid))) => { + if D::forbid_inference_vars() { + // Forbid inference variables in the RHS. + bug!("unexpected inference var {:?}", b) + } else { + self.relate_ty_var((a, vid)) + } + } + + (&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var((vid, b)), + + (&ty::Opaque(a_def_id, _), &ty::Opaque(b_def_id, _)) if a_def_id == b_def_id => { + self.infcx.super_combine_tys(self, a, b) + } + (&ty::Opaque(did, ..), _) | (_, &ty::Opaque(did, ..)) if did.is_local() => { + let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) }; + let mut generalize = |ty, ty_is_expected| { + let var = infcx.next_ty_var_id_in_universe( + TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: self.delegate.span(), + }, + ty::UniverseIndex::ROOT, + ); + if ty_is_expected { + self.relate_ty_var((ty, var)) + } else { + self.relate_ty_var((var, ty)) + } + }; + let (a, b) = match (a.kind(), b.kind()) { + (&ty::Opaque(..), _) => (a, generalize(b, false)?), + (_, &ty::Opaque(..)) => (generalize(a, true)?, b), + _ => unreachable!(), + }; + self.delegate.register_opaque_type(a, b, true)?; + trace!(a = ?a.kind(), b = ?b.kind(), "opaque type instantiated"); + Ok(a) + } + + (&ty::Projection(projection_ty), _) + if D::normalization() == NormalizationStrategy::Lazy => + { + Ok(self.relate_projection_ty(projection_ty, b)) + } + + (_, &ty::Projection(projection_ty)) + if D::normalization() == NormalizationStrategy::Lazy => + { + Ok(self.relate_projection_ty(projection_ty, a)) + } + + _ => { + debug!(?a, ?b, ?self.ambient_variance); + + // Will also handle unification of `IntVar` and `FloatVar`. + self.infcx.super_combine_tys(self, a, b) + } + } + } + + #[instrument(skip(self), level = "trace")] + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!(?self.ambient_variance); + + let v_a = self.replace_bound_region(a, ty::INNERMOST, &self.a_scopes); + let v_b = self.replace_bound_region(b, ty::INNERMOST, &self.b_scopes); + + debug!(?v_a); + debug!(?v_b); + + if self.ambient_covariance() { + // Covariance: a <= b. Hence, `b: a`. + self.push_outlives(v_b, v_a, self.ambient_variance_info); + } + + if self.ambient_contravariance() { + // Contravariant: b <= a. Hence, `a: b`. + self.push_outlives(v_a, v_b, self.ambient_variance_info); + } + + Ok(a) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + mut b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + let a = self.infcx.shallow_resolve(a); + + if !D::forbid_inference_vars() { + b = self.infcx.shallow_resolve(b); + } + + match b.kind() { + ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { + // Forbid inference variables in the RHS. + self.infcx.tcx.sess.delay_span_bug( + self.delegate.span(), + format!("unexpected inference var {:?}", b,), + ); + Ok(a) + } + // FIXME(invariance): see the related FIXME above. + _ => self.infcx.super_combine_consts(self, a, b), + } + } + + #[instrument(skip(self), level = "trace")] + fn binders<T>( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + // We want that + // + // ``` + // for<'a> fn(&'a u32) -> &'a u32 <: + // fn(&'b u32) -> &'b u32 + // ``` + // + // but not + // + // ``` + // fn(&'a u32) -> &'a u32 <: + // for<'b> fn(&'b u32) -> &'b u32 + // ``` + // + // We therefore proceed as follows: + // + // - Instantiate binders on `b` universally, yielding a universe U1. + // - Instantiate binders on `a` existentially in U1. + + debug!(?self.ambient_variance); + + if let (Some(a), Some(b)) = (a.no_bound_vars(), b.no_bound_vars()) { + // Fast path for the common case. + self.relate(a, b)?; + return Ok(ty::Binder::dummy(a)); + } + + if self.ambient_covariance() { + // Covariance, so we want `for<..> A <: for<..> B` -- + // therefore we compare any instantiation of A (i.e., A + // instantiated with existentials) against every + // instantiation of B (i.e., B instantiated with + // universals). + + let b_scope = self.create_scope(b, UniversallyQuantified(true)); + let a_scope = self.create_scope(a, UniversallyQuantified(false)); + + debug!(?a_scope, "(existential)"); + debug!(?b_scope, "(universal)"); + + self.b_scopes.push(b_scope); + self.a_scopes.push(a_scope); + + // Reset the ambient variance to covariant. This is needed + // to correctly handle cases like + // + // for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32) + // + // Somewhat surprisingly, these two types are actually + // **equal**, even though the one on the right looks more + // polymorphic. The reason is due to subtyping. To see it, + // consider that each function can call the other: + // + // - The left function can call the right with `'b` and + // `'c` both equal to `'a` + // + // - The right function can call the left with `'a` set to + // `{P}`, where P is the point in the CFG where the call + // itself occurs. Note that `'b` and `'c` must both + // include P. At the point, the call works because of + // subtyping (i.e., `&'b u32 <: &{P} u32`). + let variance = std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant); + + self.relate(a.skip_binder(), b.skip_binder())?; + + self.ambient_variance = variance; + + self.b_scopes.pop().unwrap(); + self.a_scopes.pop().unwrap(); + } + + if self.ambient_contravariance() { + // Contravariance, so we want `for<..> A :> for<..> B` + // -- therefore we compare every instantiation of A (i.e., + // A instantiated with universals) against any + // instantiation of B (i.e., B instantiated with + // existentials). Opposite of above. + + let a_scope = self.create_scope(a, UniversallyQuantified(true)); + let b_scope = self.create_scope(b, UniversallyQuantified(false)); + + debug!(?a_scope, "(universal)"); + debug!(?b_scope, "(existential)"); + + self.a_scopes.push(a_scope); + self.b_scopes.push(b_scope); + + // Reset ambient variance to contravariance. See the + // covariant case above for an explanation. + let variance = + std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant); + + self.relate(a.skip_binder(), b.skip_binder())?; + + self.ambient_variance = variance; + + self.b_scopes.pop().unwrap(); + self.a_scopes.pop().unwrap(); + } + + Ok(a) + } +} + +impl<'tcx, D> ConstEquateRelation<'tcx> for TypeRelating<'_, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { + self.delegate.const_equate(a, b); + } +} + +/// When we encounter a binder like `for<..> fn(..)`, we actually have +/// to walk the `fn` value to find all the values bound by the `for` +/// (these are not explicitly present in the ty representation right +/// now). This visitor handles that: it descends the type, tracking +/// binder depth, and finds late-bound regions targeting the +/// `for<..`>. For each of those, it creates an entry in +/// `bound_region_scope`. +struct ScopeInstantiator<'me, 'tcx> { + next_region: &'me mut dyn FnMut(ty::BoundRegion) -> ty::Region<'tcx>, + // The debruijn index of the scope we are instantiating. + target_index: ty::DebruijnIndex, + bound_region_scope: &'me mut BoundRegionScope<'tcx>, +} + +impl<'me, 'tcx> TypeVisitor<'tcx> for ScopeInstantiator<'me, 'tcx> { + fn visit_binder<T: TypeVisitable<'tcx>>( + &mut self, + t: &ty::Binder<'tcx, T>, + ) -> ControlFlow<Self::BreakTy> { + self.target_index.shift_in(1); + t.super_visit_with(self); + self.target_index.shift_out(1); + + ControlFlow::CONTINUE + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + let ScopeInstantiator { bound_region_scope, next_region, .. } = self; + + match *r { + ty::ReLateBound(debruijn, br) if debruijn == self.target_index => { + bound_region_scope.map.entry(br).or_insert_with(|| next_region(br)); + } + + _ => {} + } + + ControlFlow::CONTINUE + } +} + +/// The "type generalizer" is used when handling inference variables. +/// +/// The basic strategy for handling a constraint like `?A <: B` is to +/// apply a "generalization strategy" to the type `B` -- this replaces +/// all the lifetimes in the type `B` with fresh inference +/// variables. (You can read more about the strategy in this [blog +/// post].) +/// +/// As an example, if we had `?A <: &'x u32`, we would generalize `&'x +/// u32` to `&'0 u32` where `'0` is a fresh variable. This becomes the +/// value of `A`. Finally, we relate `&'0 u32 <: &'x u32`, which +/// establishes `'0: 'x` as a constraint. +/// +/// As a side-effect of this generalization procedure, we also replace +/// all the bound regions that we have traversed with concrete values, +/// so that the resulting generalized type is independent from the +/// scopes. +/// +/// [blog post]: https://is.gd/0hKvIr +struct TypeGeneralizer<'me, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + infcx: &'me InferCtxt<'me, 'tcx>, + + delegate: &'me mut D, + + /// After we generalize this type, we are going to relate it to + /// some other type. What will be the variance at this point? + ambient_variance: ty::Variance, + + first_free_index: ty::DebruijnIndex, + + /// The vid of the type variable that is in the process of being + /// instantiated. If we find this within the value we are folding, + /// that means we would have created a cyclic value. + for_vid_sub_root: ty::TyVid, + + /// The universe of the type variable that is in the process of being + /// instantiated. If we find anything that this universe cannot name, + /// we reject the relation. + universe: ty::UniverseIndex, +} + +impl<'tcx, D> TypeRelation<'tcx> for TypeGeneralizer<'_, 'tcx, D> +where + D: TypeRelatingDelegate<'tcx>, +{ + fn tcx(&self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.delegate.param_env() + } + + fn tag(&self) -> &'static str { + "nll::generalizer" + } + + fn a_is_expected(&self) -> bool { + true + } + + fn relate_with_variance<T: Relate<'tcx>>( + &mut self, + variance: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + debug!( + "TypeGeneralizer::relate_with_variance(variance={:?}, a={:?}, b={:?})", + variance, a, b + ); + + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + + debug!( + "TypeGeneralizer::relate_with_variance: ambient_variance = {:?}", + self.ambient_variance + ); + + let r = self.relate(a, b)?; + + self.ambient_variance = old_ambient_variance; + + debug!("TypeGeneralizer::relate_with_variance: r={:?}", r); + + Ok(r) + } + + fn tys(&mut self, a: Ty<'tcx>, _: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + use crate::infer::type_variable::TypeVariableValue; + + debug!("TypeGeneralizer::tys(a={:?})", a); + + match *a.kind() { + ty::Infer(ty::TyVar(_)) | ty::Infer(ty::IntVar(_)) | ty::Infer(ty::FloatVar(_)) + if D::forbid_inference_vars() => + { + bug!("unexpected inference variable encountered in NLL generalization: {:?}", a); + } + + ty::Infer(ty::TyVar(vid)) => { + let mut inner = self.infcx.inner.borrow_mut(); + let variables = &mut inner.type_variables(); + let vid = variables.root_var(vid); + let sub_vid = variables.sub_root_var(vid); + if sub_vid == self.for_vid_sub_root { + // If sub-roots are equal, then `for_vid` and + // `vid` are related via subtyping. + debug!("TypeGeneralizer::tys: occurs check failed"); + Err(TypeError::Mismatch) + } else { + match variables.probe(vid) { + TypeVariableValue::Known { value: u } => { + drop(variables); + self.relate(u, u) + } + TypeVariableValue::Unknown { universe: _universe } => { + if self.ambient_variance == ty::Bivariant { + // FIXME: we may need a WF predicate (related to #54105). + } + + let origin = *variables.var_origin(vid); + + // Replacing with a new variable in the universe `self.universe`, + // it will be unified later with the original type variable in + // the universe `_universe`. + let new_var_id = variables.new_var(self.universe, origin); + + let u = self.tcx().mk_ty_var(new_var_id); + debug!("generalize: replacing original vid={:?} with new={:?}", vid, u); + Ok(u) + } + } + } + } + + ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) => { + // No matter what mode we are in, + // integer/floating-point types must be equal to be + // relatable. + Ok(a) + } + + ty::Placeholder(placeholder) => { + if self.universe.cannot_name(placeholder.universe) { + debug!( + "TypeGeneralizer::tys: root universe {:?} cannot name\ + placeholder in universe {:?}", + self.universe, placeholder.universe + ); + Err(TypeError::Mismatch) + } else { + Ok(a) + } + } + + _ => relate::super_relate_tys(self, a, a), + } + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + _: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("TypeGeneralizer::regions(a={:?})", a); + + if let ty::ReLateBound(debruijn, _) = *a && debruijn < self.first_free_index { + return Ok(a); + } + + // For now, we just always create a fresh region variable to + // replace all the regions in the source type. In the main + // type checker, we special case the case where the ambient + // variance is `Invariant` and try to avoid creating a fresh + // region variable, but since this comes up so much less in + // NLL (only when users use `_` etc) it is much less + // important. + // + // As an aside, since these new variables are created in + // `self.universe` universe, this also serves to enforce the + // universe scoping rules. + // + // FIXME(#54105) -- if the ambient variance is bivariant, + // though, we may however need to check well-formedness or + // risk a problem like #41677 again. + + let replacement_region_vid = self.delegate.generalize_existential(self.universe); + + Ok(replacement_region_vid) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + _: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + match a.kind() { + ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { + bug!("unexpected inference variable encountered in NLL generalization: {:?}", a); + } + ty::ConstKind::Infer(InferConst::Var(vid)) => { + let mut inner = self.infcx.inner.borrow_mut(); + let variable_table = &mut inner.const_unification_table(); + let var_value = variable_table.probe_value(vid); + match var_value.val.known() { + Some(u) => self.relate(u, u), + None => { + let new_var_id = variable_table.new_key(ConstVarValue { + origin: var_value.origin, + val: ConstVariableValue::Unknown { universe: self.universe }, + }); + Ok(self.tcx().mk_const_var(new_var_id, a.ty())) + } + } + } + ty::ConstKind::Unevaluated(..) if self.tcx().lazy_normalization() => Ok(a), + _ => relate::super_relate_consts(self, a, a), + } + } + + fn binders<T>( + &mut self, + a: ty::Binder<'tcx, T>, + _: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + debug!("TypeGeneralizer::binders(a={:?})", a); + + self.first_free_index.shift_in(1); + let result = self.relate(a.skip_binder(), a.skip_binder())?; + self.first_free_index.shift_out(1); + Ok(a.rebind(result)) + } +} diff --git a/compiler/rustc_infer/src/infer/opaque_types.rs b/compiler/rustc_infer/src/infer/opaque_types.rs new file mode 100644 index 000000000..e579afbf3 --- /dev/null +++ b/compiler/rustc_infer/src/infer/opaque_types.rs @@ -0,0 +1,649 @@ +use crate::infer::{DefiningAnchor, InferCtxt, InferOk}; +use crate::traits; +use hir::def_id::{DefId, LocalDefId}; +use hir::{HirId, OpaqueTyOrigin}; +use rustc_data_structures::sync::Lrc; +use rustc_data_structures::vec_map::VecMap; +use rustc_hir as hir; +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::fold::BottomUpFolder; +use rustc_middle::ty::subst::{GenericArgKind, Subst}; +use rustc_middle::ty::{ + self, OpaqueHiddenType, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeSuperVisitable, + TypeVisitable, TypeVisitor, +}; +use rustc_span::Span; + +use std::ops::ControlFlow; + +pub type OpaqueTypeMap<'tcx> = VecMap<OpaqueTypeKey<'tcx>, OpaqueTypeDecl<'tcx>>; + +mod table; + +pub use table::{OpaqueTypeStorage, OpaqueTypeTable}; + +use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use super::InferResult; + +/// Information about the opaque types whose values we +/// are inferring in this function (these are the `impl Trait` that +/// appear in the return type). +#[derive(Clone, Debug)] +pub struct OpaqueTypeDecl<'tcx> { + /// The hidden types that have been inferred for this opaque type. + /// There can be multiple, but they are all `lub`ed together at the end + /// to obtain the canonical hidden type. + pub hidden_type: OpaqueHiddenType<'tcx>, + + /// The origin of the opaque type. + pub origin: hir::OpaqueTyOrigin, +} + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + /// This is a backwards compatibility hack to prevent breaking changes from + /// lazy TAIT around RPIT handling. + pub fn replace_opaque_types_with_inference_vars<T: TypeFoldable<'tcx>>( + &self, + value: T, + body_id: HirId, + span: Span, + param_env: ty::ParamEnv<'tcx>, + ) -> InferOk<'tcx, T> { + if !value.has_opaque_types() { + return InferOk { value, obligations: vec![] }; + } + let mut obligations = vec![]; + let replace_opaque_type = |def_id: DefId| { + def_id + .as_local() + .map_or(false, |def_id| self.opaque_type_origin(def_id, span).is_some()) + }; + let value = value.fold_with(&mut ty::fold::BottomUpFolder { + tcx: self.tcx, + lt_op: |lt| lt, + ct_op: |ct| ct, + ty_op: |ty| match *ty.kind() { + ty::Opaque(def_id, _substs) if replace_opaque_type(def_id) => { + let def_span = self.tcx.def_span(def_id); + let span = if span.contains(def_span) { def_span } else { span }; + let code = traits::ObligationCauseCode::OpaqueReturnType(None); + let cause = ObligationCause::new(span, body_id, code); + // FIXME(compiler-errors): We probably should add a new TypeVariableOriginKind + // for opaque types, and then use that kind to fix the spans for type errors + // that we see later on. + let ty_var = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeInference, + span, + }); + obligations.extend( + self.handle_opaque_type(ty, ty_var, true, &cause, param_env) + .unwrap() + .obligations, + ); + ty_var + } + _ => ty, + }, + }); + InferOk { value, obligations } + } + + pub fn handle_opaque_type( + &self, + a: Ty<'tcx>, + b: Ty<'tcx>, + a_is_expected: bool, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) -> InferResult<'tcx, ()> { + if a.references_error() || b.references_error() { + return Ok(InferOk { value: (), obligations: vec![] }); + } + let (a, b) = if a_is_expected { (a, b) } else { (b, a) }; + let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() { + ty::Opaque(def_id, substs) if def_id.is_local() => { + let def_id = def_id.expect_local(); + let origin = match self.defining_use_anchor { + DefiningAnchor::Bind(_) => { + // Check that this is `impl Trait` type is + // declared by `parent_def_id` -- i.e., one whose + // value we are inferring. At present, this is + // always true during the first phase of + // type-check, but not always true later on during + // NLL. Once we support named opaque types more fully, + // this same scenario will be able to arise during all phases. + // + // Here is an example using type alias `impl Trait` + // that indicates the distinction we are checking for: + // + // ```rust + // mod a { + // pub type Foo = impl Iterator; + // pub fn make_foo() -> Foo { .. } + // } + // + // mod b { + // fn foo() -> a::Foo { a::make_foo() } + // } + // ``` + // + // Here, the return type of `foo` references an + // `Opaque` indeed, but not one whose value is + // presently being inferred. You can get into a + // similar situation with closure return types + // today: + // + // ```rust + // fn foo() -> impl Iterator { .. } + // fn bar() { + // let x = || foo(); // returns the Opaque assoc with `foo` + // } + // ``` + self.opaque_type_origin(def_id, cause.span)? + } + DefiningAnchor::Bubble => self.opaque_ty_origin_unchecked(def_id, cause.span), + DefiningAnchor::Error => return None, + }; + if let ty::Opaque(did2, _) = *b.kind() { + // We could accept this, but there are various ways to handle this situation, and we don't + // want to make a decision on it right now. Likely this case is so super rare anyway, that + // no one encounters it in practice. + // It does occur however in `fn fut() -> impl Future<Output = i32> { async { 42 } }`, + // where it is of no concern, so we only check for TAITs. + if let Some(OpaqueTyOrigin::TyAlias) = + did2.as_local().and_then(|did2| self.opaque_type_origin(did2, cause.span)) + { + self.tcx + .sess + .struct_span_err( + cause.span, + "opaque type's hidden type cannot be another opaque type from the same scope", + ) + .span_label(cause.span, "one of the two opaque types used here has to be outside its defining scope") + .span_note( + self.tcx.def_span(def_id), + "opaque type whose hidden type is being assigned", + ) + .span_note( + self.tcx.def_span(did2), + "opaque type being used as hidden type", + ) + .emit(); + } + } + Some(self.register_hidden_type( + OpaqueTypeKey { def_id, substs }, + cause.clone(), + param_env, + b, + origin, + )) + } + _ => None, + }; + if let Some(res) = process(a, b) { + res + } else if let Some(res) = process(b, a) { + res + } else { + // Rerun equality check, but this time error out due to + // different types. + match self.at(cause, param_env).define_opaque_types(false).eq(a, b) { + Ok(_) => span_bug!( + cause.span, + "opaque types are never equal to anything but themselves: {:#?}", + (a.kind(), b.kind()) + ), + Err(e) => Err(e), + } + } + } + + /// Given the map `opaque_types` containing the opaque + /// `impl Trait` types whose underlying, hidden types are being + /// inferred, this method adds constraints to the regions + /// appearing in those underlying hidden types to ensure that they + /// at least do not refer to random scopes within the current + /// function. These constraints are not (quite) sufficient to + /// guarantee that the regions are actually legal values; that + /// final condition is imposed after region inference is done. + /// + /// # The Problem + /// + /// Let's work through an example to explain how it works. Assume + /// the current function is as follows: + /// + /// ```text + /// fn foo<'a, 'b>(..) -> (impl Bar<'a>, impl Bar<'b>) + /// ``` + /// + /// Here, we have two `impl Trait` types whose values are being + /// inferred (the `impl Bar<'a>` and the `impl + /// Bar<'b>`). Conceptually, this is sugar for a setup where we + /// define underlying opaque types (`Foo1`, `Foo2`) and then, in + /// the return type of `foo`, we *reference* those definitions: + /// + /// ```text + /// type Foo1<'x> = impl Bar<'x>; + /// type Foo2<'x> = impl Bar<'x>; + /// fn foo<'a, 'b>(..) -> (Foo1<'a>, Foo2<'b>) { .. } + /// // ^^^^ ^^ + /// // | | + /// // | substs + /// // def_id + /// ``` + /// + /// As indicating in the comments above, each of those references + /// is (in the compiler) basically a substitution (`substs`) + /// applied to the type of a suitable `def_id` (which identifies + /// `Foo1` or `Foo2`). + /// + /// Now, at this point in compilation, what we have done is to + /// replace each of the references (`Foo1<'a>`, `Foo2<'b>`) with + /// fresh inference variables C1 and C2. We wish to use the values + /// of these variables to infer the underlying types of `Foo1` and + /// `Foo2`. That is, this gives rise to higher-order (pattern) unification + /// constraints like: + /// + /// ```text + /// for<'a> (Foo1<'a> = C1) + /// for<'b> (Foo1<'b> = C2) + /// ``` + /// + /// For these equation to be satisfiable, the types `C1` and `C2` + /// can only refer to a limited set of regions. For example, `C1` + /// can only refer to `'static` and `'a`, and `C2` can only refer + /// to `'static` and `'b`. The job of this function is to impose that + /// constraint. + /// + /// Up to this point, C1 and C2 are basically just random type + /// inference variables, and hence they may contain arbitrary + /// regions. In fact, it is fairly likely that they do! Consider + /// this possible definition of `foo`: + /// + /// ```text + /// fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> (impl Bar<'a>, impl Bar<'b>) { + /// (&*x, &*y) + /// } + /// ``` + /// + /// Here, the values for the concrete types of the two impl + /// traits will include inference variables: + /// + /// ```text + /// &'0 i32 + /// &'1 i32 + /// ``` + /// + /// Ordinarily, the subtyping rules would ensure that these are + /// sufficiently large. But since `impl Bar<'a>` isn't a specific + /// type per se, we don't get such constraints by default. This + /// is where this function comes into play. It adds extra + /// constraints to ensure that all the regions which appear in the + /// inferred type are regions that could validly appear. + /// + /// This is actually a bit of a tricky constraint in general. We + /// want to say that each variable (e.g., `'0`) can only take on + /// values that were supplied as arguments to the opaque type + /// (e.g., `'a` for `Foo1<'a>`) or `'static`, which is always in + /// scope. We don't have a constraint quite of this kind in the current + /// region checker. + /// + /// # The Solution + /// + /// We generally prefer to make `<=` constraints, since they + /// integrate best into the region solver. To do that, we find the + /// "minimum" of all the arguments that appear in the substs: that + /// is, some region which is less than all the others. In the case + /// of `Foo1<'a>`, that would be `'a` (it's the only choice, after + /// all). Then we apply that as a least bound to the variables + /// (e.g., `'a <= '0`). + /// + /// In some cases, there is no minimum. Consider this example: + /// + /// ```text + /// fn baz<'a, 'b>() -> impl Trait<'a, 'b> { ... } + /// ``` + /// + /// Here we would report a more complex "in constraint", like `'r + /// in ['a, 'b, 'static]` (where `'r` is some region appearing in + /// the hidden type). + /// + /// # Constrain regions, not the hidden concrete type + /// + /// Note that generating constraints on each region `Rc` is *not* + /// the same as generating an outlives constraint on `Tc` itself. + /// For example, if we had a function like this: + /// + /// ``` + /// # #![feature(type_alias_impl_trait)] + /// # fn main() {} + /// # trait Foo<'a> {} + /// # impl<'a, T> Foo<'a> for (&'a u32, T) {} + /// fn foo<'a, T>(x: &'a u32, y: T) -> impl Foo<'a> { + /// (x, y) + /// } + /// + /// // Equivalent to: + /// # mod dummy { use super::*; + /// type FooReturn<'a, T> = impl Foo<'a>; + /// fn foo<'a, T>(x: &'a u32, y: T) -> FooReturn<'a, T> { + /// (x, y) + /// } + /// # } + /// ``` + /// + /// then the hidden type `Tc` would be `(&'0 u32, T)` (where `'0` + /// is an inference variable). If we generated a constraint that + /// `Tc: 'a`, then this would incorrectly require that `T: 'a` -- + /// but this is not necessary, because the opaque type we + /// create will be allowed to reference `T`. So we only generate a + /// constraint that `'0: 'a`. + #[instrument(level = "debug", skip(self))] + pub fn register_member_constraints( + &self, + param_env: ty::ParamEnv<'tcx>, + opaque_type_key: OpaqueTypeKey<'tcx>, + concrete_ty: Ty<'tcx>, + span: Span, + ) { + let def_id = opaque_type_key.def_id; + + let tcx = self.tcx; + + let concrete_ty = self.resolve_vars_if_possible(concrete_ty); + + debug!(?concrete_ty); + + let first_own_region = match self.opaque_ty_origin_unchecked(def_id, span) { + hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => { + // We lower + // + // fn foo<'l0..'ln>() -> impl Trait<'l0..'lm> + // + // into + // + // type foo::<'p0..'pn>::Foo<'q0..'qm> + // fn foo<l0..'ln>() -> foo::<'static..'static>::Foo<'l0..'lm>. + // + // For these types we only iterate over `'l0..lm` below. + tcx.generics_of(def_id).parent_count + } + // These opaque type inherit all lifetime parameters from their + // parent, so we have to check them all. + hir::OpaqueTyOrigin::TyAlias => 0, + }; + + // For a case like `impl Foo<'a, 'b>`, we would generate a constraint + // `'r in ['a, 'b, 'static]` for each region `'r` that appears in the + // hidden type (i.e., it must be equal to `'a`, `'b`, or `'static`). + // + // `conflict1` and `conflict2` are the two region bounds that we + // detected which were unrelated. They are used for diagnostics. + + // Create the set of choice regions: each region in the hidden + // type can be equal to any of the region parameters of the + // opaque type definition. + let choice_regions: Lrc<Vec<ty::Region<'tcx>>> = Lrc::new( + opaque_type_key.substs[first_own_region..] + .iter() + .filter_map(|arg| match arg.unpack() { + GenericArgKind::Lifetime(r) => Some(r), + GenericArgKind::Type(_) | GenericArgKind::Const(_) => None, + }) + .chain(std::iter::once(self.tcx.lifetimes.re_static)) + .collect(), + ); + + concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor { + op: |r| self.member_constraint(opaque_type_key, span, concrete_ty, r, &choice_regions), + }); + } + + #[instrument(skip(self), level = "trace")] + pub fn opaque_type_origin(&self, def_id: LocalDefId, span: Span) -> Option<OpaqueTyOrigin> { + let opaque_hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); + let parent_def_id = match self.defining_use_anchor { + DefiningAnchor::Bubble | DefiningAnchor::Error => return None, + DefiningAnchor::Bind(bind) => bind, + }; + let item_kind = &self.tcx.hir().expect_item(def_id).kind; + + let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else { + span_bug!( + span, + "weird opaque type: {:#?}, {:#?}", + def_id, + item_kind + ) + }; + let in_definition_scope = match *origin { + // Async `impl Trait` + hir::OpaqueTyOrigin::AsyncFn(parent) => parent == parent_def_id, + // Anonymous `impl Trait` + hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id, + // Named `type Foo = impl Bar;` + hir::OpaqueTyOrigin::TyAlias => { + may_define_opaque_type(self.tcx, parent_def_id, opaque_hir_id) + } + }; + trace!(?origin); + in_definition_scope.then_some(*origin) + } + + #[instrument(skip(self), level = "trace")] + fn opaque_ty_origin_unchecked(&self, def_id: LocalDefId, span: Span) -> OpaqueTyOrigin { + let origin = match self.tcx.hir().expect_item(def_id).kind { + hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => origin, + ref itemkind => { + span_bug!(span, "weird opaque type: {:?}, {:#?}", def_id, itemkind) + } + }; + trace!(?origin); + origin + } +} + +// Visitor that requires that (almost) all regions in the type visited outlive +// `least_region`. We cannot use `push_outlives_components` because regions in +// closure signatures are not included in their outlives components. We need to +// ensure all regions outlive the given bound so that we don't end up with, +// say, `ReVar` appearing in a return type and causing ICEs when other +// functions end up with region constraints involving regions from other +// functions. +// +// We also cannot use `for_each_free_region` because for closures it includes +// the regions parameters from the enclosing item. +// +// We ignore any type parameters because impl trait values are assumed to +// capture all the in-scope type parameters. +struct ConstrainOpaqueTypeRegionVisitor<OP> { + op: OP, +} + +impl<'tcx, OP> TypeVisitor<'tcx> for ConstrainOpaqueTypeRegionVisitor<OP> +where + OP: FnMut(ty::Region<'tcx>), +{ + fn visit_binder<T: TypeVisitable<'tcx>>( + &mut self, + t: &ty::Binder<'tcx, T>, + ) -> ControlFlow<Self::BreakTy> { + t.super_visit_with(self); + ControlFlow::CONTINUE + } + + fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { + match *r { + // ignore bound regions, keep visiting + ty::ReLateBound(_, _) => ControlFlow::CONTINUE, + _ => { + (self.op)(r); + ControlFlow::CONTINUE + } + } + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + // We're only interested in types involving regions + if !ty.flags().intersects(ty::TypeFlags::HAS_FREE_REGIONS) { + return ControlFlow::CONTINUE; + } + + match ty.kind() { + ty::Closure(_, ref substs) => { + // Skip lifetime parameters of the enclosing item(s) + + substs.as_closure().tupled_upvars_ty().visit_with(self); + substs.as_closure().sig_as_fn_ptr_ty().visit_with(self); + } + + ty::Generator(_, ref substs, _) => { + // Skip lifetime parameters of the enclosing item(s) + // Also skip the witness type, because that has no free regions. + + substs.as_generator().tupled_upvars_ty().visit_with(self); + substs.as_generator().return_ty().visit_with(self); + substs.as_generator().yield_ty().visit_with(self); + substs.as_generator().resume_ty().visit_with(self); + } + _ => { + ty.super_visit_with(self); + } + } + + ControlFlow::CONTINUE + } +} + +pub enum UseKind { + DefiningUse, + OpaqueUse, +} + +impl UseKind { + pub fn is_defining(self) -> bool { + match self { + UseKind::DefiningUse => true, + UseKind::OpaqueUse => false, + } + } +} + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + #[instrument(skip(self), level = "debug")] + pub fn register_hidden_type( + &self, + opaque_type_key: OpaqueTypeKey<'tcx>, + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + hidden_ty: Ty<'tcx>, + origin: hir::OpaqueTyOrigin, + ) -> InferResult<'tcx, ()> { + let tcx = self.tcx; + let OpaqueTypeKey { def_id, substs } = opaque_type_key; + + // Ideally, we'd get the span where *this specific `ty` came + // from*, but right now we just use the span from the overall + // value being folded. In simple cases like `-> impl Foo`, + // these are the same span, but not in cases like `-> (impl + // Foo, impl Bar)`. + let span = cause.span; + + let mut obligations = vec![]; + let prev = self.inner.borrow_mut().opaque_types().register( + OpaqueTypeKey { def_id, substs }, + OpaqueHiddenType { ty: hidden_ty, span }, + origin, + ); + if let Some(prev) = prev { + obligations = self.at(&cause, param_env).eq(prev, hidden_ty)?.obligations; + } + + let item_bounds = tcx.bound_explicit_item_bounds(def_id.to_def_id()); + + for predicate in item_bounds.transpose_iter().map(|e| e.map_bound(|(p, _)| *p)) { + debug!(?predicate); + let predicate = predicate.subst(tcx, substs); + + let predicate = predicate.fold_with(&mut BottomUpFolder { + tcx, + ty_op: |ty| match *ty.kind() { + // We can't normalize associated types from `rustc_infer`, + // but we can eagerly register inference variables for them. + ty::Projection(projection_ty) if !projection_ty.has_escaping_bound_vars() => { + self.infer_projection( + param_env, + projection_ty, + cause.clone(), + 0, + &mut obligations, + ) + } + // Replace all other mentions of the same opaque type with the hidden type, + // as the bounds must hold on the hidden type after all. + ty::Opaque(def_id2, substs2) + if def_id.to_def_id() == def_id2 && substs == substs2 => + { + hidden_ty + } + _ => ty, + }, + lt_op: |lt| lt, + ct_op: |ct| ct, + }); + + if let ty::PredicateKind::Projection(projection) = predicate.kind().skip_binder() { + if projection.term.references_error() { + // No point on adding these obligations since there's a type error involved. + return Ok(InferOk { value: (), obligations: vec![] }); + } + trace!("{:#?}", projection.term); + } + // Require that the predicate holds for the concrete type. + debug!(?predicate); + obligations.push(traits::Obligation::new(cause.clone(), param_env, predicate)); + } + Ok(InferOk { value: (), obligations }) + } +} + +/// Returns `true` if `opaque_hir_id` is a sibling or a child of a sibling of `def_id`. +/// +/// Example: +/// ```ignore UNSOLVED (is this a bug?) +/// # #![feature(type_alias_impl_trait)] +/// pub mod foo { +/// pub mod bar { +/// pub trait Bar { /* ... */ } +/// pub type Baz = impl Bar; +/// +/// # impl Bar for () {} +/// fn f1() -> Baz { /* ... */ } +/// } +/// fn f2() -> bar::Baz { /* ... */ } +/// } +/// ``` +/// +/// Here, `def_id` is the `LocalDefId` of the defining use of the opaque type (e.g., `f1` or `f2`), +/// and `opaque_hir_id` is the `HirId` of the definition of the opaque type `Baz`. +/// For the above example, this function returns `true` for `f1` and `false` for `f2`. +fn may_define_opaque_type(tcx: TyCtxt<'_>, def_id: LocalDefId, opaque_hir_id: hir::HirId) -> bool { + let mut hir_id = tcx.hir().local_def_id_to_hir_id(def_id); + + // Named opaque types can be defined by any siblings or children of siblings. + let scope = tcx.hir().get_defining_scope(opaque_hir_id); + // We walk up the node tree until we hit the root or the scope of the opaque type. + while hir_id != scope && hir_id != hir::CRATE_HIR_ID { + hir_id = tcx.hir().local_def_id_to_hir_id(tcx.hir().get_parent_item(hir_id)); + } + // Syntactically, we are allowed to define the concrete type if: + let res = hir_id == scope; + trace!( + "may_define_opaque_type(def={:?}, opaque_node={:?}) = {}", + tcx.hir().find(hir_id), + tcx.hir().get(opaque_hir_id), + res + ); + res +} diff --git a/compiler/rustc_infer/src/infer/opaque_types/table.rs b/compiler/rustc_infer/src/infer/opaque_types/table.rs new file mode 100644 index 000000000..fb12da0cc --- /dev/null +++ b/compiler/rustc_infer/src/infer/opaque_types/table.rs @@ -0,0 +1,80 @@ +use rustc_data_structures::undo_log::UndoLogs; +use rustc_hir::OpaqueTyOrigin; +use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey, Ty}; +use rustc_span::DUMMY_SP; + +use crate::infer::{InferCtxtUndoLogs, UndoLog}; + +use super::{OpaqueTypeDecl, OpaqueTypeMap}; + +#[derive(Default, Debug, Clone)] +pub struct OpaqueTypeStorage<'tcx> { + // Opaque types found in explicit return types and their + // associated fresh inference variable. Writeback resolves these + // variables to get the concrete type, which can be used to + // 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions. + pub opaque_types: OpaqueTypeMap<'tcx>, +} + +impl<'tcx> OpaqueTypeStorage<'tcx> { + #[instrument(level = "debug")] + pub(crate) fn remove(&mut self, key: OpaqueTypeKey<'tcx>, idx: Option<OpaqueHiddenType<'tcx>>) { + if let Some(idx) = idx { + self.opaque_types.get_mut(&key).unwrap().hidden_type = idx; + } else { + match self.opaque_types.remove(&key) { + None => bug!("reverted opaque type inference that was never registered: {:?}", key), + Some(_) => {} + } + } + } + + #[instrument(level = "debug")] + pub fn take_opaque_types(&mut self) -> OpaqueTypeMap<'tcx> { + std::mem::take(&mut self.opaque_types) + } + + #[inline] + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, + ) -> OpaqueTypeTable<'a, 'tcx> { + OpaqueTypeTable { storage: self, undo_log } + } +} + +impl<'tcx> Drop for OpaqueTypeStorage<'tcx> { + fn drop(&mut self) { + if !self.opaque_types.is_empty() { + ty::tls::with(|tcx| { + tcx.sess.delay_span_bug(DUMMY_SP, &format!("{:?}", self.opaque_types)) + }); + } + } +} + +pub struct OpaqueTypeTable<'a, 'tcx> { + storage: &'a mut OpaqueTypeStorage<'tcx>, + + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, +} + +impl<'a, 'tcx> OpaqueTypeTable<'a, 'tcx> { + #[instrument(skip(self), level = "debug")] + pub(crate) fn register( + &mut self, + key: OpaqueTypeKey<'tcx>, + hidden_type: OpaqueHiddenType<'tcx>, + origin: OpaqueTyOrigin, + ) -> Option<Ty<'tcx>> { + if let Some(decl) = self.storage.opaque_types.get_mut(&key) { + let prev = std::mem::replace(&mut decl.hidden_type, hidden_type); + self.undo_log.push(UndoLog::OpaqueTypes(key, Some(prev))); + return Some(prev.ty); + } + let decl = OpaqueTypeDecl { hidden_type, origin }; + self.storage.opaque_types.insert(key, decl); + self.undo_log.push(UndoLog::OpaqueTypes(key, None)); + None + } +} diff --git a/compiler/rustc_infer/src/infer/outlives/components.rs b/compiler/rustc_infer/src/infer/outlives/components.rs new file mode 100644 index 000000000..b2d7f4a66 --- /dev/null +++ b/compiler/rustc_infer/src/infer/outlives/components.rs @@ -0,0 +1,219 @@ +// The outlines relation `T: 'a` or `'a: 'b`. This code frequently +// refers to rules defined in RFC 1214 (`OutlivesFooBar`), so see that +// RFC for reference. + +use rustc_data_structures::sso::SsoHashSet; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; +use smallvec::{smallvec, SmallVec}; + +#[derive(Debug)] +pub enum Component<'tcx> { + Region(ty::Region<'tcx>), + Param(ty::ParamTy), + UnresolvedInferenceVariable(ty::InferTy), + + // Projections like `T::Foo` are tricky because a constraint like + // `T::Foo: 'a` can be satisfied in so many ways. There may be a + // where-clause that says `T::Foo: 'a`, or the defining trait may + // include a bound like `type Foo: 'static`, or -- in the most + // conservative way -- we can prove that `T: 'a` (more generally, + // that all components in the projection outlive `'a`). This code + // is not in a position to judge which is the best technique, so + // we just product the projection as a component and leave it to + // the consumer to decide (but see `EscapingProjection` below). + Projection(ty::ProjectionTy<'tcx>), + + // In the case where a projection has escaping regions -- meaning + // regions bound within the type itself -- we always use + // the most conservative rule, which requires that all components + // outlive the bound. So for example if we had a type like this: + // + // for<'a> Trait1< <T as Trait2<'a,'b>>::Foo > + // ~~~~~~~~~~~~~~~~~~~~~~~~~ + // + // then the inner projection (underlined) has an escaping region + // `'a`. We consider that outer trait `'c` to meet a bound if `'b` + // outlives `'b: 'c`, and we don't consider whether the trait + // declares that `Foo: 'static` etc. Therefore, we just return the + // free components of such a projection (in this case, `'b`). + // + // However, in the future, we may want to get smarter, and + // actually return a "higher-ranked projection" here. Therefore, + // we mark that these components are part of an escaping + // projection, so that implied bounds code can avoid relying on + // them. This gives us room to improve the regionck reasoning in + // the future without breaking backwards compat. + EscapingProjection(Vec<Component<'tcx>>), +} + +/// Push onto `out` all the things that must outlive `'a` for the condition +/// `ty0: 'a` to hold. Note that `ty0` must be a **fully resolved type**. +pub fn push_outlives_components<'tcx>( + tcx: TyCtxt<'tcx>, + ty0: Ty<'tcx>, + out: &mut SmallVec<[Component<'tcx>; 4]>, +) { + let mut visited = SsoHashSet::new(); + compute_components(tcx, ty0, out, &mut visited); + debug!("components({:?}) = {:?}", ty0, out); +} + +fn compute_components<'tcx>( + tcx: TyCtxt<'tcx>, + ty: Ty<'tcx>, + out: &mut SmallVec<[Component<'tcx>; 4]>, + visited: &mut SsoHashSet<GenericArg<'tcx>>, +) { + // Descend through the types, looking for the various "base" + // components and collecting them into `out`. This is not written + // with `collect()` because of the need to sometimes skip subtrees + // in the `subtys` iterator (e.g., when encountering a + // projection). + match *ty.kind() { + ty::FnDef(_, substs) => { + // HACK(eddyb) ignore lifetimes found shallowly in `substs`. + // This is inconsistent with `ty::Adt` (including all substs) + // and with `ty::Closure` (ignoring all substs other than + // upvars, of which a `ty::FnDef` doesn't have any), but + // consistent with previous (accidental) behavior. + // See https://github.com/rust-lang/rust/issues/70917 + // for further background and discussion. + for child in substs { + match child.unpack() { + GenericArgKind::Type(ty) => { + compute_components(tcx, ty, out, visited); + } + GenericArgKind::Lifetime(_) => {} + GenericArgKind::Const(_) => { + compute_components_recursive(tcx, child, out, visited); + } + } + } + } + + ty::Array(element, _) => { + // Don't look into the len const as it doesn't affect regions + compute_components(tcx, element, out, visited); + } + + ty::Closure(_, ref substs) => { + let tupled_ty = substs.as_closure().tupled_upvars_ty(); + compute_components(tcx, tupled_ty, out, visited); + } + + ty::Generator(_, ref substs, _) => { + // Same as the closure case + let tupled_ty = substs.as_generator().tupled_upvars_ty(); + compute_components(tcx, tupled_ty, out, visited); + + // We ignore regions in the generator interior as we don't + // want these to affect region inference + } + + // All regions are bound inside a witness + ty::GeneratorWitness(..) => (), + + // OutlivesTypeParameterEnv -- the actual checking that `X:'a` + // is implied by the environment is done in regionck. + ty::Param(p) => { + out.push(Component::Param(p)); + } + + // For projections, we prefer to generate an obligation like + // `<P0 as Trait<P1...Pn>>::Foo: 'a`, because this gives the + // regionck more ways to prove that it holds. However, + // regionck is not (at least currently) prepared to deal with + // higher-ranked regions that may appear in the + // trait-ref. Therefore, if we see any higher-ranked regions, + // we simply fallback to the most restrictive rule, which + // requires that `Pi: 'a` for all `i`. + ty::Projection(ref data) => { + if !data.has_escaping_bound_vars() { + // best case: no escaping regions, so push the + // projection and skip the subtree (thus generating no + // constraints for Pi). This defers the choice between + // the rules OutlivesProjectionEnv, + // OutlivesProjectionTraitDef, and + // OutlivesProjectionComponents to regionck. + out.push(Component::Projection(*data)); + } else { + // fallback case: hard code + // OutlivesProjectionComponents. Continue walking + // through and constrain Pi. + let mut subcomponents = smallvec![]; + let mut subvisited = SsoHashSet::new(); + compute_components_recursive(tcx, ty.into(), &mut subcomponents, &mut subvisited); + out.push(Component::EscapingProjection(subcomponents.into_iter().collect())); + } + } + + // We assume that inference variables are fully resolved. + // So, if we encounter an inference variable, just record + // the unresolved variable as a component. + ty::Infer(infer_ty) => { + out.push(Component::UnresolvedInferenceVariable(infer_ty)); + } + + // Most types do not introduce any region binders, nor + // involve any other subtle cases, and so the WF relation + // simply constraints any regions referenced directly by + // the type and then visits the types that are lexically + // contained within. (The comments refer to relevant rules + // from RFC1214.) + ty::Bool | // OutlivesScalar + ty::Char | // OutlivesScalar + ty::Int(..) | // OutlivesScalar + ty::Uint(..) | // OutlivesScalar + ty::Float(..) | // OutlivesScalar + ty::Never | // ... + ty::Adt(..) | // OutlivesNominalType + ty::Opaque(..) | // OutlivesNominalType (ish) + ty::Foreign(..) | // OutlivesNominalType + ty::Str | // OutlivesScalar (ish) + ty::Slice(..) | // ... + ty::RawPtr(..) | // ... + ty::Ref(..) | // OutlivesReference + ty::Tuple(..) | // ... + ty::FnPtr(_) | // OutlivesFunction (*) + ty::Dynamic(..) | // OutlivesObject, OutlivesFragment (*) + ty::Placeholder(..) | + ty::Bound(..) | + ty::Error(_) => { + // (*) Function pointers and trait objects are both binders. + // In the RFC, this means we would add the bound regions to + // the "bound regions list". In our representation, no such + // list is maintained explicitly, because bound regions + // themselves can be readily identified. + compute_components_recursive(tcx, ty.into(), out, visited); + } + } +} + +/// Collect [Component]s for *all* the substs of `parent`. +/// +/// This should not be used to get the components of `parent` itself. +/// Use [push_outlives_components] instead. +pub(super) fn compute_components_recursive<'tcx>( + tcx: TyCtxt<'tcx>, + parent: GenericArg<'tcx>, + out: &mut SmallVec<[Component<'tcx>; 4]>, + visited: &mut SsoHashSet<GenericArg<'tcx>>, +) { + for child in parent.walk_shallow(visited) { + match child.unpack() { + GenericArgKind::Type(ty) => { + compute_components(tcx, ty, out, visited); + } + GenericArgKind::Lifetime(lt) => { + // Ignore late-bound regions. + if !lt.is_late_bound() { + out.push(Component::Region(lt)); + } + } + GenericArgKind::Const(_) => { + compute_components_recursive(tcx, child, out, visited); + } + } + } +} diff --git a/compiler/rustc_infer/src/infer/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs new file mode 100644 index 000000000..b2decd64f --- /dev/null +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -0,0 +1,131 @@ +use crate::infer::free_regions::FreeRegionMap; +use crate::infer::{GenericKind, InferCtxt}; +use crate::traits::query::OutlivesBound; +use rustc_data_structures::fx::FxIndexSet; +use rustc_middle::ty::{self, ReEarlyBound, ReFree, ReVar, Region}; + +use super::explicit_outlives_bounds; + +/// The `OutlivesEnvironment` collects information about what outlives +/// what in a given type-checking setting. For example, if we have a +/// where-clause like `where T: 'a` in scope, then the +/// `OutlivesEnvironment` would record that (in its +/// `region_bound_pairs` field). Similarly, it contains methods for +/// processing and adding implied bounds into the outlives +/// environment. +/// +/// Other code at present does not typically take a +/// `&OutlivesEnvironment`, but rather takes some of its fields (e.g., +/// `process_registered_region_obligations` wants the +/// region-bound-pairs). There is no mistaking it: the current setup +/// of tracking region information is quite scattered! The +/// `OutlivesEnvironment`, for example, needs to sometimes be combined +/// with the `middle::RegionRelations`, to yield a full picture of how +/// (lexical) lifetimes interact. However, I'm reluctant to do more +/// refactoring here, since the setup with NLL is quite different. +/// For example, NLL has no need of `RegionRelations`, and is solely +/// interested in the `OutlivesEnvironment`. -nmatsakis +#[derive(Clone)] +pub struct OutlivesEnvironment<'tcx> { + pub param_env: ty::ParamEnv<'tcx>, + free_region_map: FreeRegionMap<'tcx>, + + // Contains the implied region bounds in scope for our current body. + // + // Example: + // + // ``` + // fn foo<'a, 'b, T>(x: &'a T, y: &'b ()) { + // bar(x, y, |y: &'b T| { .. } // body B1) + // } // body B0 + // ``` + // + // Here, when checking the body B0, the list would be `[T: 'a]`, because we + // infer that `T` must outlive `'a` from the implied bounds on the + // fn declaration. + // + // For the body B1 however, the list would be `[T: 'a, T: 'b]`, because we + // also can see that -- within the closure body! -- `T` must + // outlive `'b`. This is not necessarily true outside the closure + // body, since the closure may never be called. + region_bound_pairs: RegionBoundPairs<'tcx>, +} + +/// "Region-bound pairs" tracks outlives relations that are known to +/// be true, either because of explicit where-clauses like `T: 'a` or +/// because of implied bounds. +pub type RegionBoundPairs<'tcx> = + FxIndexSet<ty::OutlivesPredicate<GenericKind<'tcx>, Region<'tcx>>>; + +impl<'a, 'tcx> OutlivesEnvironment<'tcx> { + pub fn new(param_env: ty::ParamEnv<'tcx>) -> Self { + let mut env = OutlivesEnvironment { + param_env, + free_region_map: Default::default(), + region_bound_pairs: Default::default(), + }; + + env.add_outlives_bounds(None, explicit_outlives_bounds(param_env)); + + env + } + + /// Borrows current value of the `free_region_map`. + pub fn free_region_map(&self) -> &FreeRegionMap<'tcx> { + &self.free_region_map + } + + /// Borrows current `region_bound_pairs`. + pub fn region_bound_pairs(&self) -> &RegionBoundPairs<'tcx> { + &self.region_bound_pairs + } + + /// Processes outlives bounds that are known to hold, whether from implied or other sources. + /// + /// The `infcx` parameter is optional; if the implied bounds may + /// contain inference variables, it must be supplied, in which + /// case we will register "givens" on the inference context. (See + /// `RegionConstraintData`.) + pub fn add_outlives_bounds<I>( + &mut self, + infcx: Option<&InferCtxt<'a, 'tcx>>, + outlives_bounds: I, + ) where + I: IntoIterator<Item = OutlivesBound<'tcx>>, + { + // Record relationships such as `T:'x` that don't go into the + // free-region-map but which we use here. + for outlives_bound in outlives_bounds { + debug!("add_outlives_bounds: outlives_bound={:?}", outlives_bound); + match outlives_bound { + OutlivesBound::RegionSubParam(r_a, param_b) => { + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Param(param_b), r_a)); + } + OutlivesBound::RegionSubProjection(r_a, projection_b) => { + self.region_bound_pairs + .insert(ty::OutlivesPredicate(GenericKind::Projection(projection_b), r_a)); + } + OutlivesBound::RegionSubRegion(r_a, r_b) => { + if let (ReEarlyBound(_) | ReFree(_), ReVar(vid_b)) = (r_a.kind(), r_b.kind()) { + infcx + .expect("no infcx provided but region vars found") + .add_given(r_a, vid_b); + } else { + // In principle, we could record (and take + // advantage of) every relationship here, but + // we are also free not to -- it simply means + // strictly less that we can successfully type + // check. Right now we only look for things + // relationships between free regions. (It may + // also be that we should revise our inference + // system to be more general and to make use + // of *every* relationship that arises here, + // but presently we do not.) + self.free_region_map.relate_regions(r_a, r_b); + } + } + } + } + } +} diff --git a/compiler/rustc_infer/src/infer/outlives/mod.rs b/compiler/rustc_infer/src/infer/outlives/mod.rs new file mode 100644 index 000000000..2a085288f --- /dev/null +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -0,0 +1,37 @@ +//! Various code related to computing outlives relations. + +pub mod components; +pub mod env; +pub mod obligations; +pub mod test_type_match; +pub mod verify; + +use rustc_middle::traits::query::OutlivesBound; +use rustc_middle::ty; + +#[instrument(level = "debug", skip(param_env))] +pub fn explicit_outlives_bounds<'tcx>( + param_env: ty::ParamEnv<'tcx>, +) -> impl Iterator<Item = OutlivesBound<'tcx>> + 'tcx { + param_env + .caller_bounds() + .into_iter() + .map(ty::Predicate::kind) + .filter_map(ty::Binder::no_bound_vars) + .filter_map(move |kind| match kind { + ty::PredicateKind::Projection(..) + | ty::PredicateKind::Trait(..) + | ty::PredicateKind::Coerce(..) + | ty::PredicateKind::Subtype(..) + | ty::PredicateKind::WellFormed(..) + | ty::PredicateKind::ObjectSafe(..) + | ty::PredicateKind::ClosureKind(..) + | ty::PredicateKind::TypeOutlives(..) + | ty::PredicateKind::ConstEvaluatable(..) + | ty::PredicateKind::ConstEquate(..) + | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, + ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => { + Some(OutlivesBound::RegionSubRegion(r_b, r_a)) + } + }) +} diff --git a/compiler/rustc_infer/src/infer/outlives/obligations.rs b/compiler/rustc_infer/src/infer/outlives/obligations.rs new file mode 100644 index 000000000..ad052f58c --- /dev/null +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -0,0 +1,470 @@ +//! Code that handles "type-outlives" constraints like `T: 'a`. This +//! is based on the `push_outlives_components` function defined in rustc_infer, +//! but it adds a bit of heuristics on top, in particular to deal with +//! associated types and projections. +//! +//! When we process a given `T: 'a` obligation, we may produce two +//! kinds of constraints for the region inferencer: +//! +//! - Relationships between inference variables and other regions. +//! For example, if we have `&'?0 u32: 'a`, then we would produce +//! a constraint that `'a <= '?0`. +//! - "Verifys" that must be checked after inferencing is done. +//! For example, if we know that, for some type parameter `T`, +//! `T: 'a + 'b`, and we have a requirement that `T: '?1`, +//! then we add a "verify" that checks that `'?1 <= 'a || '?1 <= 'b`. +//! - Note the difference with the previous case: here, the region +//! variable must be less than something else, so this doesn't +//! affect how inference works (it finds the smallest region that +//! will do); it's just a post-condition that we have to check. +//! +//! **The key point is that once this function is done, we have +//! reduced all of our "type-region outlives" obligations into relationships +//! between individual regions.** +//! +//! One key input to this function is the set of "region-bound pairs". +//! These are basically the relationships between type parameters and +//! regions that are in scope at the point where the outlives +//! obligation was incurred. **When type-checking a function, +//! particularly in the face of closures, this is not known until +//! regionck runs!** This is because some of those bounds come +//! from things we have yet to infer. +//! +//! Consider: +//! +//! ``` +//! fn bar<T>(a: T, b: impl for<'a> Fn(&'a T)) {} +//! fn foo<T>(x: T) { +//! bar(x, |y| { /* ... */}) +//! // ^ closure arg +//! } +//! ``` +//! +//! Here, the type of `y` may involve inference variables and the +//! like, and it may also contain implied bounds that are needed to +//! type-check the closure body (e.g., here it informs us that `T` +//! outlives the late-bound region `'a`). +//! +//! Note that by delaying the gathering of implied bounds until all +//! inference information is known, we may find relationships between +//! bound regions and other regions in the environment. For example, +//! when we first check a closure like the one expected as argument +//! to `foo`: +//! +//! ``` +//! fn foo<U, F: for<'a> FnMut(&'a U)>(_f: F) {} +//! ``` +//! +//! the type of the closure's first argument would be `&'a ?U`. We +//! might later infer `?U` to something like `&'b u32`, which would +//! imply that `'b: 'a`. + +use crate::infer::outlives::components::{push_outlives_components, Component}; +use crate::infer::outlives::env::OutlivesEnvironment; +use crate::infer::outlives::env::RegionBoundPairs; +use crate::infer::outlives::verify::VerifyBoundCx; +use crate::infer::{ + self, GenericKind, InferCtxt, RegionObligation, SubregionOrigin, UndoLog, VerifyBound, +}; +use crate::traits::{ObligationCause, ObligationCauseCode}; +use rustc_data_structures::undo_log::UndoLogs; +use rustc_hir::def_id::LocalDefId; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeVisitable}; +use smallvec::smallvec; + +impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { + /// Registers that the given region obligation must be resolved + /// from within the scope of `body_id`. These regions are enqueued + /// and later processed by regionck, when full type information is + /// available (see `region_obligations` field for more + /// information). + #[instrument(level = "debug", skip(self))] + pub fn register_region_obligation(&self, obligation: RegionObligation<'tcx>) { + let mut inner = self.inner.borrow_mut(); + inner.undo_log.push(UndoLog::PushRegionObligation); + inner.region_obligations.push(obligation); + } + + pub fn register_region_obligation_with_cause( + &self, + sup_type: Ty<'tcx>, + sub_region: Region<'tcx>, + cause: &ObligationCause<'tcx>, + ) { + let origin = SubregionOrigin::from_obligation_cause(cause, || { + infer::RelateParamBound( + cause.span, + sup_type, + match cause.code().peel_derives() { + ObligationCauseCode::BindingObligation(_, span) => Some(*span), + _ => None, + }, + ) + }); + + self.register_region_obligation(RegionObligation { sup_type, sub_region, origin }); + } + + /// Trait queries just want to pass back type obligations "as is" + pub fn take_registered_region_obligations(&self) -> Vec<RegionObligation<'tcx>> { + std::mem::take(&mut self.inner.borrow_mut().region_obligations) + } + + /// NOTE: Prefer using [`InferCtxt::check_region_obligations_and_report_errors`] + /// instead of calling this directly. + /// + /// Process the region obligations that must be proven (during + /// `regionck`) for the given `body_id`, given information about + /// the region bounds in scope and so forth. This function must be + /// invoked for all relevant body-ids before region inference is + /// done (or else an assert will fire). + /// + /// See the `region_obligations` field of `InferCtxt` for some + /// comments about how this function fits into the overall expected + /// flow of the inferencer. The key point is that it is + /// invoked after all type-inference variables have been bound -- + /// towards the end of regionck. This also ensures that the + /// region-bound-pairs are available (see comments above regarding + /// closures). + /// + /// # Parameters + /// + /// - `region_bound_pairs_map`: the set of region bounds implied by + /// the parameters and where-clauses. In particular, each pair + /// `('a, K)` in this list tells us that the bounds in scope + /// indicate that `K: 'a`, where `K` is either a generic + /// parameter like `T` or a projection like `T::Item`. + /// - `param_env` is the parameter environment for the enclosing function. + /// - `body_id` is the body-id whose region obligations are being + /// processed. + #[instrument(level = "debug", skip(self, region_bound_pairs))] + pub fn process_registered_region_obligations( + &self, + region_bound_pairs: &RegionBoundPairs<'tcx>, + param_env: ty::ParamEnv<'tcx>, + ) { + assert!( + !self.in_snapshot.get(), + "cannot process registered region obligations in a snapshot" + ); + + let my_region_obligations = self.take_registered_region_obligations(); + + for RegionObligation { sup_type, sub_region, origin } in my_region_obligations { + debug!( + "process_registered_region_obligations: sup_type={:?} sub_region={:?} origin={:?}", + sup_type, sub_region, origin + ); + + let sup_type = self.resolve_vars_if_possible(sup_type); + + let outlives = + &mut TypeOutlives::new(self, self.tcx, ®ion_bound_pairs, None, param_env); + outlives.type_must_outlive(origin, sup_type, sub_region); + } + } + + /// Processes registered region obliations and resolves regions, reporting + /// any errors if any were raised. Prefer using this function over manually + /// calling `resolve_regions_and_report_errors`. + pub fn check_region_obligations_and_report_errors( + &self, + generic_param_scope: LocalDefId, + outlives_env: &OutlivesEnvironment<'tcx>, + ) { + self.process_registered_region_obligations( + outlives_env.region_bound_pairs(), + outlives_env.param_env, + ); + + self.resolve_regions_and_report_errors(generic_param_scope, outlives_env) + } +} + +/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a` +/// obligation into a series of `'a: 'b` constraints and "verify"s, as +/// described on the module comment. The final constraints are emitted +/// via a "delegate" of type `D` -- this is usually the `infcx`, which +/// accrues them into the `region_obligations` code, but for NLL we +/// use something else. +pub struct TypeOutlives<'cx, 'tcx, D> +where + D: TypeOutlivesDelegate<'tcx>, +{ + // See the comments on `process_registered_region_obligations` for the meaning + // of these fields. + delegate: D, + tcx: TyCtxt<'tcx>, + verify_bound: VerifyBoundCx<'cx, 'tcx>, +} + +pub trait TypeOutlivesDelegate<'tcx> { + fn push_sub_region_constraint( + &mut self, + origin: SubregionOrigin<'tcx>, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ); + + fn push_verify( + &mut self, + origin: SubregionOrigin<'tcx>, + kind: GenericKind<'tcx>, + a: ty::Region<'tcx>, + bound: VerifyBound<'tcx>, + ); +} + +impl<'cx, 'tcx, D> TypeOutlives<'cx, 'tcx, D> +where + D: TypeOutlivesDelegate<'tcx>, +{ + pub fn new( + delegate: D, + tcx: TyCtxt<'tcx>, + region_bound_pairs: &'cx RegionBoundPairs<'tcx>, + implicit_region_bound: Option<ty::Region<'tcx>>, + param_env: ty::ParamEnv<'tcx>, + ) -> Self { + Self { + delegate, + tcx, + verify_bound: VerifyBoundCx::new( + tcx, + region_bound_pairs, + implicit_region_bound, + param_env, + ), + } + } + + /// Adds constraints to inference such that `T: 'a` holds (or + /// reports an error if it cannot). + /// + /// # Parameters + /// + /// - `origin`, the reason we need this constraint + /// - `ty`, the type `T` + /// - `region`, the region `'a` + pub fn type_must_outlive( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + ty: Ty<'tcx>, + region: ty::Region<'tcx>, + ) { + debug!("type_must_outlive(ty={:?}, region={:?}, origin={:?})", ty, region, origin); + + assert!(!ty.has_escaping_bound_vars()); + + let mut components = smallvec![]; + push_outlives_components(self.tcx, ty, &mut components); + self.components_must_outlive(origin, &components, region); + } + + fn components_must_outlive( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + components: &[Component<'tcx>], + region: ty::Region<'tcx>, + ) { + for component in components.iter() { + let origin = origin.clone(); + match component { + Component::Region(region1) => { + self.delegate.push_sub_region_constraint(origin, region, *region1); + } + Component::Param(param_ty) => { + self.param_ty_must_outlive(origin, region, *param_ty); + } + Component::Projection(projection_ty) => { + self.projection_must_outlive(origin, region, *projection_ty); + } + Component::EscapingProjection(subcomponents) => { + self.components_must_outlive(origin, &subcomponents, region); + } + Component::UnresolvedInferenceVariable(v) => { + // ignore this, we presume it will yield an error + // later, since if a type variable is not resolved by + // this point it never will be + self.tcx.sess.delay_span_bug( + origin.span(), + &format!("unresolved inference variable in outlives: {:?}", v), + ); + } + } + } + } + + fn param_ty_must_outlive( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + param_ty: ty::ParamTy, + ) { + debug!( + "param_ty_must_outlive(region={:?}, param_ty={:?}, origin={:?})", + region, param_ty, origin + ); + + let generic = GenericKind::Param(param_ty); + let verify_bound = self.verify_bound.generic_bound(generic); + self.delegate.push_verify(origin, generic, region, verify_bound); + } + + #[tracing::instrument(level = "debug", skip(self))] + fn projection_must_outlive( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + ) { + // This case is thorny for inference. The fundamental problem is + // that there are many cases where we have choice, and inference + // doesn't like choice (the current region inference in + // particular). :) First off, we have to choose between using the + // OutlivesProjectionEnv, OutlivesProjectionTraitDef, and + // OutlivesProjectionComponent rules, any one of which is + // sufficient. If there are no inference variables involved, it's + // not hard to pick the right rule, but if there are, we're in a + // bit of a catch 22: if we picked which rule we were going to + // use, we could add constraints to the region inference graph + // that make it apply, but if we don't add those constraints, the + // rule might not apply (but another rule might). For now, we err + // on the side of adding too few edges into the graph. + + // Compute the bounds we can derive from the trait definition. + // These are guaranteed to apply, no matter the inference + // results. + let trait_bounds: Vec<_> = + self.verify_bound.projection_declared_bounds_from_trait(projection_ty).collect(); + + debug!(?trait_bounds); + + // Compute the bounds we can derive from the environment. This + // is an "approximate" match -- in some cases, these bounds + // may not apply. + let mut approx_env_bounds = + self.verify_bound.projection_approx_declared_bounds_from_env(projection_ty); + debug!("projection_must_outlive: approx_env_bounds={:?}", approx_env_bounds); + + // Remove outlives bounds that we get from the environment but + // which are also deducible from the trait. This arises (cc + // #55756) in cases where you have e.g., `<T as Foo<'a>>::Item: + // 'a` in the environment but `trait Foo<'b> { type Item: 'b + // }` in the trait definition. + approx_env_bounds.retain(|bound_outlives| { + // OK to skip binder because we only manipulate and compare against other + // values from the same binder. e.g. if we have (e.g.) `for<'a> <T as Trait<'a>>::Item: 'a` + // in `bound`, the `'a` will be a `^1` (bound, debruijn index == innermost) region. + // If the declaration is `trait Trait<'b> { type Item: 'b; }`, then `projection_declared_bounds_from_trait` + // will be invoked with `['b => ^1]` and so we will get `^1` returned. + let bound = bound_outlives.skip_binder(); + match *bound.0.kind() { + ty::Projection(projection_ty) => self + .verify_bound + .projection_declared_bounds_from_trait(projection_ty) + .all(|r| r != bound.1), + + _ => panic!("expected only projection types from env, not {:?}", bound.0), + } + }); + + // If declared bounds list is empty, the only applicable rule is + // OutlivesProjectionComponent. If there are inference variables, + // then, we can break down the outlives into more primitive + // components without adding unnecessary edges. + // + // If there are *no* inference variables, however, we COULD do + // this, but we choose not to, because the error messages are less + // good. For example, a requirement like `T::Item: 'r` would be + // translated to a requirement that `T: 'r`; when this is reported + // to the user, it will thus say "T: 'r must hold so that T::Item: + // 'r holds". But that makes it sound like the only way to fix + // the problem is to add `T: 'r`, which isn't true. So, if there are no + // inference variables, we use a verify constraint instead of adding + // edges, which winds up enforcing the same condition. + let needs_infer = projection_ty.needs_infer(); + if approx_env_bounds.is_empty() && trait_bounds.is_empty() && needs_infer { + debug!("projection_must_outlive: no declared bounds"); + + for k in projection_ty.substs { + match k.unpack() { + GenericArgKind::Lifetime(lt) => { + self.delegate.push_sub_region_constraint(origin.clone(), region, lt); + } + GenericArgKind::Type(ty) => { + self.type_must_outlive(origin.clone(), ty, region); + } + GenericArgKind::Const(_) => { + // Const parameters don't impose constraints. + } + } + } + + return; + } + + // If we found a unique bound `'b` from the trait, and we + // found nothing else from the environment, then the best + // action is to require that `'b: 'r`, so do that. + // + // This is best no matter what rule we use: + // + // - OutlivesProjectionEnv: these would translate to the requirement that `'b:'r` + // - OutlivesProjectionTraitDef: these would translate to the requirement that `'b:'r` + // - OutlivesProjectionComponent: this would require `'b:'r` + // in addition to other conditions + if !trait_bounds.is_empty() + && trait_bounds[1..] + .iter() + .map(|r| Some(*r)) + .chain( + // NB: The environment may contain `for<'a> T: 'a` style bounds. + // In that case, we don't know if they are equal to the trait bound + // or not (since we don't *know* whether the environment bound even applies), + // so just map to `None` here if there are bound vars, ensuring that + // the call to `all` will fail below. + approx_env_bounds.iter().map(|b| b.map_bound(|b| b.1).no_bound_vars()), + ) + .all(|b| b == Some(trait_bounds[0])) + { + let unique_bound = trait_bounds[0]; + debug!("projection_must_outlive: unique trait bound = {:?}", unique_bound); + debug!("projection_must_outlive: unique declared bound appears in trait ref"); + self.delegate.push_sub_region_constraint(origin, region, unique_bound); + return; + } + + // Fallback to verifying after the fact that there exists a + // declared bound, or that all the components appearing in the + // projection outlive; in some cases, this may add insufficient + // edges into the inference graph, leading to inference failures + // even though a satisfactory solution exists. + let generic = GenericKind::Projection(projection_ty); + let verify_bound = self.verify_bound.generic_bound(generic); + debug!("projection_must_outlive: pushing {:?}", verify_bound); + self.delegate.push_verify(origin, generic, region, verify_bound); + } +} + +impl<'cx, 'tcx> TypeOutlivesDelegate<'tcx> for &'cx InferCtxt<'cx, 'tcx> { + fn push_sub_region_constraint( + &mut self, + origin: SubregionOrigin<'tcx>, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) { + self.sub_regions(origin, a, b) + } + + fn push_verify( + &mut self, + origin: SubregionOrigin<'tcx>, + kind: GenericKind<'tcx>, + a: ty::Region<'tcx>, + bound: VerifyBound<'tcx>, + ) { + self.verify_generic_bound(origin, kind, a, bound) + } +} diff --git a/compiler/rustc_infer/src/infer/outlives/test_type_match.rs b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs new file mode 100644 index 000000000..772e297b7 --- /dev/null +++ b/compiler/rustc_infer/src/infer/outlives/test_type_match.rs @@ -0,0 +1,207 @@ +use std::collections::hash_map::Entry; + +use rustc_data_structures::fx::FxHashMap; +use rustc_middle::ty::TypeVisitable; +use rustc_middle::ty::{ + self, + error::TypeError, + relate::{self, Relate, RelateResult, TypeRelation}, + Ty, TyCtxt, +}; + +use crate::infer::region_constraints::VerifyIfEq; + +/// Given a "verify-if-eq" type test like: +/// +/// exists<'a...> { +/// verify_if_eq(some_type, bound_region) +/// } +/// +/// and the type `test_ty` that the type test is being tested against, +/// returns: +/// +/// * `None` if `some_type` cannot be made equal to `test_ty`, +/// no matter the values of the variables in `exists`. +/// * `Some(r)` with a suitable bound (typically the value of `bound_region`, modulo +/// any bound existential variables, which will be substituted) for the +/// type under test. +/// +/// NB: This function uses a simplistic, syntactic version of type equality. +/// In other words, it may spuriously return `None` even if the type-under-test +/// is in fact equal to `some_type`. In practice, though, this is used on types +/// that are either projections like `T::Item` or `T` and it works fine, but it +/// could have trouble when complex types with higher-ranked binders and the +/// like are used. This is a particular challenge since this function is invoked +/// very late in inference and hence cannot make use of the normal inference +/// machinery. +#[tracing::instrument(level = "debug", skip(tcx, param_env))] +pub fn extract_verify_if_eq<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + verify_if_eq_b: &ty::Binder<'tcx, VerifyIfEq<'tcx>>, + test_ty: Ty<'tcx>, +) -> Option<ty::Region<'tcx>> { + assert!(!verify_if_eq_b.has_escaping_bound_vars()); + let mut m = Match::new(tcx, param_env); + let verify_if_eq = verify_if_eq_b.skip_binder(); + m.relate(verify_if_eq.ty, test_ty).ok()?; + + if let ty::RegionKind::ReLateBound(depth, br) = verify_if_eq.bound.kind() { + assert!(depth == ty::INNERMOST); + match m.map.get(&br) { + Some(&r) => Some(r), + None => { + // If there is no mapping, then this region is unconstrained. + // In that case, we escalate to `'static`. + Some(tcx.lifetimes.re_static) + } + } + } else { + // The region does not contain any bound variables, so we don't need + // to do any substitution. + // + // Example: + // + // for<'a> <T as Foo<'a>>::Item: 'b + // + // In this case, we've now matched and found a value for + // `'a`, but it doesn't affect the bound `'b`. + Some(verify_if_eq.bound) + } +} + +/// True if a (potentially higher-ranked) outlives +#[tracing::instrument(level = "debug", skip(tcx, param_env))] +pub(super) fn can_match_erased_ty<'tcx>( + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + outlives_predicate: ty::Binder<'tcx, ty::TypeOutlivesPredicate<'tcx>>, + erased_ty: Ty<'tcx>, +) -> bool { + assert!(!outlives_predicate.has_escaping_bound_vars()); + let erased_outlives_predicate = tcx.erase_regions(outlives_predicate); + let outlives_ty = erased_outlives_predicate.skip_binder().0; + if outlives_ty == erased_ty { + // pointless micro-optimization + true + } else { + Match::new(tcx, param_env).relate(outlives_ty, erased_ty).is_ok() + } +} + +struct Match<'tcx> { + tcx: TyCtxt<'tcx>, + param_env: ty::ParamEnv<'tcx>, + pattern_depth: ty::DebruijnIndex, + map: FxHashMap<ty::BoundRegion, ty::Region<'tcx>>, +} + +impl<'tcx> Match<'tcx> { + fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> Match<'tcx> { + Match { tcx, param_env, pattern_depth: ty::INNERMOST, map: FxHashMap::default() } + } +} + +impl<'tcx> Match<'tcx> { + /// Creates the "Error" variant that signals "no match". + fn no_match<T>(&self) -> RelateResult<'tcx, T> { + Err(TypeError::Mismatch) + } + + /// Binds the pattern variable `br` to `value`; returns an `Err` if the pattern + /// is already bound to a different value. + #[tracing::instrument(level = "debug", skip(self))] + fn bind( + &mut self, + br: ty::BoundRegion, + value: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + match self.map.entry(br) { + Entry::Occupied(entry) => { + if *entry.get() == value { + Ok(value) + } else { + self.no_match() + } + } + Entry::Vacant(entry) => { + entry.insert(value); + Ok(value) + } + } + } +} + +impl<'tcx> TypeRelation<'tcx> for Match<'tcx> { + fn tag(&self) -> &'static str { + "Match" + } + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.param_env + } + fn a_is_expected(&self) -> bool { + true + } // irrelevant + + fn relate_with_variance<T: Relate<'tcx>>( + &mut self, + _: ty::Variance, + _: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + self.relate(a, b) + } + + #[instrument(skip(self), level = "debug")] + fn regions( + &mut self, + pattern: ty::Region<'tcx>, + value: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("self.pattern_depth = {:?}", self.pattern_depth); + if let ty::RegionKind::ReLateBound(depth, br) = pattern.kind() && depth == self.pattern_depth { + self.bind(br, value) + } else if pattern == value { + Ok(pattern) + } else { + self.no_match() + } + } + + #[instrument(skip(self), level = "debug")] + fn tys(&mut self, pattern: Ty<'tcx>, value: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + if pattern == value { Ok(pattern) } else { relate::super_relate_tys(self, pattern, value) } + } + + #[instrument(skip(self), level = "debug")] + fn consts( + &mut self, + pattern: ty::Const<'tcx>, + value: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + debug!("{}.consts({:?}, {:?})", self.tag(), pattern, value); + if pattern == value { + Ok(pattern) + } else { + relate::super_relate_consts(self, pattern, value) + } + } + + fn binders<T>( + &mut self, + pattern: ty::Binder<'tcx, T>, + value: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + self.pattern_depth.shift_in(1); + let result = Ok(pattern.rebind(self.relate(pattern.skip_binder(), value.skip_binder())?)); + self.pattern_depth.shift_out(1); + result + } +} diff --git a/compiler/rustc_infer/src/infer/outlives/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs new file mode 100644 index 000000000..c7d7ef40d --- /dev/null +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -0,0 +1,373 @@ +use crate::infer::outlives::components::{compute_components_recursive, Component}; +use crate::infer::outlives::env::RegionBoundPairs; +use crate::infer::region_constraints::VerifyIfEq; +use crate::infer::{GenericKind, VerifyBound}; +use rustc_data_structures::captures::Captures; +use rustc_data_structures::sso::SsoHashSet; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::subst::{GenericArg, Subst}; +use rustc_middle::ty::{self, EarlyBinder, OutlivesPredicate, Ty, TyCtxt}; + +use smallvec::smallvec; + +/// The `TypeOutlives` struct has the job of "lowering" a `T: 'a` +/// obligation into a series of `'a: 'b` constraints and "verifys", as +/// described on the module comment. The final constraints are emitted +/// via a "delegate" of type `D` -- this is usually the `infcx`, which +/// accrues them into the `region_obligations` code, but for NLL we +/// use something else. +pub struct VerifyBoundCx<'cx, 'tcx> { + tcx: TyCtxt<'tcx>, + region_bound_pairs: &'cx RegionBoundPairs<'tcx>, + /// During borrowck, if there are no outlives bounds on a generic + /// parameter `T`, we assume that `T: 'in_fn_body` holds. + /// + /// Outside of borrowck the only way to prove `T: '?0` is by + /// setting `'?0` to `'empty`. + implicit_region_bound: Option<ty::Region<'tcx>>, + param_env: ty::ParamEnv<'tcx>, +} + +impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> { + pub fn new( + tcx: TyCtxt<'tcx>, + region_bound_pairs: &'cx RegionBoundPairs<'tcx>, + implicit_region_bound: Option<ty::Region<'tcx>>, + param_env: ty::ParamEnv<'tcx>, + ) -> Self { + Self { tcx, region_bound_pairs, implicit_region_bound, param_env } + } + + /// Returns a "verify bound" that encodes what we know about + /// `generic` and the regions it outlives. + pub fn generic_bound(&self, generic: GenericKind<'tcx>) -> VerifyBound<'tcx> { + let mut visited = SsoHashSet::new(); + match generic { + GenericKind::Param(param_ty) => self.param_bound(param_ty), + GenericKind::Projection(projection_ty) => { + self.projection_bound(projection_ty, &mut visited) + } + } + } + + fn param_bound(&self, param_ty: ty::ParamTy) -> VerifyBound<'tcx> { + debug!("param_bound(param_ty={:?})", param_ty); + + // Start with anything like `T: 'a` we can scrape from the + // environment. If the environment contains something like + // `for<'a> T: 'a`, then we know that `T` outlives everything. + let declared_bounds_from_env = self.declared_generic_bounds_from_env(param_ty); + let mut param_bounds = vec![]; + for declared_bound in declared_bounds_from_env { + let bound_region = declared_bound.map_bound(|outlives| outlives.1); + if let Some(region) = bound_region.no_bound_vars() { + // This is `T: 'a` for some free region `'a`. + param_bounds.push(VerifyBound::OutlivedBy(region)); + } else { + // This is `for<'a> T: 'a`. This means that `T` outlives everything! All done here. + return VerifyBound::AllBounds(vec![]); + } + } + + // Add in the default bound of fn body that applies to all in + // scope type parameters: + if let Some(r) = self.implicit_region_bound { + param_bounds.push(VerifyBound::OutlivedBy(r)); + } + + if param_bounds.is_empty() { + // We know that all types `T` outlive `'empty`, so if we + // can find no other bound, then check that the region + // being tested is `'empty`. + VerifyBound::IsEmpty + } else if param_bounds.len() == 1 { + // Micro-opt: no need to store the vector if it's just len 1 + param_bounds.pop().unwrap() + } else { + // If we can find any other bound `R` such that `T: R`, then + // we don't need to check for `'empty`, because `R: 'empty`. + VerifyBound::AnyBound(param_bounds) + } + } + + /// Given a projection like `T::Item`, searches the environment + /// for where-clauses like `T::Item: 'a`. Returns the set of + /// regions `'a` that it finds. + /// + /// This is an "approximate" check -- it may not find all + /// applicable bounds, and not all the bounds it returns can be + /// relied upon. In particular, this check ignores region + /// identity. So, for example, if we have `<T as + /// Trait<'0>>::Item` where `'0` is a region variable, and the + /// user has `<T as Trait<'a>>::Item: 'b` in the environment, then + /// the clause from the environment only applies if `'0 = 'a`, + /// which we don't know yet. But we would still include `'b` in + /// this list. + pub fn projection_approx_declared_bounds_from_env( + &self, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> { + let projection_ty = GenericKind::Projection(projection_ty).to_ty(self.tcx); + let erased_projection_ty = self.tcx.erase_regions(projection_ty); + self.declared_generic_bounds_from_env_for_erased_ty(erased_projection_ty) + } + + /// Searches the where-clauses in scope for regions that + /// `projection_ty` is known to outlive. Currently requires an + /// exact match. + pub fn projection_declared_bounds_from_trait( + &self, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'tcx> { + self.declared_projection_bounds_from_trait(projection_ty) + } + + pub fn projection_bound( + &self, + projection_ty: ty::ProjectionTy<'tcx>, + visited: &mut SsoHashSet<GenericArg<'tcx>>, + ) -> VerifyBound<'tcx> { + debug!("projection_bound(projection_ty={:?})", projection_ty); + + let projection_ty_as_ty = + self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); + + // Search the env for where clauses like `P: 'a`. + let env_bounds = self + .projection_approx_declared_bounds_from_env(projection_ty) + .into_iter() + .map(|binder| { + if let Some(ty::OutlivesPredicate(ty, r)) = binder.no_bound_vars() && ty == projection_ty_as_ty { + // Micro-optimize if this is an exact match (this + // occurs often when there are no region variables + // involved). + VerifyBound::OutlivedBy(r) + } else { + let verify_if_eq_b = binder.map_bound(|ty::OutlivesPredicate(ty, bound)| VerifyIfEq { ty, bound }); + VerifyBound::IfEq(verify_if_eq_b) + } + }); + + // Extend with bounds that we can find from the trait. + let trait_bounds = self + .projection_declared_bounds_from_trait(projection_ty) + .map(|r| VerifyBound::OutlivedBy(r)); + + // see the extensive comment in projection_must_outlive + let recursive_bound = { + let mut components = smallvec![]; + let ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); + compute_components_recursive(self.tcx, ty.into(), &mut components, visited); + self.bound_from_components(&components, visited) + }; + + VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound) + } + + fn bound_from_components( + &self, + components: &[Component<'tcx>], + visited: &mut SsoHashSet<GenericArg<'tcx>>, + ) -> VerifyBound<'tcx> { + let mut bounds = components + .iter() + .map(|component| self.bound_from_single_component(component, visited)) + .filter(|bound| { + // Remove bounds that must hold, since they are not interesting. + !bound.must_hold() + }); + + match (bounds.next(), bounds.next()) { + (Some(first), None) => first, + (first, second) => { + VerifyBound::AllBounds(first.into_iter().chain(second).chain(bounds).collect()) + } + } + } + + fn bound_from_single_component( + &self, + component: &Component<'tcx>, + visited: &mut SsoHashSet<GenericArg<'tcx>>, + ) -> VerifyBound<'tcx> { + match *component { + Component::Region(lt) => VerifyBound::OutlivedBy(lt), + Component::Param(param_ty) => self.param_bound(param_ty), + Component::Projection(projection_ty) => self.projection_bound(projection_ty, visited), + Component::EscapingProjection(ref components) => { + self.bound_from_components(components, visited) + } + Component::UnresolvedInferenceVariable(v) => { + // ignore this, we presume it will yield an error + // later, since if a type variable is not resolved by + // this point it never will be + self.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + &format!("unresolved inference variable in outlives: {:?}", v), + ); + // add a bound that never holds + VerifyBound::AnyBound(vec![]) + } + } + } + + /// Searches the environment for where-clauses like `G: 'a` where + /// `G` is either some type parameter `T` or a projection like + /// `T::Item`. Returns a vector of the `'a` bounds it can find. + /// + /// This is a conservative check -- it may not find all applicable + /// bounds, but all the bounds it returns can be relied upon. + fn declared_generic_bounds_from_env( + &self, + param_ty: ty::ParamTy, + ) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> { + let generic_ty = param_ty.to_ty(self.tcx); + self.declared_generic_bounds_from_env_for_erased_ty(generic_ty) + } + + /// Searches the environment to find all bounds that apply to `erased_ty`. + /// Obviously these must be approximate -- they are in fact both *over* and + /// and *under* approximated: + /// + /// * Over-approximated because we erase regions, so + /// * Under-approximated because we look for syntactic equality and so for complex types + /// like `<T as Foo<fn(&u32, &u32)>>::Item` or whatever we may fail to figure out + /// all the subtleties. + /// + /// In some cases, such as when `erased_ty` represents a `ty::Param`, however, + /// the result is precise. + fn declared_generic_bounds_from_env_for_erased_ty( + &self, + erased_ty: Ty<'tcx>, + ) -> Vec<ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> { + let tcx = self.tcx; + + // To start, collect bounds from user environment. Note that + // parameter environments are already elaborated, so we don't + // have to worry about that. + let c_b = self.param_env.caller_bounds(); + let param_bounds = self.collect_outlives_from_predicate_list(erased_ty, c_b.into_iter()); + + // Next, collect regions we scraped from the well-formedness + // constraints in the fn signature. To do that, we walk the list + // of known relations from the fn ctxt. + // + // This is crucial because otherwise code like this fails: + // + // fn foo<'a, A>(x: &'a A) { x.bar() } + // + // The problem is that the type of `x` is `&'a A`. To be + // well-formed, then, A must outlive `'a`, but we don't know that + // this holds from first principles. + let from_region_bound_pairs = + self.region_bound_pairs.iter().filter_map(|&OutlivesPredicate(p, r)| { + debug!( + "declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}", + (r, p) + ); + let p_ty = p.to_ty(tcx); + let erased_p_ty = self.tcx.erase_regions(p_ty); + (erased_p_ty == erased_ty) + .then_some(ty::Binder::dummy(ty::OutlivesPredicate(p.to_ty(tcx), r))) + }); + + param_bounds + .chain(from_region_bound_pairs) + .inspect(|bound| { + debug!( + "declared_generic_bounds_from_env_for_erased_ty: result predicate = {:?}", + bound + ) + }) + .collect() + } + + /// Given a projection like `<T as Foo<'x>>::Bar`, returns any bounds + /// declared in the trait definition. For example, if the trait were + /// + /// ```rust + /// trait Foo<'a> { + /// type Bar: 'a; + /// } + /// ``` + /// + /// then this function would return `'x`. This is subject to the + /// limitations around higher-ranked bounds described in + /// `region_bounds_declared_on_associated_item`. + fn declared_projection_bounds_from_trait( + &self, + projection_ty: ty::ProjectionTy<'tcx>, + ) -> impl Iterator<Item = ty::Region<'tcx>> + 'cx + Captures<'tcx> { + debug!("projection_bounds(projection_ty={:?})", projection_ty); + let tcx = self.tcx; + self.region_bounds_declared_on_associated_item(projection_ty.item_def_id) + .map(move |r| EarlyBinder(r).subst(tcx, projection_ty.substs)) + } + + /// Given the `DefId` of an associated item, returns any region + /// bounds attached to that associated item from the trait definition. + /// + /// For example: + /// + /// ```rust + /// trait Foo<'a> { + /// type Bar: 'a; + /// } + /// ``` + /// + /// If we were given the `DefId` of `Foo::Bar`, we would return + /// `'a`. You could then apply the substitutions from the + /// projection to convert this into your namespace. This also + /// works if the user writes `where <Self as Foo<'a>>::Bar: 'a` on + /// the trait. In fact, it works by searching for just such a + /// where-clause. + /// + /// It will not, however, work for higher-ranked bounds like: + /// + /// ```compile_fail,E0311 + /// trait Foo<'a, 'b> + /// where for<'x> <Self as Foo<'x, 'b>>::Bar: 'x + /// { + /// type Bar; + /// } + /// ``` + /// + /// This is for simplicity, and because we are not really smart + /// enough to cope with such bounds anywhere. + fn region_bounds_declared_on_associated_item( + &self, + assoc_item_def_id: DefId, + ) -> impl Iterator<Item = ty::Region<'tcx>> { + let tcx = self.tcx; + let bounds = tcx.item_bounds(assoc_item_def_id); + bounds + .into_iter() + .filter_map(|p| p.to_opt_type_outlives()) + .filter_map(|p| p.no_bound_vars()) + .map(|b| b.1) + } + + /// Searches through a predicate list for a predicate `T: 'a`. + /// + /// Careful: does not elaborate predicates, and just uses `==` + /// when comparing `ty` for equality, so `ty` must be something + /// that does not involve inference variables and where you + /// otherwise want a precise match. + fn collect_outlives_from_predicate_list( + &self, + erased_ty: Ty<'tcx>, + predicates: impl Iterator<Item = ty::Predicate<'tcx>>, + ) -> impl Iterator<Item = ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>> + { + let tcx = self.tcx; + let param_env = self.param_env; + predicates.filter_map(|p| p.to_opt_type_outlives()).filter(move |outlives_predicate| { + super::test_type_match::can_match_erased_ty( + tcx, + param_env, + *outlives_predicate, + erased_ty, + ) + }) + } +} diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs new file mode 100644 index 000000000..b45a6514d --- /dev/null +++ b/compiler/rustc_infer/src/infer/projection.rs @@ -0,0 +1,40 @@ +use rustc_middle::traits::ObligationCause; +use rustc_middle::ty::{self, ToPredicate, Ty}; + +use crate::traits::{Obligation, PredicateObligation}; + +use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use super::InferCtxt; + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + /// Instead of normalizing an associated type projection, + /// this function generates an inference variable and registers + /// an obligation that this inference variable must be the result + /// of the given projection. This allows us to proceed with projections + /// while they cannot be resolved yet due to missing information or + /// simply due to the lack of access to the trait resolution machinery. + pub fn infer_projection( + &self, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + recursion_depth: usize, + obligations: &mut Vec<PredicateObligation<'tcx>>, + ) -> Ty<'tcx> { + let def_id = projection_ty.item_def_id; + let ty_var = self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::NormalizeProjectionType, + span: self.tcx.def_span(def_id), + }); + let projection = + ty::Binder::dummy(ty::ProjectionPredicate { projection_ty, term: ty_var.into() }); + let obligation = Obligation::with_depth( + cause, + recursion_depth, + param_env, + projection.to_predicate(self.tcx), + ); + obligations.push(obligation); + ty_var + } +} diff --git a/compiler/rustc_infer/src/infer/region_constraints/README.md b/compiler/rustc_infer/src/infer/region_constraints/README.md new file mode 100644 index 000000000..0231dd066 --- /dev/null +++ b/compiler/rustc_infer/src/infer/region_constraints/README.md @@ -0,0 +1,3 @@ +For info on how the current borrowck works, see the [rustc dev guide]. + +[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html diff --git a/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs new file mode 100644 index 000000000..397efe6ee --- /dev/null +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -0,0 +1,447 @@ +use super::*; +use crate::infer::CombinedSnapshot; +use rustc_data_structures::{ + graph::{scc::Sccs, vec_graph::VecGraph}, + undo_log::UndoLogs, +}; +use rustc_index::vec::Idx; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::relate::RelateResult; + +impl<'tcx> RegionConstraintCollector<'_, 'tcx> { + /// Searches new universes created during `snapshot`, looking for + /// placeholders that may "leak" out from the universes they are contained + /// in. If any leaking placeholders are found, then an `Err` is returned + /// (typically leading to the snapshot being reversed). + /// + /// The leak check *used* to be the only way we had to handle higher-ranked + /// obligations. Now that we have integrated universes into the region + /// solvers, this is no longer the case, but we retain the leak check for + /// backwards compatibility purposes. In particular, it lets us make "early" + /// decisions about whether a region error will be reported that are used in + /// coherence and elsewhere -- see #56105 and #59490 for more details. The + /// eventual fate of the leak checker is not yet settled. + /// + /// The leak checker works by searching for the following error patterns: + /// + /// * P1: P2, where P1 != P2 + /// * P1: R, where R is in some universe that cannot name P1 + /// + /// The idea here is that each of these patterns represents something that + /// the region solver would eventually report as an error, so we can detect + /// the error early. There is a fly in the ointment, though, in that this is + /// not entirely true. In particular, in the future, we may extend the + /// environment with implied bounds or other info about how placeholders + /// relate to regions in outer universes. In that case, `P1: R` for example + /// might become solvable. + /// + /// # Summary of the implementation + /// + /// The leak checks as follows. First, we construct a graph where `R2: R1` + /// implies `R2 -> R1`, and we compute the SCCs. + /// + /// For each SCC S, we compute: + /// + /// * what placeholder P it must be equal to, if any + /// * if there are multiple placeholders that must be equal, report an error because `P1: P2` + /// * the minimum universe of its constituents + /// + /// Then we walk the SCCs in dependency order and compute + /// + /// * what placeholder they must outlive transitively + /// * if they must also be equal to a placeholder, report an error because `P1: P2` + /// * minimum universe U of all SCCs they must outlive + /// * if they must also be equal to a placeholder P, and U cannot name P, report an error, as that + /// indicates `P: R` and `R` is in an incompatible universe + /// + /// # Historical note + /// + /// Older variants of the leak check used to report errors for these + /// patterns, but we no longer do: + /// + /// * R: P1, even if R cannot name P1, because R = 'static is a valid sol'n + /// * R: P1, R: P2, as above + pub fn leak_check( + &mut self, + tcx: TyCtxt<'tcx>, + overly_polymorphic: bool, + max_universe: ty::UniverseIndex, + snapshot: &CombinedSnapshot<'_, 'tcx>, + ) -> RelateResult<'tcx, ()> { + debug!( + "leak_check(max_universe={:?}, snapshot.universe={:?}, overly_polymorphic={:?})", + max_universe, snapshot.universe, overly_polymorphic + ); + + assert!(UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log)); + + let universe_at_start_of_snapshot = snapshot.universe; + if universe_at_start_of_snapshot == max_universe { + return Ok(()); + } + + let mini_graph = + &MiniGraph::new(tcx, self.undo_log.region_constraints(), &self.storage.data.verifys); + + let mut leak_check = LeakCheck::new( + tcx, + universe_at_start_of_snapshot, + max_universe, + overly_polymorphic, + mini_graph, + self, + ); + leak_check.assign_placeholder_values()?; + leak_check.propagate_scc_value()?; + Ok(()) + } +} + +struct LeakCheck<'me, 'tcx> { + tcx: TyCtxt<'tcx>, + universe_at_start_of_snapshot: ty::UniverseIndex, + /// Only used when reporting region errors. + overly_polymorphic: bool, + mini_graph: &'me MiniGraph<'tcx>, + rcc: &'me RegionConstraintCollector<'me, 'tcx>, + + // Initially, for each SCC S, stores a placeholder `P` such that `S = P` + // must hold. + // + // Later, during the [`LeakCheck::propagate_scc_value`] function, this array + // is repurposed to store some placeholder `P` such that the weaker + // condition `S: P` must hold. (This is true if `S: S1` transitively and `S1 + // = P`.) + scc_placeholders: IndexVec<LeakCheckScc, Option<ty::PlaceholderRegion>>, + + // For each SCC S, track the minimum universe that flows into it. Note that + // this is both the minimum of the universes for every region that is a + // member of the SCC, but also if you have `R1: R2`, then the universe of + // `R2` must be less than the universe of `R1` (i.e., `R1` flows `R2`). To + // see that, imagine that you have `P1: R` -- in that case, `R` must be + // either the placeholder `P1` or the empty region in that same universe. + // + // To detect errors, we look for an SCC S where the values in + // `scc_values[S]` (if any) cannot be stored into `scc_universes[S]`. + scc_universes: IndexVec<LeakCheckScc, SccUniverse<'tcx>>, +} + +impl<'me, 'tcx> LeakCheck<'me, 'tcx> { + fn new( + tcx: TyCtxt<'tcx>, + universe_at_start_of_snapshot: ty::UniverseIndex, + max_universe: ty::UniverseIndex, + overly_polymorphic: bool, + mini_graph: &'me MiniGraph<'tcx>, + rcc: &'me RegionConstraintCollector<'me, 'tcx>, + ) -> Self { + let dummy_scc_universe = SccUniverse { universe: max_universe, region: None }; + Self { + tcx, + universe_at_start_of_snapshot, + overly_polymorphic, + mini_graph, + rcc, + scc_placeholders: IndexVec::from_elem_n(None, mini_graph.sccs.num_sccs()), + scc_universes: IndexVec::from_elem_n(dummy_scc_universe, mini_graph.sccs.num_sccs()), + } + } + + /// Compute what placeholders (if any) each SCC must be equal to. + /// Also compute the minimum universe of all the regions in each SCC. + fn assign_placeholder_values(&mut self) -> RelateResult<'tcx, ()> { + // First walk: find each placeholder that is from a newly created universe. + for (region, leak_check_node) in &self.mini_graph.nodes { + let scc = self.mini_graph.sccs.scc(*leak_check_node); + + // Set the universe of each SCC to be the minimum of its constituent universes + let universe = self.rcc.universe(*region); + debug!( + "assign_placeholder_values: scc={:?} universe={:?} region={:?}", + scc, universe, region + ); + self.scc_universes[scc].take_min(universe, *region); + + // Detect those SCCs that directly contain a placeholder + if let ty::RePlaceholder(placeholder) = **region { + if self.universe_at_start_of_snapshot.cannot_name(placeholder.universe) { + self.assign_scc_value(scc, placeholder)?; + } + } + } + + Ok(()) + } + + // assign_scc_value(S, P): Update `scc_values` to account for the fact that `P: S` must hold. + // This may create an error. + fn assign_scc_value( + &mut self, + scc: LeakCheckScc, + placeholder: ty::PlaceholderRegion, + ) -> RelateResult<'tcx, ()> { + match self.scc_placeholders[scc] { + Some(p) => { + assert_ne!(p, placeholder); + return Err(self.placeholder_error(p, placeholder)); + } + None => { + self.scc_placeholders[scc] = Some(placeholder); + } + }; + + Ok(()) + } + + /// For each SCC S, iterate over each successor S1 where `S: S1`: + /// + /// * Compute + /// Iterate over each SCC `S` and ensure that, for each `S1` where `S1: S`, + /// `universe(S) <= universe(S1)`. This executes after + /// `assign_placeholder_values`, so `universe(S)` is already the minimum + /// universe of any of its direct constituents. + fn propagate_scc_value(&mut self) -> RelateResult<'tcx, ()> { + // Loop invariants: + // + // On start of the loop iteration for `scc1`: + // + // * `scc_universes[scc1]` contains the minimum universe of the + // constituents of `scc1` + // * `scc_placeholder[scc1]` stores the placeholder that `scc1` must + // be equal to (if any) + // + // For each successor `scc2` where `scc1: scc2`: + // + // * `scc_placeholder[scc2]` stores some placeholder `P` where + // `scc2: P` (if any) + // * `scc_universes[scc2]` contains the minimum universe of the + // constituents of `scc2` and any of its successors + for scc1 in self.mini_graph.sccs.all_sccs() { + debug!( + "propagate_scc_value: scc={:?} with universe {:?}", + scc1, self.scc_universes[scc1] + ); + + // Walk over each `scc2` such that `scc1: scc2` and compute: + // + // * `scc1_universe`: the minimum universe of `scc2` and the constituents of `scc1` + // * `succ_bound`: placeholder `P` that the successors must outlive, if any (if there are multiple, + // we pick one arbitrarily) + let mut scc1_universe = self.scc_universes[scc1]; + let mut succ_bound = None; + for &scc2 in self.mini_graph.sccs.successors(scc1) { + let SccUniverse { universe: scc2_universe, region: scc2_region } = + self.scc_universes[scc2]; + + scc1_universe.take_min(scc2_universe, scc2_region.unwrap()); + + if let Some(b) = self.scc_placeholders[scc2] { + succ_bound = Some(b); + } + } + + // Update minimum universe of scc1. + self.scc_universes[scc1] = scc1_universe; + + // At this point, `scc_placeholders[scc1]` stores the placeholder that + // `scc1` must be equal to, if any. + if let Some(scc1_placeholder) = self.scc_placeholders[scc1] { + debug!( + "propagate_scc_value: scc1={:?} placeholder={:?} scc1_universe={:?}", + scc1, scc1_placeholder, scc1_universe + ); + + // Check if `P1: R` for some `R` in a universe that cannot name + // P1. That's an error. + if scc1_universe.universe.cannot_name(scc1_placeholder.universe) { + return Err(self.error(scc1_placeholder, scc1_universe.region.unwrap())); + } + + // Check if we have some placeholder where `S: P2` + // (transitively). In that case, since `S = P1`, that implies + // `P1: P2`, which is an error condition. + if let Some(scc2_placeholder) = succ_bound { + assert_ne!(scc1_placeholder, scc2_placeholder); + return Err(self.placeholder_error(scc1_placeholder, scc2_placeholder)); + } + } else { + // Otherwise, we can reach a placeholder if some successor can. + self.scc_placeholders[scc1] = succ_bound; + } + + // At this point, `scc_placeholder[scc1]` stores some placeholder that `scc1` must outlive (if any). + } + Ok(()) + } + + fn placeholder_error( + &self, + placeholder1: ty::PlaceholderRegion, + placeholder2: ty::PlaceholderRegion, + ) -> TypeError<'tcx> { + self.error(placeholder1, self.tcx.mk_region(ty::RePlaceholder(placeholder2))) + } + + fn error( + &self, + placeholder: ty::PlaceholderRegion, + other_region: ty::Region<'tcx>, + ) -> TypeError<'tcx> { + debug!("error: placeholder={:?}, other_region={:?}", placeholder, other_region); + if self.overly_polymorphic { + TypeError::RegionsOverlyPolymorphic(placeholder.name, other_region) + } else { + TypeError::RegionsInsufficientlyPolymorphic(placeholder.name, other_region) + } + } +} + +// States we need to distinguish: +// +// * must be equal to a placeholder (i.e., a placeholder is in the SCC) +// * it could conflict with some other regions in the SCC in different universes +// * or a different placeholder +// * `P1: S` and `S` must be equal to a placeholder +// * `P1: S` and `S` is in an incompatible universe +// +// So if we +// +// (a) compute which placeholder (if any) each SCC must be equal to +// (b) compute its minimum universe +// (c) compute *some* placeholder where `S: P1` (any one will do) +// +// then we get an error if: +// +// - it must be equal to a placeholder `P1` and minimum universe cannot name `P1` +// - `S: P1` and minimum universe cannot name `P1` +// - `S: P1` and we must be equal to `P2` +// +// So we want to track: +// +// * Equal placeholder (if any) +// * Some bounding placeholder (if any) +// * Minimum universe +// +// * We compute equal placeholder + minimum universe of constituents in first pass +// * Then we walk in order and compute from our dependencies `S1` where `S: S1` (`S -> S1`) +// * bounding placeholder (if any) +// * minimum universe +// * And if we must be equal to a placeholder then we check it against +// * minimum universe +// * no bounding placeholder + +/// Tracks the "minimum universe" for each SCC, along with some region that +/// caused it to change. +#[derive(Copy, Clone, Debug)] +struct SccUniverse<'tcx> { + /// For some SCC S, the minimum universe of: + /// + /// * each region R in S + /// * each SCC S1 such that S: S1 + universe: ty::UniverseIndex, + + /// Some region that caused `universe` to be what it is. + region: Option<ty::Region<'tcx>>, +} + +impl<'tcx> SccUniverse<'tcx> { + /// If `universe` is less than our current universe, then update + /// `self.universe` and `self.region`. + fn take_min(&mut self, universe: ty::UniverseIndex, region: ty::Region<'tcx>) { + if universe < self.universe || self.region.is_none() { + self.universe = universe; + self.region = Some(region); + } + } +} + +rustc_index::newtype_index! { + struct LeakCheckNode { + DEBUG_FORMAT = "LeakCheckNode({})" + } +} + +rustc_index::newtype_index! { + struct LeakCheckScc { + DEBUG_FORMAT = "LeakCheckScc({})" + } +} + +/// Represents the graph of constraints. For each `R1: R2` constraint we create +/// an edge `R1 -> R2` in the graph. +struct MiniGraph<'tcx> { + /// Map from a region to the index of the node in the graph. + nodes: FxHashMap<ty::Region<'tcx>, LeakCheckNode>, + + /// Map from node index to SCC, and stores the successors of each SCC. All + /// the regions in the same SCC are equal to one another, and if `S1 -> S2`, + /// then `S1: S2`. + sccs: Sccs<LeakCheckNode, LeakCheckScc>, +} + +impl<'tcx> MiniGraph<'tcx> { + fn new<'a>( + tcx: TyCtxt<'tcx>, + undo_log: impl Iterator<Item = &'a UndoLog<'tcx>>, + verifys: &[Verify<'tcx>], + ) -> Self + where + 'tcx: 'a, + { + let mut nodes = FxHashMap::default(); + let mut edges = Vec::new(); + + // Note that if `R2: R1`, we get a callback `r1, r2`, so `target` is first parameter. + Self::iterate_undo_log(tcx, undo_log, verifys, |target, source| { + let source_node = Self::add_node(&mut nodes, source); + let target_node = Self::add_node(&mut nodes, target); + edges.push((source_node, target_node)); + }); + let graph = VecGraph::new(nodes.len(), edges); + let sccs = Sccs::new(&graph); + Self { nodes, sccs } + } + + /// Invokes `each_edge(R1, R2)` for each edge where `R2: R1` + fn iterate_undo_log<'a>( + tcx: TyCtxt<'tcx>, + undo_log: impl Iterator<Item = &'a UndoLog<'tcx>>, + verifys: &[Verify<'tcx>], + mut each_edge: impl FnMut(ty::Region<'tcx>, ty::Region<'tcx>), + ) where + 'tcx: 'a, + { + for undo_entry in undo_log { + match undo_entry { + &AddConstraint(Constraint::VarSubVar(a, b)) => { + each_edge(tcx.mk_region(ReVar(a)), tcx.mk_region(ReVar(b))); + } + &AddConstraint(Constraint::RegSubVar(a, b)) => { + each_edge(a, tcx.mk_region(ReVar(b))); + } + &AddConstraint(Constraint::VarSubReg(a, b)) => { + each_edge(tcx.mk_region(ReVar(a)), b); + } + &AddConstraint(Constraint::RegSubReg(a, b)) => { + each_edge(a, b); + } + &AddGiven(a, b) => { + each_edge(a, tcx.mk_region(ReVar(b))); + } + &AddVerify(i) => span_bug!( + verifys[i].origin.span(), + "we never add verifications while doing higher-ranked things", + ), + &AddCombination(..) | &AddVar(..) => {} + } + } + } + + fn add_node( + nodes: &mut FxHashMap<ty::Region<'tcx>, LeakCheckNode>, + r: ty::Region<'tcx>, + ) -> LeakCheckNode { + let l = nodes.len(); + *nodes.entry(r).or_insert(LeakCheckNode::new(l)) + } +} diff --git a/compiler/rustc_infer/src/infer/region_constraints/mod.rs b/compiler/rustc_infer/src/infer/region_constraints/mod.rs new file mode 100644 index 000000000..0d4472a1c --- /dev/null +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -0,0 +1,821 @@ +//! See `README.md`. + +use self::CombineMapType::*; +use self::UndoLog::*; + +use super::{ + InferCtxtUndoLogs, MiscVariable, RegionVariableOrigin, Rollback, Snapshot, SubregionOrigin, +}; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::intern::Interned; +use rustc_data_structures::sync::Lrc; +use rustc_data_structures::undo_log::UndoLogs; +use rustc_data_structures::unify as ut; +use rustc_index::vec::IndexVec; +use rustc_middle::infer::unify_key::{RegionVidKey, UnifiedRegion}; +use rustc_middle::ty::ReStatic; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_middle::ty::{ReLateBound, ReVar}; +use rustc_middle::ty::{Region, RegionVid}; +use rustc_span::Span; + +use std::collections::BTreeMap; +use std::ops::Range; +use std::{cmp, fmt, mem}; + +mod leak_check; + +pub use rustc_middle::infer::MemberConstraint; + +#[derive(Clone, Default)] +pub struct RegionConstraintStorage<'tcx> { + /// For each `RegionVid`, the corresponding `RegionVariableOrigin`. + var_infos: IndexVec<RegionVid, RegionVariableInfo>, + + data: RegionConstraintData<'tcx>, + + /// For a given pair of regions (R1, R2), maps to a region R3 that + /// is designated as their LUB (edges R1 <= R3 and R2 <= R3 + /// exist). This prevents us from making many such regions. + lubs: CombineMap<'tcx>, + + /// For a given pair of regions (R1, R2), maps to a region R3 that + /// is designated as their GLB (edges R3 <= R1 and R3 <= R2 + /// exist). This prevents us from making many such regions. + glbs: CombineMap<'tcx>, + + /// When we add a R1 == R2 constraint, we currently add (a) edges + /// R1 <= R2 and R2 <= R1 and (b) we unify the two regions in this + /// table. You can then call `opportunistic_resolve_var` early + /// which will map R1 and R2 to some common region (i.e., either + /// R1 or R2). This is important when fulfillment, dropck and other such + /// code is iterating to a fixed point, because otherwise we sometimes + /// would wind up with a fresh stream of region variables that have been + /// equated but appear distinct. + pub(super) unification_table: ut::UnificationTableStorage<RegionVidKey<'tcx>>, + + /// a flag set to true when we perform any unifications; this is used + /// to micro-optimize `take_and_reset_data` + any_unifications: bool, +} + +pub struct RegionConstraintCollector<'a, 'tcx> { + storage: &'a mut RegionConstraintStorage<'tcx>, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, +} + +impl<'tcx> std::ops::Deref for RegionConstraintCollector<'_, 'tcx> { + type Target = RegionConstraintStorage<'tcx>; + #[inline] + fn deref(&self) -> &RegionConstraintStorage<'tcx> { + self.storage + } +} + +impl<'tcx> std::ops::DerefMut for RegionConstraintCollector<'_, 'tcx> { + #[inline] + fn deref_mut(&mut self) -> &mut RegionConstraintStorage<'tcx> { + self.storage + } +} + +pub type VarInfos = IndexVec<RegionVid, RegionVariableInfo>; + +/// The full set of region constraints gathered up by the collector. +/// Describes constraints between the region variables and other +/// regions, as well as other conditions that must be verified, or +/// assumptions that can be made. +#[derive(Debug, Default, Clone)] +pub struct RegionConstraintData<'tcx> { + /// Constraints of the form `A <= B`, where either `A` or `B` can + /// be a region variable (or neither, as it happens). + pub constraints: BTreeMap<Constraint<'tcx>, SubregionOrigin<'tcx>>, + + /// Constraints of the form `R0 member of [R1, ..., Rn]`, meaning that + /// `R0` must be equal to one of the regions `R1..Rn`. These occur + /// with `impl Trait` quite frequently. + pub member_constraints: Vec<MemberConstraint<'tcx>>, + + /// A "verify" is something that we need to verify after inference + /// is done, but which does not directly affect inference in any + /// way. + /// + /// An example is a `A <= B` where neither `A` nor `B` are + /// inference variables. + pub verifys: Vec<Verify<'tcx>>, + + /// A "given" is a relationship that is known to hold. In + /// particular, we often know from closure fn signatures that a + /// particular free region must be a subregion of a region + /// variable: + /// + /// foo.iter().filter(<'a> |x: &'a &'b T| ...) + /// + /// In situations like this, `'b` is in fact a region variable + /// introduced by the call to `iter()`, and `'a` is a bound region + /// on the closure (as indicated by the `<'a>` prefix). If we are + /// naive, we wind up inferring that `'b` must be `'static`, + /// because we require that it be greater than `'a` and we do not + /// know what `'a` is precisely. + /// + /// This hashmap is used to avoid that naive scenario. Basically + /// we record the fact that `'a <= 'b` is implied by the fn + /// signature, and then ignore the constraint when solving + /// equations. This is a bit of a hack but seems to work. + pub givens: FxHashSet<(Region<'tcx>, ty::RegionVid)>, +} + +/// Represents a constraint that influences the inference process. +#[derive(Clone, Copy, PartialEq, Eq, Debug, PartialOrd, Ord)] +pub enum Constraint<'tcx> { + /// A region variable is a subregion of another. + VarSubVar(RegionVid, RegionVid), + + /// A concrete region is a subregion of region variable. + RegSubVar(Region<'tcx>, RegionVid), + + /// A region variable is a subregion of a concrete region. This does not + /// directly affect inference, but instead is checked after + /// inference is complete. + VarSubReg(RegionVid, Region<'tcx>), + + /// A constraint where neither side is a variable. This does not + /// directly affect inference, but instead is checked after + /// inference is complete. + RegSubReg(Region<'tcx>, Region<'tcx>), +} + +impl Constraint<'_> { + pub fn involves_placeholders(&self) -> bool { + match self { + Constraint::VarSubVar(_, _) => false, + Constraint::VarSubReg(_, r) | Constraint::RegSubVar(r, _) => r.is_placeholder(), + Constraint::RegSubReg(r, s) => r.is_placeholder() || s.is_placeholder(), + } + } +} + +#[derive(Debug, Clone)] +pub struct Verify<'tcx> { + pub kind: GenericKind<'tcx>, + pub origin: SubregionOrigin<'tcx>, + pub region: Region<'tcx>, + pub bound: VerifyBound<'tcx>, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash, TypeFoldable, TypeVisitable)] +pub enum GenericKind<'tcx> { + Param(ty::ParamTy), + Projection(ty::ProjectionTy<'tcx>), +} + +/// Describes the things that some `GenericKind` value `G` is known to +/// outlive. Each variant of `VerifyBound` can be thought of as a +/// function: +/// ```ignore (pseudo-rust) +/// fn(min: Region) -> bool { .. } +/// ``` +/// where `true` means that the region `min` meets that `G: min`. +/// (False means nothing.) +/// +/// So, for example, if we have the type `T` and we have in scope that +/// `T: 'a` and `T: 'b`, then the verify bound might be: +/// ```ignore (pseudo-rust) +/// fn(min: Region) -> bool { +/// ('a: min) || ('b: min) +/// } +/// ``` +/// This is described with an `AnyRegion('a, 'b)` node. +#[derive(Debug, Clone)] +pub enum VerifyBound<'tcx> { + /// See [`VerifyIfEq`] docs + IfEq(ty::Binder<'tcx, VerifyIfEq<'tcx>>), + + /// Given a region `R`, expands to the function: + /// + /// ```ignore (pseudo-rust) + /// fn(min) -> bool { + /// R: min + /// } + /// ``` + /// + /// This is used when we can establish that `G: R` -- therefore, + /// if `R: min`, then by transitivity `G: min`. + OutlivedBy(Region<'tcx>), + + /// Given a region `R`, true if it is `'empty`. + IsEmpty, + + /// Given a set of bounds `B`, expands to the function: + /// + /// ```ignore (pseudo-rust) + /// fn(min) -> bool { + /// exists (b in B) { b(min) } + /// } + /// ``` + /// + /// In other words, if we meet some bound in `B`, that suffices. + /// This is used when all the bounds in `B` are known to apply to `G`. + AnyBound(Vec<VerifyBound<'tcx>>), + + /// Given a set of bounds `B`, expands to the function: + /// + /// ```ignore (pseudo-rust) + /// fn(min) -> bool { + /// forall (b in B) { b(min) } + /// } + /// ``` + /// + /// In other words, if we meet *all* bounds in `B`, that suffices. + /// This is used when *some* bound in `B` is known to suffice, but + /// we don't know which. + AllBounds(Vec<VerifyBound<'tcx>>), +} + +/// This is a "conditional bound" that checks the result of inference +/// and supplies a bound if it ended up being relevant. It's used in situations +/// like this: +/// +/// ```rust +/// fn foo<'a, 'b, T: SomeTrait<'a>> +/// where +/// <T as SomeTrait<'a>>::Item: 'b +/// ``` +/// +/// If we have an obligation like `<T as SomeTrait<'?x>>::Item: 'c`, then +/// we don't know yet whether it suffices to show that `'b: 'c`. If `'?x` winds +/// up being equal to `'a`, then the where-clauses on function applies, and +/// in that case we can show `'b: 'c`. But if `'?x` winds up being something +/// else, the bound isn't relevant. +/// +/// In the [`VerifyBound`], this struct is enclosed in `Binder to account +/// for cases like +/// +/// ```rust +/// where for<'a> <T as SomeTrait<'a>::Item: 'a +/// ``` +/// +/// The idea is that we have to find some instantiation of `'a` that can +/// make `<T as SomeTrait<'a>>::Item` equal to the final value of `G`, +/// the generic we are checking. +/// +/// ```ignore (pseudo-rust) +/// fn(min) -> bool { +/// exists<'a> { +/// if G == K { +/// B(min) +/// } else { +/// false +/// } +/// } +/// } +/// ``` +#[derive(Debug, Copy, Clone, TypeFoldable, TypeVisitable)] +pub struct VerifyIfEq<'tcx> { + /// Type which must match the generic `G` + pub ty: Ty<'tcx>, + + /// Bound that applies if `ty` is equal. + pub bound: Region<'tcx>, +} + +#[derive(Copy, Clone, PartialEq, Eq, Hash)] +pub(crate) struct TwoRegions<'tcx> { + a: Region<'tcx>, + b: Region<'tcx>, +} + +#[derive(Copy, Clone, PartialEq)] +pub(crate) enum UndoLog<'tcx> { + /// We added `RegionVid`. + AddVar(RegionVid), + + /// We added the given `constraint`. + AddConstraint(Constraint<'tcx>), + + /// We added the given `verify`. + AddVerify(usize), + + /// We added the given `given`. + AddGiven(Region<'tcx>, ty::RegionVid), + + /// We added a GLB/LUB "combination variable". + AddCombination(CombineMapType, TwoRegions<'tcx>), +} + +#[derive(Copy, Clone, PartialEq)] +pub(crate) enum CombineMapType { + Lub, + Glb, +} + +type CombineMap<'tcx> = FxHashMap<TwoRegions<'tcx>, RegionVid>; + +#[derive(Debug, Clone, Copy)] +pub struct RegionVariableInfo { + pub origin: RegionVariableOrigin, + pub universe: ty::UniverseIndex, +} + +pub struct RegionSnapshot { + any_unifications: bool, +} + +impl<'tcx> RegionConstraintStorage<'tcx> { + pub fn new() -> Self { + Self::default() + } + + #[inline] + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, + ) -> RegionConstraintCollector<'a, 'tcx> { + RegionConstraintCollector { storage: self, undo_log } + } + + fn rollback_undo_entry(&mut self, undo_entry: UndoLog<'tcx>) { + match undo_entry { + AddVar(vid) => { + self.var_infos.pop().unwrap(); + assert_eq!(self.var_infos.len(), vid.index() as usize); + } + AddConstraint(ref constraint) => { + self.data.constraints.remove(constraint); + } + AddVerify(index) => { + self.data.verifys.pop(); + assert_eq!(self.data.verifys.len(), index); + } + AddGiven(sub, sup) => { + self.data.givens.remove(&(sub, sup)); + } + AddCombination(Glb, ref regions) => { + self.glbs.remove(regions); + } + AddCombination(Lub, ref regions) => { + self.lubs.remove(regions); + } + } + } +} + +impl<'tcx> RegionConstraintCollector<'_, 'tcx> { + pub fn num_region_vars(&self) -> usize { + self.var_infos.len() + } + + pub fn region_constraint_data(&self) -> &RegionConstraintData<'tcx> { + &self.data + } + + /// Once all the constraints have been gathered, extract out the final data. + /// + /// Not legal during a snapshot. + pub fn into_infos_and_data(self) -> (VarInfos, RegionConstraintData<'tcx>) { + assert!(!UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log)); + (mem::take(&mut self.storage.var_infos), mem::take(&mut self.storage.data)) + } + + /// Takes (and clears) the current set of constraints. Note that + /// the set of variables remains intact, but all relationships + /// between them are reset. This is used during NLL checking to + /// grab the set of constraints that arose from a particular + /// operation. + /// + /// We don't want to leak relationships between variables between + /// points because just because (say) `r1 == r2` was true at some + /// point P in the graph doesn't imply that it will be true at + /// some other point Q, in NLL. + /// + /// Not legal during a snapshot. + pub fn take_and_reset_data(&mut self) -> RegionConstraintData<'tcx> { + assert!(!UndoLogs::<super::UndoLog<'_>>::in_snapshot(&self.undo_log)); + + // If you add a new field to `RegionConstraintCollector`, you + // should think carefully about whether it needs to be cleared + // or updated in some way. + let RegionConstraintStorage { + var_infos: _, + data, + lubs, + glbs, + unification_table: _, + any_unifications, + } = self.storage; + + // Clear the tables of (lubs, glbs), so that we will create + // fresh regions if we do a LUB operation. As it happens, + // LUB/GLB are not performed by the MIR type-checker, which is + // the one that uses this method, but it's good to be correct. + lubs.clear(); + glbs.clear(); + + let data = mem::take(data); + + // Clear all unifications and recreate the variables a "now + // un-unified" state. Note that when we unify `a` and `b`, we + // also insert `a <= b` and a `b <= a` edges, so the + // `RegionConstraintData` contains the relationship here. + if *any_unifications { + *any_unifications = false; + self.unification_table().reset_unifications(|_| UnifiedRegion(None)); + } + + data + } + + pub fn data(&self) -> &RegionConstraintData<'tcx> { + &self.data + } + + pub fn start_snapshot(&mut self) -> RegionSnapshot { + debug!("RegionConstraintCollector: start_snapshot"); + RegionSnapshot { any_unifications: self.any_unifications } + } + + pub fn rollback_to(&mut self, snapshot: RegionSnapshot) { + debug!("RegionConstraintCollector: rollback_to({:?})", snapshot); + self.any_unifications = snapshot.any_unifications; + } + + pub fn new_region_var( + &mut self, + universe: ty::UniverseIndex, + origin: RegionVariableOrigin, + ) -> RegionVid { + let vid = self.var_infos.push(RegionVariableInfo { origin, universe }); + + let u_vid = self.unification_table().new_key(UnifiedRegion(None)); + assert_eq!(vid, u_vid.vid); + self.undo_log.push(AddVar(vid)); + debug!("created new region variable {:?} in {:?} with origin {:?}", vid, universe, origin); + vid + } + + /// Returns the universe for the given variable. + pub fn var_universe(&self, vid: RegionVid) -> ty::UniverseIndex { + self.var_infos[vid].universe + } + + /// Returns the origin for the given variable. + pub fn var_origin(&self, vid: RegionVid) -> RegionVariableOrigin { + self.var_infos[vid].origin + } + + fn add_constraint(&mut self, constraint: Constraint<'tcx>, origin: SubregionOrigin<'tcx>) { + // cannot add constraints once regions are resolved + debug!("RegionConstraintCollector: add_constraint({:?})", constraint); + + // never overwrite an existing (constraint, origin) - only insert one if it isn't + // present in the map yet. This prevents origins from outside the snapshot being + // replaced with "less informative" origins e.g., during calls to `can_eq` + let undo_log = &mut self.undo_log; + self.storage.data.constraints.entry(constraint).or_insert_with(|| { + undo_log.push(AddConstraint(constraint)); + origin + }); + } + + fn add_verify(&mut self, verify: Verify<'tcx>) { + // cannot add verifys once regions are resolved + debug!("RegionConstraintCollector: add_verify({:?})", verify); + + // skip no-op cases known to be satisfied + if let VerifyBound::AllBounds(ref bs) = verify.bound && bs.is_empty() { + return; + } + + let index = self.data.verifys.len(); + self.data.verifys.push(verify); + self.undo_log.push(AddVerify(index)); + } + + pub fn add_given(&mut self, sub: Region<'tcx>, sup: ty::RegionVid) { + // cannot add givens once regions are resolved + if self.data.givens.insert((sub, sup)) { + debug!("add_given({:?} <= {:?})", sub, sup); + + self.undo_log.push(AddGiven(sub, sup)); + } + } + + pub fn make_eqregion( + &mut self, + origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) { + if sub != sup { + // Eventually, it would be nice to add direct support for + // equating regions. + self.make_subregion(origin.clone(), sub, sup); + self.make_subregion(origin, sup, sub); + + match (sub, sup) { + (Region(Interned(ReVar(sub), _)), Region(Interned(ReVar(sup), _))) => { + debug!("make_eqregion: unifying {:?} with {:?}", sub, sup); + self.unification_table().union(*sub, *sup); + self.any_unifications = true; + } + (Region(Interned(ReVar(vid), _)), value) + | (value, Region(Interned(ReVar(vid), _))) => { + debug!("make_eqregion: unifying {:?} with {:?}", vid, value); + self.unification_table().union_value(*vid, UnifiedRegion(Some(value))); + self.any_unifications = true; + } + (_, _) => {} + } + } + } + + pub fn member_constraint( + &mut self, + key: ty::OpaqueTypeKey<'tcx>, + definition_span: Span, + hidden_ty: Ty<'tcx>, + member_region: ty::Region<'tcx>, + choice_regions: &Lrc<Vec<ty::Region<'tcx>>>, + ) { + debug!("member_constraint({:?} in {:#?})", member_region, choice_regions); + + if choice_regions.iter().any(|&r| r == member_region) { + return; + } + + self.data.member_constraints.push(MemberConstraint { + key, + definition_span, + hidden_ty, + member_region, + choice_regions: choice_regions.clone(), + }); + } + + #[instrument(skip(self, origin), level = "debug")] + pub fn make_subregion( + &mut self, + origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) { + // cannot add constraints once regions are resolved + debug!("origin = {:#?}", origin); + + match (*sub, *sup) { + (ReLateBound(..), _) | (_, ReLateBound(..)) => { + span_bug!(origin.span(), "cannot relate bound region: {:?} <= {:?}", sub, sup); + } + (_, ReStatic) => { + // all regions are subregions of static, so we can ignore this + } + (ReVar(sub_id), ReVar(sup_id)) => { + self.add_constraint(Constraint::VarSubVar(sub_id, sup_id), origin); + } + (_, ReVar(sup_id)) => { + self.add_constraint(Constraint::RegSubVar(sub, sup_id), origin); + } + (ReVar(sub_id), _) => { + self.add_constraint(Constraint::VarSubReg(sub_id, sup), origin); + } + _ => { + self.add_constraint(Constraint::RegSubReg(sub, sup), origin); + } + } + } + + pub fn verify_generic_bound( + &mut self, + origin: SubregionOrigin<'tcx>, + kind: GenericKind<'tcx>, + sub: Region<'tcx>, + bound: VerifyBound<'tcx>, + ) { + self.add_verify(Verify { kind, origin, region: sub, bound }); + } + + pub fn lub_regions( + &mut self, + tcx: TyCtxt<'tcx>, + origin: SubregionOrigin<'tcx>, + a: Region<'tcx>, + b: Region<'tcx>, + ) -> Region<'tcx> { + // cannot add constraints once regions are resolved + debug!("RegionConstraintCollector: lub_regions({:?}, {:?})", a, b); + if a.is_static() || b.is_static() { + a // nothing lives longer than static + } else if a == b { + a // LUB(a,a) = a + } else { + self.combine_vars(tcx, Lub, a, b, origin) + } + } + + pub fn glb_regions( + &mut self, + tcx: TyCtxt<'tcx>, + origin: SubregionOrigin<'tcx>, + a: Region<'tcx>, + b: Region<'tcx>, + ) -> Region<'tcx> { + // cannot add constraints once regions are resolved + debug!("RegionConstraintCollector: glb_regions({:?}, {:?})", a, b); + if a.is_static() { + b // static lives longer than everything else + } else if b.is_static() { + a // static lives longer than everything else + } else if a == b { + a // GLB(a,a) = a + } else { + self.combine_vars(tcx, Glb, a, b, origin) + } + } + + /// Resolves the passed RegionVid to the root RegionVid in the unification table + pub fn opportunistic_resolve_var(&mut self, rid: ty::RegionVid) -> ty::RegionVid { + self.unification_table().find(rid).vid + } + + /// If the Region is a `ReVar`, then resolves it either to the root value in + /// the unification table, if it exists, or to the root `ReVar` in the table. + /// If the Region is not a `ReVar`, just returns the Region itself. + pub fn opportunistic_resolve_region( + &mut self, + tcx: TyCtxt<'tcx>, + region: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + match *region { + ty::ReVar(rid) => { + let unified_region = self.unification_table().probe_value(rid); + unified_region.0.unwrap_or_else(|| { + let root = self.unification_table().find(rid).vid; + tcx.reuse_or_mk_region(region, ty::ReVar(root)) + }) + } + _ => region, + } + } + + fn combine_map(&mut self, t: CombineMapType) -> &mut CombineMap<'tcx> { + match t { + Glb => &mut self.glbs, + Lub => &mut self.lubs, + } + } + + fn combine_vars( + &mut self, + tcx: TyCtxt<'tcx>, + t: CombineMapType, + a: Region<'tcx>, + b: Region<'tcx>, + origin: SubregionOrigin<'tcx>, + ) -> Region<'tcx> { + let vars = TwoRegions { a, b }; + if let Some(&c) = self.combine_map(t).get(&vars) { + return tcx.mk_region(ReVar(c)); + } + let a_universe = self.universe(a); + let b_universe = self.universe(b); + let c_universe = cmp::max(a_universe, b_universe); + let c = self.new_region_var(c_universe, MiscVariable(origin.span())); + self.combine_map(t).insert(vars, c); + self.undo_log.push(AddCombination(t, vars)); + let new_r = tcx.mk_region(ReVar(c)); + for old_r in [a, b] { + match t { + Glb => self.make_subregion(origin.clone(), new_r, old_r), + Lub => self.make_subregion(origin.clone(), old_r, new_r), + } + } + debug!("combine_vars() c={:?}", c); + new_r + } + + pub fn universe(&self, region: Region<'tcx>) -> ty::UniverseIndex { + match *region { + ty::ReStatic | ty::ReErased | ty::ReFree(..) | ty::ReEarlyBound(..) => { + ty::UniverseIndex::ROOT + } + ty::ReEmpty(ui) => ui, + ty::RePlaceholder(placeholder) => placeholder.universe, + ty::ReVar(vid) => self.var_universe(vid), + ty::ReLateBound(..) => bug!("universe(): encountered bound region {:?}", region), + } + } + + pub fn vars_since_snapshot( + &self, + value_count: usize, + ) -> (Range<RegionVid>, Vec<RegionVariableOrigin>) { + let range = RegionVid::from(value_count)..RegionVid::from(self.unification_table.len()); + ( + range.clone(), + (range.start.index()..range.end.index()) + .map(|index| self.var_infos[ty::RegionVid::from(index)].origin) + .collect(), + ) + } + + /// See `InferCtxt::region_constraints_added_in_snapshot`. + pub fn region_constraints_added_in_snapshot(&self, mark: &Snapshot<'tcx>) -> Option<bool> { + self.undo_log + .region_constraints_in_snapshot(mark) + .map(|&elt| match elt { + AddConstraint(constraint) => Some(constraint.involves_placeholders()), + _ => None, + }) + .max() + .unwrap_or(None) + } + + #[inline] + fn unification_table(&mut self) -> super::UnificationTable<'_, 'tcx, RegionVidKey<'tcx>> { + ut::UnificationTable::with_log(&mut self.storage.unification_table, self.undo_log) + } +} + +impl fmt::Debug for RegionSnapshot { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "RegionSnapshot") + } +} + +impl<'tcx> fmt::Debug for GenericKind<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + GenericKind::Param(ref p) => write!(f, "{:?}", p), + GenericKind::Projection(ref p) => write!(f, "{:?}", p), + } + } +} + +impl<'tcx> fmt::Display for GenericKind<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + GenericKind::Param(ref p) => write!(f, "{}", p), + GenericKind::Projection(ref p) => write!(f, "{}", p), + } + } +} + +impl<'tcx> GenericKind<'tcx> { + pub fn to_ty(&self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { + match *self { + GenericKind::Param(ref p) => p.to_ty(tcx), + GenericKind::Projection(ref p) => tcx.mk_projection(p.item_def_id, p.substs), + } + } +} + +impl<'tcx> VerifyBound<'tcx> { + pub fn must_hold(&self) -> bool { + match self { + VerifyBound::IfEq(..) => false, + VerifyBound::OutlivedBy(re) => re.is_static(), + VerifyBound::IsEmpty => false, + VerifyBound::AnyBound(bs) => bs.iter().any(|b| b.must_hold()), + VerifyBound::AllBounds(bs) => bs.iter().all(|b| b.must_hold()), + } + } + + pub fn cannot_hold(&self) -> bool { + match self { + VerifyBound::IfEq(..) => false, + VerifyBound::IsEmpty => false, + VerifyBound::OutlivedBy(_) => false, + VerifyBound::AnyBound(bs) => bs.iter().all(|b| b.cannot_hold()), + VerifyBound::AllBounds(bs) => bs.iter().any(|b| b.cannot_hold()), + } + } + + pub fn or(self, vb: VerifyBound<'tcx>) -> VerifyBound<'tcx> { + if self.must_hold() || vb.cannot_hold() { + self + } else if self.cannot_hold() || vb.must_hold() { + vb + } else { + VerifyBound::AnyBound(vec![self, vb]) + } + } +} + +impl<'tcx> RegionConstraintData<'tcx> { + /// Returns `true` if this region constraint data contains no constraints, and `false` + /// otherwise. + pub fn is_empty(&self) -> bool { + let RegionConstraintData { constraints, member_constraints, verifys, givens } = self; + constraints.is_empty() + && member_constraints.is_empty() + && verifys.is_empty() + && givens.is_empty() + } +} + +impl<'tcx> Rollback<UndoLog<'tcx>> for RegionConstraintStorage<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + self.rollback_undo_entry(undo) + } +} diff --git a/compiler/rustc_infer/src/infer/resolve.rs b/compiler/rustc_infer/src/infer/resolve.rs new file mode 100644 index 000000000..3d99f0958 --- /dev/null +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -0,0 +1,237 @@ +use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use super::{FixupError, FixupResult, InferCtxt, Span}; +use rustc_middle::mir; +use rustc_middle::ty::fold::{FallibleTypeFolder, TypeFolder, TypeSuperFoldable}; +use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitor}; +use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable, TypeVisitable}; + +use std::ops::ControlFlow; + +/////////////////////////////////////////////////////////////////////////// +// OPPORTUNISTIC VAR RESOLVER + +/// The opportunistic resolver can be used at any time. It simply replaces +/// type/const variables that have been unified with the things they have +/// been unified with (similar to `shallow_resolve`, but deep). This is +/// useful for printing messages etc but also required at various +/// points for correctness. +pub struct OpportunisticVarResolver<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, +} + +impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> { + #[inline] + pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self { + OpportunisticVarResolver { infcx } + } +} + +impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + if !t.has_infer_types_or_consts() { + t // micro-optimize -- if there is nothing in this type that this fold affects... + } else { + let t = self.infcx.shallow_resolve(t); + t.super_fold_with(self) + } + } + + fn fold_const(&mut self, ct: Const<'tcx>) -> Const<'tcx> { + if !ct.has_infer_types_or_consts() { + ct // micro-optimize -- if there is nothing in this const that this fold affects... + } else { + let ct = self.infcx.shallow_resolve(ct); + ct.super_fold_with(self) + } + } + + fn fold_mir_const(&mut self, constant: mir::ConstantKind<'tcx>) -> mir::ConstantKind<'tcx> { + constant.super_fold_with(self) + } +} + +/// The opportunistic region resolver opportunistically resolves regions +/// variables to the variable with the least variable id. It is used when +/// normalizing projections to avoid hitting the recursion limit by creating +/// many versions of a predicate for types that in the end have to unify. +/// +/// If you want to resolve type and const variables as well, call +/// [InferCtxt::resolve_vars_if_possible] first. +pub struct OpportunisticRegionResolver<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, +} + +impl<'a, 'tcx> OpportunisticRegionResolver<'a, 'tcx> { + pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self { + OpportunisticRegionResolver { infcx } + } +} + +impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticRegionResolver<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + if !t.has_infer_regions() { + t // micro-optimize -- if there is nothing in this type that this fold affects... + } else { + t.super_fold_with(self) + } + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + ty::ReVar(rid) => { + let resolved = self + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .opportunistic_resolve_var(rid); + TypeFolder::tcx(self).reuse_or_mk_region(r, ty::ReVar(resolved)) + } + _ => r, + } + } + + fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { + if !ct.has_infer_regions() { + ct // micro-optimize -- if there is nothing in this const that this fold affects... + } else { + ct.super_fold_with(self) + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// UNRESOLVED TYPE FINDER + +/// The unresolved type **finder** walks a type searching for +/// type variables that don't yet have a value. The first unresolved type is stored. +/// It does not construct the fully resolved type (which might +/// involve some hashing and so forth). +pub struct UnresolvedTypeFinder<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, +} + +impl<'a, 'tcx> UnresolvedTypeFinder<'a, 'tcx> { + pub fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self { + UnresolvedTypeFinder { infcx } + } +} + +impl<'a, 'tcx> TypeVisitor<'tcx> for UnresolvedTypeFinder<'a, 'tcx> { + type BreakTy = (Ty<'tcx>, Option<Span>); + fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { + let t = self.infcx.shallow_resolve(t); + if t.has_infer_types() { + if let ty::Infer(infer_ty) = *t.kind() { + // Since we called `shallow_resolve` above, this must + // be an (as yet...) unresolved inference variable. + let ty_var_span = if let ty::TyVar(ty_vid) = infer_ty { + let mut inner = self.infcx.inner.borrow_mut(); + let ty_vars = &inner.type_variables(); + if let TypeVariableOrigin { + kind: TypeVariableOriginKind::TypeParameterDefinition(_, _), + span, + } = *ty_vars.var_origin(ty_vid) + { + Some(span) + } else { + None + } + } else { + None + }; + ControlFlow::Break((t, ty_var_span)) + } else { + // Otherwise, visit its contents. + t.super_visit_with(self) + } + } else { + // All type variables in inference types must already be resolved, + // - no need to visit the contents, continue visiting. + ControlFlow::CONTINUE + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// FULL TYPE RESOLUTION + +/// Full type resolution replaces all type and region variables with +/// their concrete results. If any variable cannot be replaced (never unified, etc) +/// then an `Err` result is returned. +pub fn fully_resolve<'a, 'tcx, T>(infcx: &InferCtxt<'a, 'tcx>, value: T) -> FixupResult<'tcx, T> +where + T: TypeFoldable<'tcx>, +{ + value.try_fold_with(&mut FullTypeResolver { infcx }) +} + +// N.B. This type is not public because the protocol around checking the +// `err` field is not enforceable otherwise. +struct FullTypeResolver<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, +} + +impl<'a, 'tcx> FallibleTypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { + type Error = FixupError<'tcx>; + + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> { + if !t.needs_infer() { + Ok(t) // micro-optimize -- if there is nothing in this type that this fold affects... + } else { + let t = self.infcx.shallow_resolve(t); + match *t.kind() { + ty::Infer(ty::TyVar(vid)) => Err(FixupError::UnresolvedTy(vid)), + ty::Infer(ty::IntVar(vid)) => Err(FixupError::UnresolvedIntTy(vid)), + ty::Infer(ty::FloatVar(vid)) => Err(FixupError::UnresolvedFloatTy(vid)), + ty::Infer(_) => { + bug!("Unexpected type in full type resolver: {:?}", t); + } + _ => t.try_super_fold_with(self), + } + } + } + + fn try_fold_region(&mut self, r: ty::Region<'tcx>) -> Result<ty::Region<'tcx>, Self::Error> { + match *r { + ty::ReVar(_) => Ok(self + .infcx + .lexical_region_resolutions + .borrow() + .as_ref() + .expect("region resolution not performed") + .resolve_region(self.infcx.tcx, r)), + _ => Ok(r), + } + } + + fn try_fold_const(&mut self, c: ty::Const<'tcx>) -> Result<ty::Const<'tcx>, Self::Error> { + if !c.needs_infer() { + Ok(c) // micro-optimize -- if there is nothing in this const that this fold affects... + } else { + let c = self.infcx.shallow_resolve(c); + match c.kind() { + ty::ConstKind::Infer(InferConst::Var(vid)) => { + return Err(FixupError::UnresolvedConst(vid)); + } + ty::ConstKind::Infer(InferConst::Fresh(_)) => { + bug!("Unexpected const in full const resolver: {:?}", c); + } + _ => {} + } + c.try_super_fold_with(self) + } + } +} diff --git a/compiler/rustc_infer/src/infer/sub.rs b/compiler/rustc_infer/src/infer/sub.rs new file mode 100644 index 000000000..b27571275 --- /dev/null +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -0,0 +1,210 @@ +use super::combine::{CombineFields, RelationDir}; +use super::SubregionOrigin; + +use crate::infer::combine::ConstEquateRelation; +use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind}; +use crate::traits::Obligation; +use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::visit::TypeVisitable; +use rustc_middle::ty::TyVar; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; +use std::mem; + +/// Ensures `a` is made a subtype of `b`. Returns `a` on success. +pub struct Sub<'combine, 'infcx, 'tcx> { + fields: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, +} + +impl<'combine, 'infcx, 'tcx> Sub<'combine, 'infcx, 'tcx> { + pub fn new( + f: &'combine mut CombineFields<'infcx, 'tcx>, + a_is_expected: bool, + ) -> Sub<'combine, 'infcx, 'tcx> { + Sub { fields: f, a_is_expected } + } + + fn with_expected_switched<R, F: FnOnce(&mut Self) -> R>(&mut self, f: F) -> R { + self.a_is_expected = !self.a_is_expected; + let result = f(self); + self.a_is_expected = !self.a_is_expected; + result + } +} + +impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> { + fn tag(&self) -> &'static str { + "Sub" + } + fn tcx(&self) -> TyCtxt<'tcx> { + self.fields.infcx.tcx + } + + fn param_env(&self) -> ty::ParamEnv<'tcx> { + self.fields.param_env + } + + fn a_is_expected(&self) -> bool { + self.a_is_expected + } + + fn with_cause<F, R>(&mut self, cause: Cause, f: F) -> R + where + F: FnOnce(&mut Self) -> R, + { + debug!("sub with_cause={:?}", cause); + let old_cause = mem::replace(&mut self.fields.cause, Some(cause)); + let r = f(self); + debug!("sub old_cause={:?}", old_cause); + self.fields.cause = old_cause; + r + } + + fn relate_with_variance<T: Relate<'tcx>>( + &mut self, + variance: ty::Variance, + _info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + match variance { + ty::Invariant => self.fields.equate(self.a_is_expected).relate(a, b), + ty::Covariant => self.relate(a, b), + ty::Bivariant => Ok(a), + ty::Contravariant => self.with_expected_switched(|this| this.relate(b, a)), + } + } + + #[instrument(skip(self), level = "debug")] + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + if a == b { + return Ok(a); + } + + let infcx = self.fields.infcx; + let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a); + let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b); + + match (a.kind(), b.kind()) { + (&ty::Infer(TyVar(_)), &ty::Infer(TyVar(_))) => { + // Shouldn't have any LBR here, so we can safely put + // this under a binder below without fear of accidental + // capture. + assert!(!a.has_escaping_bound_vars()); + assert!(!b.has_escaping_bound_vars()); + + // can't make progress on `A <: B` if both A and B are + // type variables, so record an obligation. + self.fields.obligations.push(Obligation::new( + self.fields.trace.cause.clone(), + self.fields.param_env, + ty::Binder::dummy(ty::PredicateKind::Subtype(ty::SubtypePredicate { + a_is_expected: self.a_is_expected, + a, + b, + })) + .to_predicate(self.tcx()), + )); + + Ok(a) + } + (&ty::Infer(TyVar(a_id)), _) => { + self.fields.instantiate(b, RelationDir::SupertypeOf, a_id, !self.a_is_expected)?; + Ok(a) + } + (_, &ty::Infer(TyVar(b_id))) => { + self.fields.instantiate(a, RelationDir::SubtypeOf, b_id, self.a_is_expected)?; + Ok(a) + } + + (&ty::Error(_), _) | (_, &ty::Error(_)) => { + infcx.set_tainted_by_errors(); + Ok(self.tcx().ty_error()) + } + + (&ty::Opaque(a_def_id, _), &ty::Opaque(b_def_id, _)) if a_def_id == b_def_id => { + self.fields.infcx.super_combine_tys(self, a, b)?; + Ok(a) + } + (&ty::Opaque(did, ..), _) | (_, &ty::Opaque(did, ..)) + if self.fields.define_opaque_types && did.is_local() => + { + let mut generalize = |ty, ty_is_expected| { + let var = infcx.next_ty_var_id_in_universe( + TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span: self.fields.trace.cause.span, + }, + ty::UniverseIndex::ROOT, + ); + self.fields.instantiate(ty, RelationDir::SubtypeOf, var, ty_is_expected)?; + Ok(infcx.tcx.mk_ty_var(var)) + }; + let (a, b) = if self.a_is_expected { (a, b) } else { (b, a) }; + let (a, b) = match (a.kind(), b.kind()) { + (&ty::Opaque(..), _) => (a, generalize(b, true)?), + (_, &ty::Opaque(..)) => (generalize(a, false)?, b), + _ => unreachable!(), + }; + self.fields.obligations.extend( + infcx + .handle_opaque_type(a, b, true, &self.fields.trace.cause, self.param_env())? + .obligations, + ); + Ok(a) + } + + _ => { + self.fields.infcx.super_combine_tys(self, a, b)?; + Ok(a) + } + } + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("{}.regions({:?}, {:?}) self.cause={:?}", self.tag(), a, b, self.fields.cause); + + // FIXME -- we have more fine-grained information available + // from the "cause" field, we could perhaps give more tailored + // error messages. + let origin = SubregionOrigin::Subtype(Box::new(self.fields.trace.clone())); + self.fields + .infcx + .inner + .borrow_mut() + .unwrap_region_constraints() + .make_subregion(origin, a, b); + + Ok(a) + } + + fn consts( + &mut self, + a: ty::Const<'tcx>, + b: ty::Const<'tcx>, + ) -> RelateResult<'tcx, ty::Const<'tcx>> { + self.fields.infcx.super_combine_consts(self, a, b) + } + + fn binders<T>( + &mut self, + a: ty::Binder<'tcx, T>, + b: ty::Binder<'tcx, T>, + ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + self.fields.higher_ranked_sub(a, b, self.a_is_expected)?; + Ok(a) + } +} + +impl<'tcx> ConstEquateRelation<'tcx> for Sub<'_, '_, 'tcx> { + fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) { + self.fields.add_const_equate_obligation(self.a_is_expected, a, b); + } +} diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs new file mode 100644 index 000000000..a0e2965b6 --- /dev/null +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -0,0 +1,460 @@ +use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, Ty, TyVid}; +use rustc_span::symbol::Symbol; +use rustc_span::Span; + +use crate::infer::InferCtxtUndoLogs; + +use rustc_data_structures::snapshot_vec as sv; +use rustc_data_structures::unify as ut; +use std::cmp; +use std::marker::PhantomData; +use std::ops::Range; + +use rustc_data_structures::undo_log::{Rollback, UndoLogs}; + +/// Represents a single undo-able action that affects a type inference variable. +#[derive(Clone)] +pub(crate) enum UndoLog<'tcx> { + EqRelation(sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>), + SubRelation(sv::UndoLog<ut::Delegate<ty::TyVid>>), + Values(sv::UndoLog<Delegate>), +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From<sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>> for UndoLog<'tcx> { + fn from(l: sv::UndoLog<ut::Delegate<TyVidEqKey<'tcx>>>) -> Self { + UndoLog::EqRelation(l) + } +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From<sv::UndoLog<ut::Delegate<ty::TyVid>>> for UndoLog<'tcx> { + fn from(l: sv::UndoLog<ut::Delegate<ty::TyVid>>) -> Self { + UndoLog::SubRelation(l) + } +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From<sv::UndoLog<Delegate>> for UndoLog<'tcx> { + fn from(l: sv::UndoLog<Delegate>) -> Self { + UndoLog::Values(l) + } +} + +/// Convert from a specific kind of undo to the more general UndoLog +impl<'tcx> From<Instantiate> for UndoLog<'tcx> { + fn from(l: Instantiate) -> Self { + UndoLog::Values(sv::UndoLog::Other(l)) + } +} + +impl<'tcx> Rollback<UndoLog<'tcx>> for TypeVariableStorage<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + match undo { + UndoLog::EqRelation(undo) => self.eq_relations.reverse(undo), + UndoLog::SubRelation(undo) => self.sub_relations.reverse(undo), + UndoLog::Values(undo) => self.values.reverse(undo), + } + } +} + +#[derive(Clone)] +pub struct TypeVariableStorage<'tcx> { + values: sv::SnapshotVecStorage<Delegate>, + + /// Two variables are unified in `eq_relations` when we have a + /// constraint `?X == ?Y`. This table also stores, for each key, + /// the known value. + eq_relations: ut::UnificationTableStorage<TyVidEqKey<'tcx>>, + + /// Two variables are unified in `sub_relations` when we have a + /// constraint `?X <: ?Y` *or* a constraint `?Y <: ?X`. This second + /// table exists only to help with the occurs check. In particular, + /// we want to report constraints like these as an occurs check + /// violation: + /// ``` text + /// ?1 <: ?3 + /// Box<?3> <: ?1 + /// ``` + /// Without this second table, what would happen in a case like + /// this is that we would instantiate `?1` with a generalized + /// type like `Box<?6>`. We would then relate `Box<?3> <: Box<?6>` + /// and infer that `?3 <: ?6`. Next, since `?1` was instantiated, + /// we would process `?1 <: ?3`, generalize `?1 = Box<?6>` to `Box<?9>`, + /// and instantiate `?3` with `Box<?9>`. Finally, we would relate + /// `?6 <: ?9`. But now that we instantiated `?3`, we can process + /// `?3 <: ?6`, which gives us `Box<?9> <: ?6`... and the cycle + /// continues. (This is `occurs-check-2.rs`.) + /// + /// What prevents this cycle is that when we generalize + /// `Box<?3>` to `Box<?6>`, we also sub-unify `?3` and `?6` + /// (in the generalizer). When we then process `Box<?6> <: ?3`, + /// the occurs check then fails because `?6` and `?3` are sub-unified, + /// and hence generalization fails. + /// + /// This is reasonable because, in Rust, subtypes have the same + /// "skeleton" and hence there is no possible type such that + /// (e.g.) `Box<?3> <: ?3` for any `?3`. + /// + /// In practice, we sometimes sub-unify variables in other spots, such + /// as when processing subtype predicates. This is not necessary but is + /// done to aid diagnostics, as it allows us to be more effective when + /// we guide the user towards where they should insert type hints. + sub_relations: ut::UnificationTableStorage<ty::TyVid>, +} + +pub struct TypeVariableTable<'a, 'tcx> { + storage: &'a mut TypeVariableStorage<'tcx>, + + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, +} + +#[derive(Copy, Clone, Debug)] +pub struct TypeVariableOrigin { + pub kind: TypeVariableOriginKind, + pub span: Span, +} + +/// Reasons to create a type inference variable +#[derive(Copy, Clone, Debug)] +pub enum TypeVariableOriginKind { + MiscVariable, + NormalizeProjectionType, + TypeInference, + TypeParameterDefinition(Symbol, Option<DefId>), + + /// One of the upvars or closure kind parameters in a `ClosureSubsts` + /// (before it has been determined). + // FIXME(eddyb) distinguish upvar inference variables from the rest. + ClosureSynthetic, + SubstitutionPlaceholder, + AutoDeref, + AdjustmentType, + + /// In type check, when we are type checking a function that + /// returns `-> dyn Foo`, we substitute a type variable for the + /// return type for diagnostic purposes. + DynReturnFn, + LatticeVariable, +} + +#[derive(Clone)] +pub(crate) struct TypeVariableData { + origin: TypeVariableOrigin, +} + +#[derive(Copy, Clone, Debug)] +pub enum TypeVariableValue<'tcx> { + Known { value: Ty<'tcx> }, + Unknown { universe: ty::UniverseIndex }, +} + +impl<'tcx> TypeVariableValue<'tcx> { + /// If this value is known, returns the type it is known to be. + /// Otherwise, `None`. + pub fn known(&self) -> Option<Ty<'tcx>> { + match *self { + TypeVariableValue::Unknown { .. } => None, + TypeVariableValue::Known { value } => Some(value), + } + } + + pub fn is_unknown(&self) -> bool { + match *self { + TypeVariableValue::Unknown { .. } => true, + TypeVariableValue::Known { .. } => false, + } + } +} + +#[derive(Clone)] +pub(crate) struct Instantiate; + +pub(crate) struct Delegate; + +impl<'tcx> TypeVariableStorage<'tcx> { + pub fn new() -> TypeVariableStorage<'tcx> { + TypeVariableStorage { + values: sv::SnapshotVecStorage::new(), + eq_relations: ut::UnificationTableStorage::new(), + sub_relations: ut::UnificationTableStorage::new(), + } + } + + #[inline] + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, + ) -> TypeVariableTable<'a, 'tcx> { + TypeVariableTable { storage: self, undo_log } + } +} + +impl<'tcx> TypeVariableTable<'_, 'tcx> { + /// Returns the origin that was given when `vid` was created. + /// + /// Note that this function does not return care whether + /// `vid` has been unified with something else or not. + pub fn var_origin(&self, vid: ty::TyVid) -> &TypeVariableOrigin { + &self.storage.values.get(vid.as_usize()).origin + } + + /// Records that `a == b`, depending on `dir`. + /// + /// Precondition: neither `a` nor `b` are known. + pub fn equate(&mut self, a: ty::TyVid, b: ty::TyVid) { + debug_assert!(self.probe(a).is_unknown()); + debug_assert!(self.probe(b).is_unknown()); + self.eq_relations().union(a, b); + self.sub_relations().union(a, b); + } + + /// Records that `a <: b`, depending on `dir`. + /// + /// Precondition: neither `a` nor `b` are known. + pub fn sub(&mut self, a: ty::TyVid, b: ty::TyVid) { + debug_assert!(self.probe(a).is_unknown()); + debug_assert!(self.probe(b).is_unknown()); + self.sub_relations().union(a, b); + } + + /// Instantiates `vid` with the type `ty`. + /// + /// Precondition: `vid` must not have been previously instantiated. + pub fn instantiate(&mut self, vid: ty::TyVid, ty: Ty<'tcx>) { + let vid = self.root_var(vid); + debug_assert!(self.probe(vid).is_unknown()); + debug_assert!( + self.eq_relations().probe_value(vid).is_unknown(), + "instantiating type variable `{:?}` twice: new-value = {:?}, old-value={:?}", + vid, + ty, + self.eq_relations().probe_value(vid) + ); + self.eq_relations().union_value(vid, TypeVariableValue::Known { value: ty }); + + // Hack: we only need this so that `types_escaping_snapshot` + // can see what has been unified; see the Delegate impl for + // more details. + self.undo_log.push(Instantiate); + } + + /// Creates a new type variable. + /// + /// - `diverging`: indicates if this is a "diverging" type + /// variable, e.g., one created as the type of a `return` + /// expression. The code in this module doesn't care if a + /// variable is diverging, but the main Rust type-checker will + /// sometimes "unify" such variables with the `!` or `()` types. + /// - `origin`: indicates *why* the type variable was created. + /// The code in this module doesn't care, but it can be useful + /// for improving error messages. + pub fn new_var( + &mut self, + universe: ty::UniverseIndex, + origin: TypeVariableOrigin, + ) -> ty::TyVid { + let eq_key = self.eq_relations().new_key(TypeVariableValue::Unknown { universe }); + + let sub_key = self.sub_relations().new_key(()); + assert_eq!(eq_key.vid, sub_key); + + let index = self.values().push(TypeVariableData { origin }); + assert_eq!(eq_key.vid.as_u32(), index as u32); + + debug!("new_var(index={:?}, universe={:?}, origin={:?})", eq_key.vid, universe, origin); + + eq_key.vid + } + + /// Returns the number of type variables created thus far. + pub fn num_vars(&self) -> usize { + self.storage.values.len() + } + + /// Returns the "root" variable of `vid` in the `eq_relations` + /// equivalence table. All type variables that have been equated + /// will yield the same root variable (per the union-find + /// algorithm), so `root_var(a) == root_var(b)` implies that `a == + /// b` (transitively). + pub fn root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { + self.eq_relations().find(vid).vid + } + + /// Returns the "root" variable of `vid` in the `sub_relations` + /// equivalence table. All type variables that have been are + /// related via equality or subtyping will yield the same root + /// variable (per the union-find algorithm), so `sub_root_var(a) + /// == sub_root_var(b)` implies that: + /// ```text + /// exists X. (a <: X || X <: a) && (b <: X || X <: b) + /// ``` + pub fn sub_root_var(&mut self, vid: ty::TyVid) -> ty::TyVid { + self.sub_relations().find(vid) + } + + /// Returns `true` if `a` and `b` have same "sub-root" (i.e., exists some + /// type X such that `forall i in {a, b}. (i <: X || X <: i)`. + pub fn sub_unified(&mut self, a: ty::TyVid, b: ty::TyVid) -> bool { + self.sub_root_var(a) == self.sub_root_var(b) + } + + /// Retrieves the type to which `vid` has been instantiated, if + /// any. + pub fn probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> { + self.inlined_probe(vid) + } + + /// An always-inlined variant of `probe`, for very hot call sites. + #[inline(always)] + pub fn inlined_probe(&mut self, vid: ty::TyVid) -> TypeVariableValue<'tcx> { + self.eq_relations().inlined_probe_value(vid) + } + + /// If `t` is a type-inference variable, and it has been + /// instantiated, then return the with which it was + /// instantiated. Otherwise, returns `t`. + pub fn replace_if_possible(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + match *t.kind() { + ty::Infer(ty::TyVar(v)) => match self.probe(v) { + TypeVariableValue::Unknown { .. } => t, + TypeVariableValue::Known { value } => value, + }, + _ => t, + } + } + + #[inline] + fn values( + &mut self, + ) -> sv::SnapshotVec<Delegate, &mut Vec<TypeVariableData>, &mut InferCtxtUndoLogs<'tcx>> { + self.storage.values.with_log(self.undo_log) + } + + #[inline] + fn eq_relations(&mut self) -> super::UnificationTable<'_, 'tcx, TyVidEqKey<'tcx>> { + self.storage.eq_relations.with_log(self.undo_log) + } + + #[inline] + fn sub_relations(&mut self) -> super::UnificationTable<'_, 'tcx, ty::TyVid> { + self.storage.sub_relations.with_log(self.undo_log) + } + + /// Returns a range of the type variables created during the snapshot. + pub fn vars_since_snapshot( + &mut self, + value_count: usize, + ) -> (Range<TyVid>, Vec<TypeVariableOrigin>) { + let range = TyVid::from_usize(value_count)..TyVid::from_usize(self.num_vars()); + ( + range.start..range.end, + (range.start.as_usize()..range.end.as_usize()) + .map(|index| self.storage.values.get(index).origin) + .collect(), + ) + } + + /// Returns indices of all variables that are not yet + /// instantiated. + pub fn unsolved_variables(&mut self) -> Vec<ty::TyVid> { + (0..self.storage.values.len()) + .filter_map(|i| { + let vid = ty::TyVid::from_usize(i); + match self.probe(vid) { + TypeVariableValue::Unknown { .. } => Some(vid), + TypeVariableValue::Known { .. } => None, + } + }) + .collect() + } +} + +impl sv::SnapshotVecDelegate for Delegate { + type Value = TypeVariableData; + type Undo = Instantiate; + + fn reverse(_values: &mut Vec<TypeVariableData>, _action: Instantiate) { + // We don't actually have to *do* anything to reverse an + // instantiation; the value for a variable is stored in the + // `eq_relations` and hence its rollback code will handle + // it. In fact, we could *almost* just remove the + // `SnapshotVec` entirely, except that we would have to + // reproduce *some* of its logic, since we want to know which + // type variables have been instantiated since the snapshot + // was started, so we can implement `types_escaping_snapshot`. + // + // (If we extended the `UnificationTable` to let us see which + // values have been unified and so forth, that might also + // suffice.) + } +} + +/////////////////////////////////////////////////////////////////////////// + +/// These structs (a newtyped TyVid) are used as the unification key +/// for the `eq_relations`; they carry a `TypeVariableValue` along +/// with them. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub(crate) struct TyVidEqKey<'tcx> { + vid: ty::TyVid, + + // in the table, we map each ty-vid to one of these: + phantom: PhantomData<TypeVariableValue<'tcx>>, +} + +impl<'tcx> From<ty::TyVid> for TyVidEqKey<'tcx> { + #[inline] // make this function eligible for inlining - it is quite hot. + fn from(vid: ty::TyVid) -> Self { + TyVidEqKey { vid, phantom: PhantomData } + } +} + +impl<'tcx> ut::UnifyKey for TyVidEqKey<'tcx> { + type Value = TypeVariableValue<'tcx>; + #[inline(always)] + fn index(&self) -> u32 { + self.vid.as_u32() + } + #[inline] + fn from_index(i: u32) -> Self { + TyVidEqKey::from(ty::TyVid::from_u32(i)) + } + fn tag() -> &'static str { + "TyVidEqKey" + } +} + +impl<'tcx> ut::UnifyValue for TypeVariableValue<'tcx> { + type Error = ut::NoError; + + fn unify_values(value1: &Self, value2: &Self) -> Result<Self, ut::NoError> { + match (value1, value2) { + // We never equate two type variables, both of which + // have known types. Instead, we recursively equate + // those types. + (&TypeVariableValue::Known { .. }, &TypeVariableValue::Known { .. }) => { + bug!("equating two type variables, both of which have known types") + } + + // If one side is known, prefer that one. + (&TypeVariableValue::Known { .. }, &TypeVariableValue::Unknown { .. }) => Ok(*value1), + (&TypeVariableValue::Unknown { .. }, &TypeVariableValue::Known { .. }) => Ok(*value2), + + // If both sides are *unknown*, it hardly matters, does it? + ( + &TypeVariableValue::Unknown { universe: universe1 }, + &TypeVariableValue::Unknown { universe: universe2 }, + ) => { + // If we unify two unbound variables, ?T and ?U, then whatever + // value they wind up taking (which must be the same value) must + // be nameable by both universes. Therefore, the resulting + // universe is the minimum of the two universes, because that is + // the one which contains the fewest names in scope. + let universe = cmp::min(universe1, universe2); + Ok(TypeVariableValue::Unknown { universe }) + } + } + } +} diff --git a/compiler/rustc_infer/src/infer/undo_log.rs b/compiler/rustc_infer/src/infer/undo_log.rs new file mode 100644 index 000000000..74a26ebc3 --- /dev/null +++ b/compiler/rustc_infer/src/infer/undo_log.rs @@ -0,0 +1,220 @@ +use std::marker::PhantomData; + +use rustc_data_structures::snapshot_vec as sv; +use rustc_data_structures::undo_log::{Rollback, UndoLogs}; +use rustc_data_structures::unify as ut; +use rustc_middle::infer::unify_key::RegionVidKey; +use rustc_middle::ty::{self, OpaqueHiddenType, OpaqueTypeKey}; + +use crate::{ + infer::{region_constraints, type_variable, InferCtxtInner}, + traits, +}; + +pub struct Snapshot<'tcx> { + pub(crate) undo_len: usize, + _marker: PhantomData<&'tcx ()>, +} + +/// Records the "undo" data for a single operation that affects some form of inference variable. +#[derive(Clone)] +pub(crate) enum UndoLog<'tcx> { + OpaqueTypes(OpaqueTypeKey<'tcx>, Option<OpaqueHiddenType<'tcx>>), + TypeVariables(type_variable::UndoLog<'tcx>), + ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>), + IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>), + FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>), + RegionConstraintCollector(region_constraints::UndoLog<'tcx>), + RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>), + ProjectionCache(traits::UndoLog<'tcx>), + PushRegionObligation, +} + +macro_rules! impl_from { + ($($ctor: ident ($ty: ty),)*) => { + $( + impl<'tcx> From<$ty> for UndoLog<'tcx> { + fn from(x: $ty) -> Self { + UndoLog::$ctor(x.into()) + } + } + )* + } +} + +// Upcast from a single kind of "undoable action" to the general enum +impl_from! { + RegionConstraintCollector(region_constraints::UndoLog<'tcx>), + TypeVariables(type_variable::UndoLog<'tcx>), + + TypeVariables(sv::UndoLog<ut::Delegate<type_variable::TyVidEqKey<'tcx>>>), + TypeVariables(sv::UndoLog<ut::Delegate<ty::TyVid>>), + TypeVariables(sv::UndoLog<type_variable::Delegate>), + TypeVariables(type_variable::Instantiate), + + IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>), + + FloatUnificationTable(sv::UndoLog<ut::Delegate<ty::FloatVid>>), + + ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>), + + RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>), + ProjectionCache(traits::UndoLog<'tcx>), +} + +/// The Rollback trait defines how to rollback a particular action. +impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + match undo { + UndoLog::OpaqueTypes(key, idx) => self.opaque_type_storage.remove(key, idx), + UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo), + UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo), + UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo), + UndoLog::FloatUnificationTable(undo) => self.float_unification_storage.reverse(undo), + UndoLog::RegionConstraintCollector(undo) => { + self.region_constraint_storage.as_mut().unwrap().reverse(undo) + } + UndoLog::RegionUnificationTable(undo) => { + self.region_constraint_storage.as_mut().unwrap().unification_table.reverse(undo) + } + UndoLog::ProjectionCache(undo) => self.projection_cache.reverse(undo), + UndoLog::PushRegionObligation => { + self.region_obligations.pop(); + } + } + } +} + +/// The combined undo log for all the various unification tables. For each change to the storage +/// for any kind of inference variable, we record an UndoLog entry in the vector here. +#[derive(Clone)] +pub(crate) struct InferCtxtUndoLogs<'tcx> { + logs: Vec<UndoLog<'tcx>>, + num_open_snapshots: usize, +} + +impl Default for InferCtxtUndoLogs<'_> { + fn default() -> Self { + Self { logs: Default::default(), num_open_snapshots: Default::default() } + } +} + +/// The UndoLogs trait defines how we undo a particular kind of action (of type T). We can undo any +/// action that is convertable into an UndoLog (per the From impls above). +impl<'tcx, T> UndoLogs<T> for InferCtxtUndoLogs<'tcx> +where + UndoLog<'tcx>: From<T>, +{ + #[inline] + fn num_open_snapshots(&self) -> usize { + self.num_open_snapshots + } + + #[inline] + fn push(&mut self, undo: T) { + if self.in_snapshot() { + self.logs.push(undo.into()) + } + } + + fn clear(&mut self) { + self.logs.clear(); + self.num_open_snapshots = 0; + } + + fn extend<J>(&mut self, undos: J) + where + Self: Sized, + J: IntoIterator<Item = T>, + { + if self.in_snapshot() { + self.logs.extend(undos.into_iter().map(UndoLog::from)) + } + } +} + +impl<'tcx> InferCtxtInner<'tcx> { + pub fn rollback_to(&mut self, snapshot: Snapshot<'tcx>) { + debug!("rollback_to({})", snapshot.undo_len); + self.undo_log.assert_open_snapshot(&snapshot); + + while self.undo_log.logs.len() > snapshot.undo_len { + let undo = self.undo_log.logs.pop().unwrap(); + self.reverse(undo); + } + + if self.undo_log.num_open_snapshots == 1 { + // The root snapshot. It's safe to clear the undo log because + // there's no snapshot further out that we might need to roll back + // to. + assert!(snapshot.undo_len == 0); + self.undo_log.logs.clear(); + } + + self.undo_log.num_open_snapshots -= 1; + } + + pub fn commit(&mut self, snapshot: Snapshot<'tcx>) { + debug!("commit({})", snapshot.undo_len); + + if self.undo_log.num_open_snapshots == 1 { + // The root snapshot. It's safe to clear the undo log because + // there's no snapshot further out that we might need to roll back + // to. + assert!(snapshot.undo_len == 0); + self.undo_log.logs.clear(); + } + + self.undo_log.num_open_snapshots -= 1; + } +} + +impl<'tcx> InferCtxtUndoLogs<'tcx> { + pub fn start_snapshot(&mut self) -> Snapshot<'tcx> { + self.num_open_snapshots += 1; + Snapshot { undo_len: self.logs.len(), _marker: PhantomData } + } + + pub(crate) fn region_constraints_in_snapshot( + &self, + s: &Snapshot<'tcx>, + ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone { + self.logs[s.undo_len..].iter().filter_map(|log| match log { + UndoLog::RegionConstraintCollector(log) => Some(log), + _ => None, + }) + } + + pub(crate) fn opaque_types_in_snapshot(&self, s: &Snapshot<'tcx>) -> bool { + self.logs[s.undo_len..].iter().any(|log| matches!(log, UndoLog::OpaqueTypes(..))) + } + + pub(crate) fn region_constraints( + &self, + ) -> impl Iterator<Item = &'_ region_constraints::UndoLog<'tcx>> + Clone { + self.logs.iter().filter_map(|log| match log { + UndoLog::RegionConstraintCollector(log) => Some(log), + _ => None, + }) + } + + fn assert_open_snapshot(&self, snapshot: &Snapshot<'tcx>) { + // Failures here may indicate a failure to follow a stack discipline. + assert!(self.logs.len() >= snapshot.undo_len); + assert!(self.num_open_snapshots > 0); + } +} + +impl<'tcx> std::ops::Index<usize> for InferCtxtUndoLogs<'tcx> { + type Output = UndoLog<'tcx>; + + fn index(&self, key: usize) -> &Self::Output { + &self.logs[key] + } +} + +impl<'tcx> std::ops::IndexMut<usize> for InferCtxtUndoLogs<'tcx> { + fn index_mut(&mut self, key: usize) -> &mut Self::Output { + &mut self.logs[key] + } +} |