diff options
Diffstat (limited to 'compiler/rustc_infer')
50 files changed, 18480 insertions, 0 deletions
diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml new file mode 100644 index 00000000000..15649bb678e --- /dev/null +++ b/compiler/rustc_infer/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "rustc_infer" +version = "0.0.0" +edition = "2018" + +[lib] +doctest = false + +[dependencies] +tracing = "0.1" +rustc_middle = { path = "../rustc_middle" } +rustc_data_structures = { path = "../rustc_data_structures" } +rustc_errors = { path = "../rustc_errors" } +rustc_hir = { path = "../rustc_hir" } +rustc_index = { path = "../rustc_index" } +rustc_macros = { path = "../rustc_macros" } +rustc_session = { path = "../rustc_session" } +rustc_serialize = { path = "../rustc_serialize" } +rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } +smallvec = { version = "1.6.1", features = ["union", "may_dangle"] } diff --git a/compiler/rustc_infer/src/infer/at.rs b/compiler/rustc_infer/src/infer/at.rs new file mode 100644 index 00000000000..11ee8fb17ad --- /dev/null +++ b/compiler/rustc_infer/src/infer/at.rs @@ -0,0 +1,334 @@ +//! 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: +//! +//! 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`: +//! +//! 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; + +pub struct At<'a, 'tcx> { + pub infcx: &'a InferCtxt<'a, 'tcx>, + pub cause: &'a ObligationCause<'tcx>, + pub param_env: ty::ParamEnv<'tcx>, +} + +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 } + } +} + +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> { + /// 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. + 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. + pub fn sup<T>(self, expected: T, actual: T) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + self.sub_exp(false, actual, expected) + } + + /// Makes `expected <: actual`. + pub fn sub<T>(self, expected: T, actual: T) -> InferResult<'tcx, ()> + where + T: ToTrace<'tcx>, + { + self.sub_exp(true, expected, actual) + } + + /// Makes `expected <: actual`. + 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`. + 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". + 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. + 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). + pub fn sub<T>(self, a: T, b: T) -> InferResult<'tcx, ()> + where + T: Relate<'tcx>, + { + debug!("sub({:?} <: {:?})", a, b); + let Trace { at, trace, a_is_expected } = self; + at.infcx.commit_if_ok(|_| { + let mut fields = at.infcx.combine_fields(trace, at.param_env); + 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()`. + pub fn eq<T>(self, a: T, b: T) -> InferResult<'tcx, ()> + where + T: Relate<'tcx>, + { + debug!("eq({:?} == {:?})", a, b); + let Trace { at, trace, a_is_expected } = self; + at.infcx.commit_if_ok(|_| { + let mut fields = at.infcx.combine_fields(trace, at.param_env); + fields + .equate(a_is_expected) + .relate(a, b) + .map(move |_| InferOk { value: (), obligations: fields.obligations }) + }) + } + + pub fn lub<T>(self, a: T, b: T) -> InferResult<'tcx, T> + where + T: Relate<'tcx>, + { + debug!("lub({:?} \\/ {:?})", a, b); + let Trace { at, trace, a_is_expected } = self; + at.infcx.commit_if_ok(|_| { + let mut fields = at.infcx.combine_fields(trace, at.param_env); + fields + .lub(a_is_expected) + .relate(a, b) + .map(move |t| InferOk { value: t, obligations: fields.obligations }) + }) + } + + pub fn glb<T>(self, a: T, b: T) -> InferResult<'tcx, T> + where + T: Relate<'tcx>, + { + debug!("glb({:?} /\\ {:?})", a, b); + let Trace { at, trace, a_is_expected } = self; + at.infcx.commit_if_ok(|_| { + let mut fields = at.infcx.combine_fields(trace, at.param_env); + fields + .glb(a_is_expected) + .relate(a, b) + .map(move |t| InferOk { value: t, obligations: fields.obligations }) + }) + } +} + +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: Types(ExpectedFound::new(a_is_expected, a, b)) } + } +} + +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 &'tcx 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: Consts(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: Types(ExpectedFound::new(a_is_expected, a_ty, b_ty)), + } + } +} 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 00000000000..448dd662348 --- /dev/null +++ b/compiler/rustc_infer/src/infer/canonical/canonicalizer.rs @@ -0,0 +1,649 @@ +//! 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}; +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) + } + + /// 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 + /// + /// 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 CanonicalizeRegionMode { + fn canonicalize_free_region( + &self, + canonicalizer: &mut Canonicalizer<'_, 'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx>; + + fn any(&self) -> bool; +} + +struct CanonicalizeQueryResponse; + +impl CanonicalizeRegionMode for CanonicalizeQueryResponse { + fn canonicalize_free_region( + &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 + } +} + +struct CanonicalizeUserTypeAnnotation; + +impl CanonicalizeRegionMode for CanonicalizeUserTypeAnnotation { + fn canonicalize_free_region( + &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 + } +} + +struct CanonicalizeAllFreeRegions; + +impl CanonicalizeRegionMode for CanonicalizeAllFreeRegions { + fn canonicalize_free_region( + &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 + } +} + +struct CanonicalizeFreeRegionsOtherThanStatic; + +impl CanonicalizeRegionMode for CanonicalizeFreeRegionsOtherThanStatic { + fn canonicalize_free_region( + &self, + canonicalizer: &mut Canonicalizer<'_, 'tcx>, + r: ty::Region<'tcx>, + ) -> ty::Region<'tcx> { + if let ty::ReStatic = r { + r + } else { + canonicalizer.canonical_var_for_region_in_root_universe(r) + } + } + + fn any(&self) -> bool { + true + } +} + +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_region_mode: &'cx dyn CanonicalizeRegionMode, + 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_region_mode.canonicalize_free_region(self, r) + } + + ty::ReStatic + | ty::ReEarlyBound(..) + | ty::ReFree(_) + | ty::ReEmpty(_) + | ty::RePlaceholder(..) + | ty::ReErased => self.canonicalize_region_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) => { + // 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: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + match ct.val { + 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) => { + // FIXME: perf problem described in #55921. + ui = ty::UniverseIndex::ROOT; + return self.canonicalize_const_var( + CanonicalVarInfo { kind: CanonicalVarKind::Const(ui) }, + 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, + ); + } + _ => {} + } + + 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 CanonicalizeRegionMode, + 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_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, so it should live in the global arena. + debug_assert!(!out_value.needs_infer()); + + let canonical_variables = tcx.intern_canonical_var_infos(&canonicalizer.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; + + // 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) + }) + } + } + + /// 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: &'tcx ty::Const<'tcx>, + ) -> &'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::Const { + val: 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 00000000000..0c26639e9b0 --- /dev/null +++ b/compiler/rustc_infer/src/infer/canonical/mod.rs @@ -0,0 +1,163 @@ +//! **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) => self + .next_const_var_in_universe( + self.next_ty_var_in_universe( + TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }, + universe_map(ui), + ), + ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span }, + universe_map(ui), + ) + .into(), + + CanonicalVarKind::PlaceholderConst(ty::PlaceholderConst { universe, name }) => { + let universe_mapped = universe_map(universe); + let placeholder_mapped = ty::PlaceholderConst { universe: universe_mapped, name }; + self.tcx + .mk_const(ty::Const { + val: ty::ConstKind::Placeholder(placeholder_mapped), + ty: name.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 00000000000..c3c28d70081 --- /dev/null +++ b/compiler/rustc_infer/src/infer/canonical/query_response.rs @@ -0,0 +1,692 @@ +//! 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 canonicaliation 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::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 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. + 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!("make_canonicalized_query_response: 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! + value: answer, + }) + } + + /// Helper for `make_canonicalized_query_response` that does + /// everything up until the final canonicalization. + 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; + + debug!( + "make_query_response(\ + inference_vars={:?}, \ + answer={:?})", + inference_vars, answer, + ); + + // Select everything, returning errors. + let true_errors = fulfill_cx.select_where_possible(self).err().unwrap_or_else(Vec::new); + 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).err().unwrap_or_else(Vec::new); + 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 }; + + Ok(QueryResponse { + var_values: inference_vars, + region_constraints, + certainty, + value: answer, + }) + } + + /// 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 result_subst = + self.query_response_substitution_guess(cause, original_values, query_response); + + // Compute `QueryOutlivesConstraint` values that unify each of + // the original values `v_o` that was canonicalized into a + // variable... + let mut obligations = vec![]; + + 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(ty::ReErased), + GenericArgKind::Lifetime(ty::ReErased), + ) => { + // 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 result_subst = + self.query_response_substitution_guess(cause, original_values, query_response); + + let obligations = self + .unify_query_response_substitution_guess( + cause, + param_env, + original_values, + &result_subst, + query_response, + )? + .into_obligations(); + + Ok(InferOk { value: result_subst, obligations }) + } + + /// 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>, + original_values: &OriginalQueryValues<'tcx>, + query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>, + ) -> 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::RegionKind::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::Const { val: ty::ConstKind::Bound(debrujin, b), .. } = result_value { + // ...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(), + }; + + result_subst + } + + /// 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); + 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 {:?}", constraint); + } + }; + let predicate = predicate.rebind(atom).to_predicate(self.tcx); + + Obligation::new(cause.clone(), 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(ty::ReErased), + GenericArgKind::Lifetime(ty::ReErased), + ) => { + // 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 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::PredicateKind::RegionOutlives(ty::OutlivesPredicate(sup, sub)) + .to_predicate(self.infcx.tcx), + recursion_depth: 0, + }); + } + + fn const_equate(&mut self, _a: &'tcx Const<'tcx>, _b: &'tcx Const<'tcx>) { + span_bug!( + self.cause.span(self.infcx.tcx), + "lazy_normalization_consts: unreachable `const_equate`" + ); + } + + fn normalization() -> NormalizationStrategy { + NormalizationStrategy::Eager + } + + fn forbid_inference_vars() -> bool { + true + } +} 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 00000000000..553a11d4393 --- /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::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 fld_r = |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), + }; + + let fld_t = |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), + }; + + let fld_c = |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(value, fld_r, fld_t, fld_c) + } +} diff --git a/compiler/rustc_infer/src/infer/combine.rs b/compiler/rustc_infer/src/infer/combine.rs new file mode 100644 index 00000000000..3a11b5a2144 --- /dev/null +++ b/compiler/rustc_infer/src/infer/combine.rs @@ -0,0 +1,992 @@ +/////////////////////////////////////////////////////////////////////////// +// # Type combining +// +// There are four type combiners: equate, sub, lub, and glb. Each +// implements the trait `Combine` and contains methods for combining +// two instances of various things and yielding a new instance. These +// combiner methods always yield a `Result<T>`. There is a lot of +// common code for these operations, implemented as default methods on +// the `Combine` trait. +// +// Each operation may have side-effects on the inference context, +// though these can be unrolled using snapshots. On success, the +// LUB/GLB operations return the appropriate bound. The Eq and Sub +// operations generally return the first operand. +// +// ## Contravariance +// +// When you are relating two things which have a contravariant +// relationship, you should use `contratys()` or `contraregions()`, +// rather than inversing the order of arguments! This is necessary +// because the order of arguments is not relevant for LUB and GLB. It +// is also useful to track which value is the "expected" value in +// terms of error reporting. + +use super::equate::Equate; +use super::glb::Glb; +use super::lub::Lub; +use super::sub::Sub; +use super::type_variable::TypeVariableValue; +use super::unify_key::replace_if_possible; +use super::unify_key::{ConstVarValue, ConstVariableValue}; +use super::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; +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::traits::ObligationCause; +use rustc_middle::ty::error::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, TypeFoldable}; +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>, +} + +#[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: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> + where + R: ConstEquateRelation<'tcx>, + { + debug!("{}.consts({:?}, {:?})", relation.tag(), a, b); + if a == b { + return Ok(a); + } + + let a = replace_if_possible(&mut self.inner.borrow_mut().const_unification_table(), a); + let b = replace_if_possible(&mut self.inner.borrow_mut().const_unification_table(), b); + + let a_is_expected = relation.a_is_expected(); + + match (a.val, b.val) { + ( + 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: + /// + /// ```rust + /// #![feature(const_generics)] + /// + /// 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: &'tcx ty::Const<'tcx>, + vid_is_expected: bool, + ) -> RelateResult<'tcx, &'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. + 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()); + + debug!("instantiate(a_ty={:?} dir={:?} b_vid={:?})", a_ty, dir, b_vid); + + // 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!( + "instantiate(a_ty={:?}, dir={:?}, b_vid={:?}, generalized b_ty={:?})", + a_ty, dir, b_vid, 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::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" + fn generalize( + &self, + ty: Ty<'tcx>, + for_vid: ty::TyVid, + dir: RelationDir, + ) -> RelateResult<'tcx, Generalization<'tcx>> { + debug!("generalize(ty={:?}, for_vid={:?}, dir={:?}", ty, for_vid, dir); + // 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, + }; + + debug!("generalize: ambient_variance = {:?}", 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, + }; + + debug!("generalize: for_universe = {:?}", for_universe); + debug!("generalize: 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!("generalize: failure {:?}", e); + return Err(e); + } + }; + let needs_wf = generalize.needs_wf; + debug!("generalize: success {{ {:?}, {:?} }}", ty, needs_wf); + Ok(Generalization { ty, needs_wf }) + } + + pub fn add_const_equate_obligation( + &mut self, + a_is_expected: bool, + a: &'tcx ty::Const<'tcx>, + b: &'tcx 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, + 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 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, None, a_subst, b_subst) + } else { + let opt_variances = self.tcx().variances_of(item_def_id); + relate::relate_substs(self, Some(&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, false, 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(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: &'tcx ty::Const<'tcx>, + c2: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + assert_eq!(c, c2); // we are abusing TypeRelation here; both LHS and RHS ought to be == + + match c.val { + 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::Const { + ty: c.ty, + val: 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: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>); +} + +pub trait RelateResultCompare<'tcx, T> { + fn compare<F>(&self, t: T, f: F) -> RelateResult<'tcx, T> + where + F: FnOnce() -> TypeError<'tcx>; +} + +impl<'tcx, T: Clone + PartialEq> RelateResultCompare<'tcx, T> for RelateResult<'tcx, T> { + fn compare<F>(&self, t: T, f: F) -> RelateResult<'tcx, T> + where + F: FnOnce() -> TypeError<'tcx>, + { + self.clone().and_then(|s| if s == t { self.clone() } else { Err(f()) }) + } +} + +pub fn const_unification_error<'tcx>( + a_is_expected: bool, + (a, b): (&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>), +) -> TypeError<'tcx> { + TypeError::ConstMismatch(ty::relate::expected_found_bool(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(ty::relate::expected_found_bool(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(ty::relate::expected_found_bool(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 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())?)) + } + + 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, + false, + 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)) + } + } + + fn consts( + &mut self, + c: &'tcx ty::Const<'tcx>, + _c: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + debug_assert_eq!(c, _c); + debug!("ConstInferUnifier: c={:?}", c); + + match c.val { + ty::ConstKind::Infer(InferConst::Var(vid)) => { + let mut inner = self.infcx.inner.borrow_mut(); + let variable_table = &mut inner.const_unification_table(); + + // 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 variable_table.unioned(self.target_vid, vid) { + return Err(TypeError::CyclicConst(c)); + } + + let var_value = variable_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 = 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::Const { + ty: c.ty, + val: 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 00000000000..0c93271a1ae --- /dev/null +++ b/compiler/rustc_infer/src/infer/equate.rs @@ -0,0 +1,149 @@ +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 TypeRelation<'tcx> for Equate<'combine, 'infcx, '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, None, 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) + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + debug!("{}.tys({:?}, {:?})", self.tag(), a, b); + 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); + + debug!("{}.tys: replacements ({:?}, {:?})", self.tag(), a, 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)?; + } + + _ => { + 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 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: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'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: &'tcx ty::Const<'tcx>, b: &'tcx 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 00000000000..d5e1c061bf0 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -0,0 +1,2578 @@ +//! 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 basis of the system are the "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}; +use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::lang_items::LangItem; +use rustc_hir::{Item, ItemKind, Node}; +use rustc_middle::dep_graph::DepContext; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::{ + self, + subst::{GenericArgKind, Subst, SubstsRef}, + Region, Ty, TyCtxt, TypeFoldable, +}; +use rustc_span::{sym, BytePos, DesugaringKind, MultiSpan, 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: TyCtxt<'tcx>, + err: &mut DiagnosticBuilder<'_>, + 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); +} + +pub(super) fn note_and_explain_free_region( + tcx: TyCtxt<'tcx>, + err: &mut DiagnosticBuilder<'_>, + prefix: &str, + region: ty::Region<'tcx>, + suffix: &str, +) { + let (description, span) = msg_span_from_free_region(tcx, region, None); + + emit_msg_span(err, prefix, description, span, suffix); +} + +fn msg_span_from_free_region( + tcx: TyCtxt<'tcx>, + region: ty::Region<'tcx>, + alt_span: Option<Span>, +) -> (String, Option<Span>) { + match *region { + ty::ReEarlyBound(_) | ty::ReFree(_) => { + msg_span_from_early_bound_and_free_regions(tcx, region) + } + 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: TyCtxt<'tcx>, + region: ty::Region<'tcx>, +) -> (String, Option<Span>) { + let sm = tcx.sess.source_map(); + + let scope = region.free_region_binding_scope(tcx); + let node = tcx.hir().local_def_id_to_hir_id(scope.expect_local()); + let tag = match tcx.hir().find(node) { + Some(Node::Block(_) | Node::Expr(_)) => "body", + Some(Node::Item(it)) => item_scope_tag(&it), + Some(Node::TraitItem(it)) => trait_item_scope_tag(&it), + Some(Node::ImplItem(it)) => impl_item_scope_tag(&it), + Some(Node::ForeignItem(it)) => foreign_item_scope_tag(&it), + _ => unreachable!(), + }; + let (prefix, span) = match *region { + ty::ReEarlyBound(ref br) => { + let mut sp = sm.guess_head_span(tcx.hir().span(node)); + if let Some(param) = + tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(br.name)) + { + sp = param.span; + } + (format!("the lifetime `{}` as defined on", br.name), sp) + } + ty::ReFree(ty::FreeRegion { + bound_region: ty::BoundRegionKind::BrNamed(_, name), .. + }) => { + let mut sp = sm.guess_head_span(tcx.hir().span(node)); + if let Some(param) = + tcx.hir().get_generics(scope).and_then(|generics| generics.get_named(name)) + { + sp = param.span; + } + (format!("the lifetime `{}` as defined on", name), sp) + } + ty::ReFree(ref fr) => match fr.bound_region { + ty::BrAnon(idx) => { + if let Some((ty, _)) = find_anon_type(tcx, region, &fr.bound_region) { + ("the anonymous lifetime defined on".to_string(), ty.span) + } else { + ( + format!("the anonymous lifetime #{} defined on", idx + 1), + tcx.hir().span(node), + ) + } + } + _ => ( + format!("the lifetime `{}` as defined on", region), + sm.guess_head_span(tcx.hir().span(node)), + ), + }, + _ => bug!(), + }; + let (msg, opt_span) = explain_span(tcx, tag, span); + (format!("{} {}", prefix, msg), opt_span) +} + +fn emit_msg_span( + err: &mut DiagnosticBuilder<'_>, + 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 item_scope_tag(item: &hir::Item<'_>) -> &'static str { + match item.kind { + hir::ItemKind::Impl { .. } => "impl", + hir::ItemKind::Struct(..) => "struct", + hir::ItemKind::Union(..) => "union", + hir::ItemKind::Enum(..) => "enum", + hir::ItemKind::Trait(..) => "trait", + hir::ItemKind::Fn(..) => "function body", + _ => "item", + } +} + +fn trait_item_scope_tag(item: &hir::TraitItem<'_>) -> &'static str { + match item.kind { + hir::TraitItemKind::Fn(..) => "method body", + hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => "associated item", + } +} + +fn impl_item_scope_tag(item: &hir::ImplItem<'_>) -> &'static str { + match item.kind { + hir::ImplItemKind::Fn(..) => "method body", + hir::ImplItemKind::Const(..) | hir::ImplItemKind::TyAlias(..) => "associated item", + } +} + +fn foreign_item_scope_tag(item: &hir::ForeignItem<'_>) -> &'static str { + match item.kind { + hir::ForeignItemKind::Fn(..) => "method body", + hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => "associated item", + } +} + +fn explain_span(tcx: TyCtxt<'tcx>, heading: &str, span: Span) -> (String, Option<Span>) { + let lo = tcx.sess.source_map().lookup_char_pos(span.lo()); + (format!("the {} at {}:{}", heading, lo.line, lo.col.to_usize() + 1), Some(span)) +} + +pub fn unexpected_hidden_region_diagnostic( + tcx: TyCtxt<'tcx>, + span: Span, + hidden_ty: Ty<'tcx>, + hidden_region: ty::Region<'tcx>, +) -> DiagnosticBuilder<'tcx> { + let mut err = struct_span_err!( + tcx.sess, + span, + E0700, + "hidden type for `impl Trait` 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(ErrorReported)` in any case, so we wouldn't be here at all. + note_and_explain_free_region( + tcx, + &mut err, + &format!("hidden type `{}` captures ", hidden_ty), + hidden_region, + "", + ); + } + _ => { + // 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, errors: &Vec<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( + 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(); + } + + RegionResolutionError::MemberConstraintFailure { + hidden_ty, + member_region, + span, + } => { + let hidden_ty = self.resolve_vars_if_possible(hidden_ty); + unexpected_hidden_region_diagnostic( + self.tcx, + span, + hidden_ty, + member_region, + ) + .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(..) + | RegionResolutionError::MemberConstraintFailure { .. } => 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(), + RegionResolutionError::MemberConstraintFailure { span, .. } => span, + }); + errors + } + + /// Adds a note if the types come from similarly named crates + fn check_and_note_conflicting_crates( + &self, + err: &mut DiagnosticBuilder<'_>, + 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: &'tcx 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 DiagnosticBuilder<'_>, 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 DiagnosticBuilder<'tcx>, + cause: &ObligationCause<'tcx>, + exp_found: Option<ty::error::ExpectedFound<Ty<'tcx>>>, + ) { + match cause.code { + ObligationCauseCode::Pattern { origin_expr: true, span: Some(span), root_ty } => { + let ty = self.resolve_vars_if_possible(root_ty); + if ty.is_suggestable() { + // don't show type `_` + err.span_label(span, format!("this expression has type `{}`", ty)); + } + if let Some(ty::error::ExpectedFound { found, .. }) = exp_found { + if ty.is_box() && ty.boxed_ty() == found { + if 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 { + semi_span, + source, + ref prior_arms, + last_ty, + scrut_hir_id, + opt_suggest_box_span, + arm_span, + scrut_span, + .. + }) => match source { + hir::MatchSource::IfLetDesugar { .. } => { + let msg = "`if let` arms have incompatible types"; + err.span_label(cause.span, msg); + if let Some(ret_sp) = opt_suggest_box_span { + self.suggest_boxing_for_return_impl_trait( + err, + ret_sp, + prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s), + ); + } + } + 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 `?`", + "".to_string(), + Applicability::MachineApplicable, + ); + } + _ => {} + } + } + } + _ => { + // `last_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, + _ => last_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); + if let Some((sp, boxed)) = semi_span { + if let (StatementAsExpression::NeedsBoxing, [.., prior_arm]) = + (boxed, &prior_arms[..]) + { + err.multipart_suggestion( + "consider removing this semicolon and boxing the expressions", + vec![ + (prior_arm.shrink_to_lo(), "Box::new(".to_string()), + (prior_arm.shrink_to_hi(), ")".to_string()), + (arm_span.shrink_to_lo(), "Box::new(".to_string()), + (arm_span.shrink_to_hi(), ")".to_string()), + (sp, String::new()), + ], + Applicability::HasPlaceholders, + ); + } else if matches!(boxed, StatementAsExpression::NeedsBoxing) { + err.span_suggestion_short( + sp, + "consider removing this semicolon and boxing the expressions", + String::new(), + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion_short( + sp, + "consider removing this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + } + } + 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, + else_sp, + outer, + semicolon, + opt_suggest_box_span, + }) => { + err.span_label(then, "expected because of this"); + if let Some(sp) = outer { + err.span_label(sp, "`if` and `else` have incompatible types"); + } + if let Some((sp, boxed)) = semicolon { + if matches!(boxed, StatementAsExpression::NeedsBoxing) { + err.multipart_suggestion( + "consider removing this semicolon and boxing the expression", + vec![ + (then.shrink_to_lo(), "Box::new(".to_string()), + (then.shrink_to_hi(), ")".to_string()), + (else_sp.shrink_to_lo(), "Box::new(".to_string()), + (else_sp.shrink_to_hi(), ")".to_string()), + (sp, String::new()), + ], + Applicability::MachineApplicable, + ); + } else { + err.span_suggestion_short( + sp, + "consider removing this semicolon", + String::new(), + Applicability::MachineApplicable, + ); + } + } + if let Some(ret_sp) = opt_suggest_box_span { + self.suggest_boxing_for_return_impl_trait( + err, + ret_sp, + vec![then, else_sp].into_iter(), + ); + } + } + _ => (), + } + } + + fn suggest_boxing_for_return_impl_trait( + &self, + err: &mut DiagnosticBuilder<'tcx>, + 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| { + vec![ + (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: + /// + /// ```no_run + /// 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: ty::subst::SubstsRef<'tcx>, + other_path: String, + other_ty: Ty<'tcx>, + ) -> Option<()> { + 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(", "); + } + } + + /// For generic types with parameters with defaults, remove the parameters corresponding to + /// the defaults. This repeats a lot of the logic found in `ty::print::pretty`. + fn strip_generic_default_params( + &self, + def_id: DefId, + substs: ty::subst::SubstsRef<'tcx>, + ) -> SubstsRef<'tcx> { + let generics = self.tcx.generics_of(def_id); + let mut num_supplied_defaults = 0; + + let default_params = generics.params.iter().rev().filter_map(|param| match param.kind { + ty::GenericParamDefKind::Type { has_default: true, .. } => Some(param.def_id), + ty::GenericParamDefKind::Const { has_default: true } => Some(param.def_id), + _ => None, + }); + for (def_id, actual) in iter::zip(default_params, substs.iter().rev()) { + match actual.unpack() { + GenericArgKind::Const(c) => { + if self.tcx.const_param_default(def_id).subst(self.tcx, substs) != c { + break; + } + } + GenericArgKind::Type(ty) => { + if self.tcx.type_of(def_id).subst(self.tcx, substs) != ty { + break; + } + } + _ => break, + } + num_supplied_defaults += 1; + } + let len = generics.params.len(); + let mut generics = generics.clone(); + generics.params.truncate(len - num_supplied_defaults); + substs.truncate_to(self.tcx, &generics) + } + + /// 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 mut s = String::new(); + let (_, sig, reg) = ty::print::FmtPrinter::new(self.tcx, &mut s, 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. + 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 sub_no_defaults_1 = self.strip_generic_default_params(def1.did, sub1); + let sub_no_defaults_2 = self.strip_generic_default_params(def2.did, sub2); + let mut values = (DiagnosticStyledString::new(), DiagnosticStyledString::new()); + let path1 = self.tcx.def_path_str(def1.did); + let path2 = self.tcx.def_path_str(def2.did); + if def1.did == def2.did { + // 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're 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 + 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_normal("'_"); + values.1.push_normal("'_"); + } else { + values.0.push_highlighted(l1); + values.1.push_highlighted(l2); + } + 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're 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.types().zip(substs2.types()).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.fn_sig(*did1).subst(self.tcx, substs1); + let sig2 = self.tcx.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.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.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()), + ) + } + } + } + } + + pub fn note_type_err( + &self, + diag: &mut DiagnosticBuilder<'tcx>, + cause: &ObligationCause<'tcx>, + secondary_span: Option<(Span, String)>, + mut values: Option<ValuePairs<'tcx>>, + terr: &TypeError<'tcx>, + ) { + let span = cause.span(self.tcx); + debug!("note_type_err cause={:?} values={:?}, terr={:?}", cause, values, terr); + + // 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 DiagnosticBuilder<'_>) { + 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 DiagnosticBuilder<'_>, + 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::fold::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) = match values { + None => (None, Mismatch::Fixed("type"), false), + Some(values) => { + let (is_simple_error, exp_found) = match values { + ValuePairs::Types(exp_found) => { + let is_simple_err = + exp_found.expected.is_simple_text() && exp_found.found.is_simple_text(); + OpaqueTypesVisitor::visit_expected_found( + self.tcx, + exp_found.expected, + exp_found.found, + span, + ) + .report(diag); + + (is_simple_err, Mismatch::Variable(exp_found)) + } + ValuePairs::TraitRefs(_) => (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. + diag.cancel(); + return; + } + }; + (vals, exp_found, is_simple_error) + } + }; + + // Ignore msg for object safe coercion + // since E0038 message will be printed + match terr { + TypeError::ObjectUnsafeCoercion(_) => {} + _ => { + diag.span_label(span, terr.to_string()); + if let Some((sp, msg)) = secondary_span { + diag.span_label(sp, msg); + } + } + }; + 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 pos = self + .tcx + .sess + .source_map() + .lookup_char_pos(self.tcx.def_span(*def_id).lo()); + format!( + " (opaque type at <{}:{}:{}>)", + pos.file.name.prefer_local(), + 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 {:?}", exp_found, terr); + if let Some(exp_found) = exp_found { + 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 a 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 { + if let ty::Closure(def_id, _) = exp_found.expected.skip_binder().self_ty().kind() { + if let Some(def_id) = def_id.as_local() { + let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id); + let span = self.tcx.hir().span(hir_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); + } + + pub fn get_impl_future_output_ty(&self, ty: Ty<'tcx>) -> Option<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_items(future_trait) + .in_definition_order() + .next() + .unwrap() + .def_id; + + let bounds = self.tcx.explicit_item_bounds(*def_id); + + for (predicate, _) in bounds { + let predicate = predicate.subst(self.tcx, substs); + if let ty::PredicateKind::Projection(projection_predicate) = + predicate.kind().skip_binder() + { + if projection_predicate.projection_ty.item_def_id == item_def_id { + // We don't account for multiple `Future::Output = Ty` contraints. + return Some(projection_predicate.ty); + } + } + } + } + None + } + + /// A possible error is to forget to add `.await` when using futures: + /// + /// ``` + /// 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 DiagnosticBuilder<'tcx>, + ) { + debug!( + "suggest_await_on_expect_found: exp_span={:?}, expected_ty={:?}, found_ty={:?}", + exp_span, exp_found.expected, exp_found.found, + ); + + if let ObligationCauseCode::CompareImplMethodObligation { .. } = &cause.code { + return; + } + + match ( + self.get_impl_future_output_ty(exp_found.expected), + self.get_impl_future_output_ty(exp_found.found), + ) { + (Some(exp), Some(found)) if ty::TyS::same_type(exp, found) => match &cause.code { + ObligationCauseCode::IfExpression(box IfExpressionCause { then, .. }) => { + diag.multipart_suggestion( + "consider `await`ing on both `Future`s", + vec![ + (then.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 ty::TyS::same_type(exp_found.expected, ty) => { + let span = match cause.code { + // scrutinee's span + ObligationCauseCode::Pattern { span: Some(span), .. } => span, + _ => exp_span, + }; + diag.span_suggestion_verbose( + span.shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await".to_string(), + Applicability::MaybeIncorrect, + ); + } + (Some(ty), _) if ty::TyS::same_type(ty, exp_found.found) => { + let span = match cause.code { + // scrutinee's span + ObligationCauseCode::Pattern { span: Some(span), .. } => span, + _ => exp_span, + }; + diag.span_suggestion_verbose( + span.shrink_to_hi(), + "consider `await`ing on the `Future`", + ".await".to_string(), + Applicability::MaybeIncorrect, + ); + } + _ => {} + } + } + + fn suggest_accessing_field_where_appropriate( + &self, + cause: &ObligationCause<'tcx>, + exp_found: &ty::error::ExpectedFound<Ty<'tcx>>, + diag: &mut DiagnosticBuilder<'tcx>, + ) { + 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.ident.name, field.ty(self.tcx, expected_substs))) + .find(|(_, ty)| ty::TyS::same_type(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 DiagnosticBuilder<'tcx>, + ) { + 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 ty::TyS::same_type(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> { + debug!("report_and_explain_type_error(trace={:?}, terr={:?})", trace, terr); + + let span = trace.cause.span(self.tcx); + 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 ValuePairs::Types(ty::error::ExpectedFound { expected, found }) = + trace.values + { + // 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) + match (expected.kind(), found.kind()) { + (ty::Tuple(_), ty::Tuple(_)) => {} + (ty::Tuple(_), _) if expected.tuple_fields().count() == 1 => { + 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, + "use a trailing comma to create a tuple with one element", + format!("({},)", code), + Applicability::MaybeIncorrect, + ); + } + } + } + _ => {} + } + } + 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); + diag + } + + fn values_str( + &self, + values: ValuePairs<'tcx>, + ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> { + match values { + infer::Types(exp_found) => self.expected_found_str_ty(exp_found), + infer::Regions(exp_found) => self.expected_found_str(exp_found), + infer::Consts(exp_found) => self.expected_found_str(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(), + }; + self.expected_found_str(pretty_exp_found) + } + 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(), + }; + self.expected_found_str(pretty_exp_found) + } + } + } + + fn expected_found_str_ty( + &self, + exp_found: ty::error::ExpectedFound<Ty<'tcx>>, + ) -> Option<(DiagnosticStyledString, DiagnosticStyledString)> { + let exp_found = self.resolve_vars_if_possible(exp_found); + if exp_found.references_error() { + return None; + } + + Some(self.cmp(exp_found.expected, exp_found.found)) + } + + /// 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, + span: Span, + origin: Option<SubregionOrigin<'tcx>>, + bound_kind: GenericKind<'tcx>, + sub: Region<'tcx>, + ) { + self.construct_generic_bound_failure(span, origin, bound_kind, sub).emit(); + } + + pub fn construct_generic_bound_failure( + &self, + span: Span, + origin: Option<SubregionOrigin<'tcx>>, + bound_kind: GenericKind<'tcx>, + sub: Region<'tcx>, + ) -> DiagnosticBuilder<'a> { + let hir = &self.tcx.hir(); + // Attempt to obtain the span of the parameter so we can + // suggest adding an explicit lifetime bound to it. + let generics = self + .in_progress_typeck_results + .map(|typeck_results| typeck_results.borrow().hir_owner) + .map(|owner| { + let hir_id = hir.local_def_id_to_hir_id(owner); + let parent_id = hir.get_parent_item(hir_id); + ( + // Parent item could be a `mod`, so we check the HIR before calling: + if let Some(Node::Item(Item { + kind: ItemKind::Trait(..) | ItemKind::Impl { .. }, + .. + })) = hir.find(parent_id) + { + Some(self.tcx.generics_of(hir.local_def_id(parent_id).to_def_id())) + } else { + None + }, + self.tcx.generics_of(owner.to_def_id()), + ) + }); + let type_param_span = match (generics, bound_kind) { + (Some((_, ref generics)), 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 id = hir.local_def_id_to_hir_id(def_id); + let mut has_bounds = false; + if let Node::GenericParam(param) = hir.get(id) { + has_bounds = !param.bounds.is_empty(); + } + let sp = hir.span(id); + // `sp` only covers `T`, change it so that it covers + // `T:` when appropriate + let is_impl_trait = bound_kind.to_string().starts_with("impl "); + let sp = if has_bounds && !is_impl_trait { + sp.to(self + .tcx + .sess + .source_map() + .next_point(self.tcx.sess.source_map().next_point(sp))) + } else { + sp + }; + (sp, has_bounds, is_impl_trait) + }) + } else { + None + } + } + _ => None, + }; + let new_lt = generics + .as_ref() + .and_then(|(parent_g, g)| { + let mut possible = (b'a'..=b'z').map(|c| format!("'{}", c as char)); + let mut lts_names = g + .params + .iter() + .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) + .map(|p| p.name.as_str()) + .collect::<Vec<_>>(); + if let Some(g) = parent_g { + lts_names.extend( + g.params + .iter() + .filter(|p| matches!(p.kind, ty::GenericParamDefKind::Lifetime)) + .map(|p| p.name.as_str()), + ); + } + let lts = lts_names.iter().map(|s| -> &str { &*s }).collect::<Vec<_>>(); + possible.find(|candidate| !lts.contains(&candidate.as_str())) + }) + .unwrap_or("'lt".to_string()); + let add_lt_sugg = generics + .as_ref() + .and_then(|(_, g)| g.params.first()) + .and_then(|param| param.def_id.as_local()) + .map(|def_id| { + ( + hir.span(hir.local_def_id_to_hir_id(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::CompareImplMethodObligation { + span, + item_name, + impl_item_def_id, + trait_item_def_id, + }) = origin + { + return self.report_extra_impl_obligation( + span, + item_name, + impl_item_def_id, + trait_item_def_id, + &format!("`{}: {}`", bound_kind, sub), + ); + } + + fn binding_suggestion<'tcx, S: fmt::Display>( + err: &mut DiagnosticBuilder<'tcx>, + type_param_span: Option<(Span, bool, bool)>, + bound_kind: GenericKind<'tcx>, + sub: S, + ) { + let msg = "consider adding an explicit lifetime bound"; + if let Some((sp, has_lifetimes, is_impl_trait)) = type_param_span { + let suggestion = if is_impl_trait { + format!("{} + {}", bound_kind, sub) + } else { + let tail = if has_lifetimes { " + " } else { "" }; + format!("{}: {}{}", bound_kind, sub, tail) + }; + err.span_suggestion( + sp, + &format!("{}...", msg), + suggestion, + Applicability::MaybeIncorrect, // Issue #41966 + ); + } else { + let consider = format!( + "{} {}...", + msg, + if type_param_span.map_or(false, |(_, _, is_impl_trait)| is_impl_trait) { + format!(" `{}` to `{}`", sub, bound_kind) + } else { + format!("`{}: {}`", bound_kind, sub) + }, + ); + err.help(&consider); + } + } + + let new_binding_suggestion = + |err: &mut DiagnosticBuilder<'tcx>, + type_param_span: Option<(Span, bool, bool)>, + bound_kind: GenericKind<'tcx>| { + let msg = "consider introducing an explicit lifetime bound"; + if let Some((sp, has_lifetimes, is_impl_trait)) = type_param_span { + let suggestion = if is_impl_trait { + (sp.shrink_to_hi(), format!(" + {}", new_lt)) + } else { + let tail = if has_lifetimes { " +" } else { "" }; + (sp, format!("{}: {}{}", bound_kind, new_lt, tail)) + }; + let mut sugg = + vec![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); + } + }; + + let mut err = match *sub { + ty::ReEarlyBound(ty::EarlyBoundRegion { name, .. }) + | ty::ReFree(ty::FreeRegion { bound_region: ty::BrNamed(_, name), .. }) => { + // 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 + .in_progress_typeck_results + .map(|typeck_results| typeck_results.borrow().hir_owner) + .and_then(|owner| self.tcx.return_type_impl_trait(owner)) + .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, bound_kind); + } + _ => { + 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, trait_def_id) + } +} + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + fn report_inference_failure( + &self, + var_origin: RegionVariableOrigin, + ) -> DiagnosticBuilder<'tcx> { + 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).ident + ), + 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 + ) + } +} + +enum FailureCode { + Error0038(DefId), + Error0317(&'static str), + Error0580(&'static str), + Error0308(&'static str), + Error0644(&'static str), +} + +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 { + CompareImplMethodObligation { .. } => Error0308("method not compatible with trait"), + CompareImplTypeObligation { .. } => Error0308("type not compatible with trait"), + MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => { + Error0308(match source { + hir::MatchSource::IfLetDesugar { .. } => { + "`if let` arms have incompatible types" + } + hir::MatchSource::TryDesugar => { + "try expression alternatives have incompatible types" + } + _ => "`match` arms have incompatible types", + }) + } + IfExpression { .. } => Error0308("`if` and `else` have incompatible types"), + IfExpressionWithNoElse => Error0317("`if` may be missing an `else` clause"), + 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 { + CompareImplMethodObligation { .. } => "method type is compatible with trait", + CompareImplTypeObligation { .. } => "associated type is compatible with trait", + ExprAssignable => "expression is assignable", + MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => match source { + hir::MatchSource::IfLetDesugar { .. } => "`if let` arms have compatible types", + _ => "`match` arms have compatible types", + }, + 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, + } + } +} 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 00000000000..c2025f3fe4d --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs @@ -0,0 +1,858 @@ +use crate::infer::type_variable::TypeVariableOriginKind; +use crate::infer::InferCtxt; +use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Namespace}; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, Pat}; +use rustc_middle::hir::map::Map; +use rustc_middle::infer::unify_key::ConstVariableOriginKind; +use rustc_middle::ty::print::Print; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind}; +use rustc_middle::ty::{self, DefIdTree, InferConst, Ty, TyCtxt}; +use rustc_span::source_map::DesugaringKind; +use rustc_span::symbol::kw; +use rustc_span::Span; +use std::borrow::Cow; + +struct FindHirNodeVisitor<'a, 'tcx> { + infcx: &'a InferCtxt<'a, 'tcx>, + target: GenericArg<'tcx>, + target_span: Span, + found_node_ty: Option<Ty<'tcx>>, + found_local_pattern: Option<&'tcx Pat<'tcx>>, + found_arg_pattern: Option<&'tcx Pat<'tcx>>, + found_closure: Option<&'tcx Expr<'tcx>>, + found_method_call: Option<&'tcx Expr<'tcx>>, + found_exact_method_call: Option<&'tcx Expr<'tcx>>, + found_use_diagnostic: Option<UseDiagnostic<'tcx>>, +} + +impl<'a, 'tcx> FindHirNodeVisitor<'a, 'tcx> { + fn new(infcx: &'a InferCtxt<'a, 'tcx>, target: GenericArg<'tcx>, target_span: Span) -> Self { + Self { + infcx, + target, + target_span, + found_node_ty: None, + found_local_pattern: None, + found_arg_pattern: None, + found_closure: None, + found_method_call: None, + found_exact_method_call: None, + found_use_diagnostic: None, + } + } + + fn node_type_opt(&self, hir_id: HirId) -> Option<Ty<'tcx>> { + self.infcx.in_progress_typeck_results?.borrow().node_type_opt(hir_id) + } + + fn node_ty_contains_target(&self, hir_id: HirId) -> Option<Ty<'tcx>> { + self.node_type_opt(hir_id).map(|ty| self.infcx.resolve_vars_if_possible(ty)).filter(|ty| { + ty.walk().any(|inner| { + inner == self.target + || match (inner.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, + } + } + _ => false, + } + }) + }) + } + + /// Determine whether the expression, assumed to be the callee within a `Call`, + /// corresponds to the `From::from` emitted in desugaring of the `?` operator. + fn is_try_conversion(&self, callee: &Expr<'tcx>) -> bool { + self.infcx + .trait_def_from_hir_fn(callee.hir_id) + .map_or(false, |def_id| self.infcx.is_try_conversion(callee.span, def_id)) + } +} + +impl<'a, 'tcx> Visitor<'tcx> for FindHirNodeVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::OnlyBodies(self.infcx.tcx.hir()) + } + + fn visit_local(&mut self, local: &'tcx Local<'tcx>) { + if let (None, Some(ty)) = + (self.found_local_pattern, self.node_ty_contains_target(local.hir_id)) + { + self.found_local_pattern = Some(&*local.pat); + self.found_node_ty = Some(ty); + } + intravisit::walk_local(self, local); + } + + fn visit_body(&mut self, body: &'tcx Body<'tcx>) { + for param in body.params { + if let (None, Some(ty)) = + (self.found_arg_pattern, self.node_ty_contains_target(param.hir_id)) + { + self.found_arg_pattern = Some(&*param.pat); + self.found_node_ty = Some(ty); + } + } + intravisit::walk_body(self, body); + } + + fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { + if let ExprKind::MethodCall(_, call_span, exprs, _) = expr.kind { + if call_span == self.target_span + && Some(self.target) + == self.infcx.in_progress_typeck_results.and_then(|typeck_results| { + typeck_results + .borrow() + .node_type_opt(exprs.first().unwrap().hir_id) + .map(Into::into) + }) + { + self.found_exact_method_call = Some(&expr); + return; + } + } + + // FIXME(const_generics): Currently, any uninferred `const` generics arguments + // are handled specially, but instead they should be handled in `annotate_method_call`, + // which currently doesn't work because this evaluates to `false` for const arguments. + // See https://github.com/rust-lang/rust/pull/77758 for more details. + if let Some(ty) = self.node_ty_contains_target(expr.hir_id) { + match expr.kind { + ExprKind::Closure(..) => self.found_closure = Some(&expr), + ExprKind::MethodCall(..) => self.found_method_call = Some(&expr), + + // If the given expression falls within the target span and is a + // `From::from(e)` call emitted during desugaring of the `?` operator, + // extract the types inferred before and after the call + ExprKind::Call(callee, [arg]) + if self.target_span.contains(expr.span) + && self.found_use_diagnostic.is_none() + && self.is_try_conversion(callee) => + { + self.found_use_diagnostic = self.node_type_opt(arg.hir_id).map(|pre_ty| { + UseDiagnostic::TryConversion { pre_ty, post_ty: ty, span: callee.span } + }); + } + _ => {} + } + } + intravisit::walk_expr(self, expr); + } +} + +/// An observation about the use site of a type to be emitted as an additional +/// note in an inference failure error. +enum UseDiagnostic<'tcx> { + /// Records the types inferred before and after `From::from` is called on the + /// error value within the desugaring of the `?` operator. + TryConversion { pre_ty: Ty<'tcx>, post_ty: Ty<'tcx>, span: Span }, +} + +impl UseDiagnostic<'_> { + /// Return a descriptor of the value at the use site + fn descr(&self) -> &'static str { + match self { + Self::TryConversion { .. } => "error for `?` operator", + } + } + + /// Return a descriptor of the type at the use site + fn type_descr(&self) -> &'static str { + match self { + Self::TryConversion { .. } => "error type for `?` operator", + } + } + + fn applies_to(&self, span: Span) -> bool { + match *self { + // In some cases the span for an inference failure due to try + // conversion contains the antecedent expression as well as the `?` + Self::TryConversion { span: s, .. } => span.contains(s) && span.hi() == s.hi(), + } + } + + fn attach_note(&self, err: &mut DiagnosticBuilder<'_>) { + match *self { + Self::TryConversion { pre_ty, post_ty, .. } => { + let intro = "`?` implicitly converts the error value"; + + let msg = match (pre_ty.is_ty_infer(), post_ty.is_ty_infer()) { + (true, true) => format!("{} using the `From` trait", intro), + (false, true) => { + format!("{} into a type implementing `From<{}>`", intro, pre_ty) + } + (true, false) => { + format!("{} into `{}` using the `From` trait", intro, post_ty) + } + (false, false) => { + format!( + "{} into `{}` using its implementation of `From<{}>`", + intro, post_ty, pre_ty + ) + } + }; + + err.note(&msg); + } + } + } +} + +/// Suggest giving an appropriate return type to a closure expression. +fn closure_return_type_suggestion( + err: &mut DiagnosticBuilder<'_>, + output: &FnRetTy<'_>, + body: &Body<'_>, + ret: &str, +) { + let (arrow, post) = match output { + FnRetTy::DefaultReturn(_) => ("-> ", " "), + _ => ("", ""), + }; + let suggestion = match body.value.kind { + ExprKind::Block(..) => vec![(output.span(), format!("{}{}{}", arrow, ret, post))], + _ => vec![ + (output.span(), format!("{}{}{}{{ ", arrow, ret, post)), + (body.value.span.shrink_to_hi(), " }".to_string()), + ], + }; + err.multipart_suggestion( + "give this closure an explicit return type without `_` placeholders", + suggestion, + Applicability::HasPlaceholders, + ); +} + +/// Given a closure signature, return a `String` containing a list of all its argument types. +fn closure_args(fn_sig: &ty::PolyFnSig<'_>) -> String { + fn_sig + .inputs() + .skip_binder() + .iter() + .next() + .map(|args| args.tuple_fields().map(|arg| arg.to_string()).collect::<Vec<_>>().join(", ")) + .unwrap_or_default() +} + +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 { + pub prefix: &'static str, + pub name: String, + pub def_id: DefId, +} + +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, use_diag: Option<&UseDiagnostic<'_>>) -> String { + if self.name == "_" && matches!(self.kind, UnderspecifiedArgKind::Type { .. }) { + if let Some(use_diag) = use_diag { + return format!("cannot infer type of {}", use_diag.descr()); + } + + return "cannot infer type".to_string(); + } + + let suffix = match (&self.parent, use_diag) { + (Some(parent), _) => format!(" declared on the {} `{}`", parent.prefix, parent.name), + (None, Some(use_diag)) => format!(" in {}", use_diag.type_descr()), + (None, None) => String::new(), + }; + + // For example: "cannot infer type for type parameter `T`" + format!("cannot infer {} `{}`{}", self.kind.prefix_string(), self.name, suffix) + } +} + +impl InferenceDiagnosticsParentData { + fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> { + let parent_def_id = tcx.parent(def_id)?; + + 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, + def_id: parent_def_id, + }) + } +} + +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(), + } + } +} + +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>, + ) -> 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 s = String::new(); + let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS); + if let Some(highlight) = highlight { + printer.region_highlight_mode = highlight; + } + let _ = ty.print(printer); + InferenceDiagnosticsData { + name: s, + 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.val { + 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 s = String::new(); + let mut printer = + ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::ValueNS); + if let Some(highlight) = highlight { + printer.region_highlight_mode = highlight; + } + let _ = ct.print(printer); + InferenceDiagnosticsData { + name: s, + span: Some(origin.span), + kind: UnderspecifiedArgKind::Const { is_parameter: false }, + parent: None, + } + } else { + bug!("unexpect const: {:?}", ct); + } + } + GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), + } + } + + pub fn emit_inference_failure_err( + &self, + body_id: Option<hir::BodyId>, + span: Span, + arg: GenericArg<'tcx>, + impl_candidates: Vec<ty::TraitRef<'tcx>>, + error_code: TypeAnnotationNeeded, + ) -> DiagnosticBuilder<'tcx> { + let arg = self.resolve_vars_if_possible(arg); + let arg_data = self.extract_inference_diagnostics_data(arg, None); + + let mut local_visitor = FindHirNodeVisitor::new(&self, arg, span); + let ty_to_string = |ty: Ty<'tcx>| -> String { + let mut s = String::new(); + let mut printer = ty::print::FmtPrinter::new(self.tcx, &mut s, Namespace::TypeNS); + let mut inner = self.inner.borrow_mut(); + let ty_vars = inner.type_variables(); + let getter = move |ty_vid| { + let var_origin = ty_vars.var_origin(ty_vid); + if let TypeVariableOriginKind::TypeParameterDefinition(name, _) = var_origin.kind { + return Some(name.to_string()); + } + None + }; + printer.name_resolver = Some(Box::new(&getter)); + let _ = if let ty::FnDef(..) = 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.fn_sig(self.tcx).print(printer) + } else { + ty.print(printer) + }; + s + }; + + if let Some(body_id) = body_id { + let expr = self.tcx.hir().expect_expr(body_id.hir_id); + local_visitor.visit_expr(expr); + } + let err_span = if let Some(pattern) = local_visitor.found_arg_pattern { + pattern.span + } else if let Some(span) = arg_data.span { + // `span` here lets us point at `sum` instead of the entire right hand side expr: + // error[E0282]: type annotations needed + // --> file2.rs:3:15 + // | + // 3 | let _ = x.sum() as f64; + // | ^^^ cannot infer type for `S` + span + } else if let Some(ExprKind::MethodCall(_, call_span, _, _)) = + local_visitor.found_method_call.map(|e| &e.kind) + { + // Point at the call instead of the whole expression: + // error[E0284]: type annotations needed + // --> file.rs:2:5 + // | + // 2 | vec![Ok(2)].into_iter().collect()?; + // | ^^^^^^^ cannot infer type + // | + // = note: cannot resolve `<_ as std::ops::Try>::Ok == _` + if span.contains(*call_span) { *call_span } else { span } + } else { + span + }; + + let is_named_and_not_impl_trait = + |ty: Ty<'_>| &ty.to_string() != "_" && !ty.is_impl_trait(); + + let ty_msg = match (local_visitor.found_node_ty, local_visitor.found_exact_method_call) { + (_, Some(_)) => String::new(), + (Some(ty), _) if ty.is_closure() => { + let substs = + if let ty::Closure(_, substs) = *ty.kind() { substs } else { unreachable!() }; + let fn_sig = substs.as_closure().sig(); + let args = closure_args(&fn_sig); + let ret = fn_sig.output().skip_binder().to_string(); + format!(" for the closure `fn({}) -> {}`", args, ret) + } + (Some(ty), _) if is_named_and_not_impl_trait(ty) => { + let ty = ty_to_string(ty); + format!(" for `{}`", ty) + } + _ => String::new(), + }; + + // When `arg_data.name` corresponds to a type argument, show the path of the full type we're + // trying to infer. In the following example, `ty_msg` contains + // " for `std::result::Result<i32, E>`": + // ``` + // error[E0282]: type annotations needed for `std::result::Result<i32, E>` + // --> file.rs:L:CC + // | + // L | let b = Ok(4); + // | - ^^ cannot infer type for `E` in `std::result::Result<i32, E>` + // | | + // | consider giving `b` the explicit type `std::result::Result<i32, E>`, where + // | the type parameter `E` is specified + // ``` + let error_code = error_code.into(); + let mut err = self.tcx.sess.struct_span_err_with_code( + err_span, + &format!("type annotations needed{}", ty_msg), + error_code, + ); + + let use_diag = local_visitor.found_use_diagnostic.as_ref(); + if let Some(use_diag) = use_diag { + if use_diag.applies_to(err_span) { + use_diag.attach_note(&mut err); + } + } + + let suffix = match local_visitor.found_node_ty { + Some(ty) if ty.is_closure() => { + let substs = + if let ty::Closure(_, substs) = *ty.kind() { substs } else { unreachable!() }; + let fn_sig = substs.as_closure().sig(); + let ret = fn_sig.output().skip_binder().to_string(); + + let closure_decl_and_body_id = + local_visitor.found_closure.and_then(|closure| match &closure.kind { + ExprKind::Closure(_, decl, body_id, ..) => Some((decl, *body_id)), + _ => None, + }); + + if let Some((decl, body_id)) = closure_decl_and_body_id { + closure_return_type_suggestion( + &mut err, + &decl.output, + self.tcx.hir().body(body_id), + &ret, + ); + // We don't want to give the other suggestions when the problem is the + // closure return type. + err.span_label( + span, + arg_data.cannot_infer_msg(use_diag.filter(|d| d.applies_to(span))), + ); + return err; + } + + // This shouldn't be reachable, but just in case we leave a reasonable fallback. + let args = closure_args(&fn_sig); + // This suggestion is incomplete, as the user will get further type inference + // errors due to the `_` placeholders and the introduction of `Box`, but it does + // nudge them in the right direction. + format!("a boxed closure type like `Box<dyn Fn({}) -> {}>`", args, ret) + } + Some(ty) if is_named_and_not_impl_trait(ty) && arg_data.name == "_" => { + let ty = ty_to_string(ty); + format!("the explicit type `{}`, with the type parameters specified", ty) + } + Some(ty) if is_named_and_not_impl_trait(ty) && ty.to_string() != arg_data.name => { + let ty = ty_to_string(ty); + format!( + "the explicit type `{}`, where the type parameter `{}` is specified", + ty, arg_data.name, + ) + } + _ => "a type".to_string(), + }; + + if let Some(e) = local_visitor.found_exact_method_call { + if let ExprKind::MethodCall(segment, ..) = &e.kind { + // Suggest specifying type params or point out the return type of the call: + // + // error[E0282]: type annotations needed + // --> $DIR/type-annotations-needed-expr.rs:2:39 + // | + // LL | let _ = x.into_iter().sum() as f64; + // | ^^^ + // | | + // | cannot infer type for `S` + // | help: consider specifying the type argument in + // | the method call: `sum::<S>` + // | + // = note: type must be known at this point + // + // or + // + // error[E0282]: type annotations needed + // --> $DIR/issue-65611.rs:59:20 + // | + // LL | let x = buffer.last().unwrap().0.clone(); + // | -------^^^^-- + // | | | + // | | cannot infer type for `T` + // | this method call resolves to `std::option::Option<&T>` + // | + // = note: type must be known at this point + self.annotate_method_call(segment, e, &mut err); + } + } else if let Some(pattern) = local_visitor.found_arg_pattern { + // We don't want to show the default label for closures. + // + // So, before clearing, the output would look something like this: + // ``` + // let x = |_| { }; + // - ^^^^ cannot infer type for `[_; 0]` + // | + // consider giving this closure parameter a type + // ``` + // + // After clearing, it looks something like this: + // ``` + // let x = |_| { }; + // ^ consider giving this closure parameter the type `[_; 0]` + // with the type parameter `_` specified + // ``` + err.span_label( + pattern.span, + format!("consider giving this closure parameter {}", suffix), + ); + } else if let Some(pattern) = local_visitor.found_local_pattern { + let msg = if let Some(simple_ident) = pattern.simple_ident() { + match pattern.span.desugaring_kind() { + None => format!("consider giving `{}` {}", simple_ident, suffix), + Some(DesugaringKind::ForLoop(_)) => { + "the element type for this iterator is not specified".to_string() + } + _ => format!("this needs {}", suffix), + } + } else { + format!("consider giving this pattern {}", suffix) + }; + err.span_label(pattern.span, msg); + } else if let Some(e) = local_visitor.found_method_call { + if let ExprKind::MethodCall(segment, _, exprs, _) = &e.kind { + // Suggest impl candidates: + // + // error[E0283]: type annotations needed + // --> $DIR/E0283.rs:35:24 + // | + // LL | let bar = foo_impl.into() * 1u32; + // | ---------^^^^-- + // | | | + // | | cannot infer type for type parameter `T` declared on the trait `Into` + // | this method call resolves to `T` + // | help: specify type like: `<Impl as Into<u32>>::into(foo_impl)` + // | + // = note: cannot satisfy `Impl: Into<_>` + if !impl_candidates.is_empty() && e.span.contains(span) { + if let Some(expr) = exprs.first() { + if let ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind { + if let [path_segment] = path.segments { + let candidate_len = impl_candidates.len(); + let suggestions = impl_candidates.iter().map(|candidate| { + format!( + "{}::{}({})", + candidate, segment.ident, path_segment.ident + ) + }); + err.span_suggestions( + e.span, + &format!( + "use the fully qualified path for the potential candidate{}", + pluralize!(candidate_len), + ), + suggestions, + Applicability::MaybeIncorrect, + ); + } + } + }; + } + // Suggest specifying type params or point out the return type of the call: + // + // error[E0282]: type annotations needed + // --> $DIR/type-annotations-needed-expr.rs:2:39 + // | + // LL | let _ = x.into_iter().sum() as f64; + // | ^^^ + // | | + // | cannot infer type for `S` + // | help: consider specifying the type argument in + // | the method call: `sum::<S>` + // | + // = note: type must be known at this point + // + // or + // + // error[E0282]: type annotations needed + // --> $DIR/issue-65611.rs:59:20 + // | + // LL | let x = buffer.last().unwrap().0.clone(); + // | -------^^^^-- + // | | | + // | | cannot infer type for `T` + // | this method call resolves to `std::option::Option<&T>` + // | + // = note: type must be known at this point + self.annotate_method_call(segment, e, &mut err); + } + } + // Instead of the following: + // error[E0282]: type annotations needed + // --> file2.rs:3:15 + // | + // 3 | let _ = x.sum() as f64; + // | --^^^--------- cannot infer type for `S` + // | + // = note: type must be known at this point + // We want: + // error[E0282]: type annotations needed + // --> file2.rs:3:15 + // | + // 3 | let _ = x.sum() as f64; + // | ^^^ cannot infer type for `S` + // | + // = note: type must be known at this point + let span = arg_data.span.unwrap_or(err_span); + + // Avoid multiple labels pointing at `span`. + if !err + .span + .span_labels() + .iter() + .any(|span_label| span_label.label.is_some() && span_label.span == span) + && local_visitor.found_arg_pattern.is_none() + { + // FIXME(const_generics): we would like to handle const arguments + // as part of the normal diagnostics flow below, but there appear to + // be subtleties in doing so, so for now we special-case const args + // here. + if let (UnderspecifiedArgKind::Const { .. }, Some(parent_data)) = + (&arg_data.kind, &arg_data.parent) + { + // (#83606): Do not emit a suggestion if the parent has an `impl Trait` + // as an argument otherwise it will cause the E0282 error. + if !self.tcx.generics_of(parent_data.def_id).has_impl_trait() + || self.tcx.features().explicit_generic_args_with_impl_trait + { + err.span_suggestion_verbose( + span, + "consider specifying the const argument", + format!("{}::<{}>", parent_data.name, arg_data.name), + Applicability::MaybeIncorrect, + ); + } + } + + err.span_label( + span, + arg_data.cannot_infer_msg(use_diag.filter(|d| d.applies_to(span))), + ); + } + + err + } + + fn trait_def_from_hir_fn(&self, hir_id: hir::HirId) -> Option<DefId> { + // The DefId will be the method's trait item ID unless this is an inherent impl + if let Some((DefKind::AssocFn, def_id)) = + self.in_progress_typeck_results?.borrow().type_dependent_def(hir_id) + { + return self + .tcx + .parent(def_id) + .filter(|&parent_def_id| self.tcx.is_trait(parent_def_id)); + } + + None + } + + /// If the `FnSig` for the method call can be found and type arguments are identified as + /// needed, suggest annotating the call, otherwise point out the resulting type of the call. + fn annotate_method_call( + &self, + segment: &hir::PathSegment<'_>, + e: &Expr<'_>, + err: &mut DiagnosticBuilder<'_>, + ) { + if let (Some(typeck_results), None) = (self.in_progress_typeck_results, &segment.args) { + let borrow = typeck_results.borrow(); + if let Some((DefKind::AssocFn, did)) = borrow.type_dependent_def(e.hir_id) { + let generics = self.tcx.generics_of(did); + if !generics.params.is_empty() && !generics.has_impl_trait() { + err.span_suggestion_verbose( + segment.ident.span.shrink_to_hi(), + &format!( + "consider specifying the type argument{} in the method call", + pluralize!(generics.params.len()), + ), + format!( + "::<{}>", + generics + .params + .iter() + .map(|p| p.name.to_string()) + .collect::<Vec<String>>() + .join(", ") + ), + Applicability::HasPlaceholders, + ); + } else { + let sig = self.tcx.fn_sig(did); + let bound_output = sig.output(); + let output = bound_output.skip_binder(); + err.span_label(e.span, &format!("this method call resolves to `{}`", output)); + let kind = output.kind(); + if let ty::Projection(proj) = kind { + if let Some(span) = self.tcx.hir().span_if_local(proj.item_def_id) { + err.span_label(span, &format!("`{}` defined here", output)); + } + } + } + } + } + } + + pub fn need_type_info_err_in_generator( + &self, + kind: hir::GeneratorKind, + span: Span, + ty: Ty<'tcx>, + ) -> DiagnosticBuilder<'tcx> { + 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(None)); + err + } +} 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 00000000000..1b35c4032f4 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/different_lifetimes.rs @@ -0,0 +1,191 @@ +//! 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 rustc_errors::{struct_span_err, ErrorReported}; + +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 + /// + /// ```no_run + /// 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<ErrorReported> { + 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(), + }; + + let (span_1, span_2, main_label, span_label, future_return_type) = + match (sup_is_ret_type, sub_is_ret_type) { + (None, None) => { + let (main_label_1, span_label_1) = if ty_sup.hir_id == ty_sub.hir_id { + ( + "this type is declared with multiple lifetimes...".to_owned(), + "...but data with one lifetime flows into the other here".to_owned(), + ) + } else { + ( + "these two types are declared with different lifetimes...".to_owned(), + format!("...but data{} flows{} here", span_label_var1, span_label_var2), + ) + }; + (ty_sup.span, ty_sub.span, main_label_1, span_label_1, None) + } + + (Some(ret_span), _) => { + let sup_future = self.future_return_type(scope_def_id_sup); + let (return_type, action) = if sup_future.is_some() { + ("returned future", "held across an await point") + } else { + ("return type", "returned") + }; + + ( + ty_sub.span, + ret_span, + format!( + "this parameter and the {} are declared with different lifetimes...", + return_type + ), + format!("...but data{} is {} here", span_label_var1, action), + sup_future, + ) + } + (_, Some(ret_span)) => { + let sub_future = self.future_return_type(scope_def_id_sub); + let (return_type, action) = if sub_future.is_some() { + ("returned future", "held across an await point") + } else { + ("return type", "returned") + }; + + ( + ty_sup.span, + ret_span, + format!( + "this parameter and the {} are declared with different lifetimes...", + return_type + ), + format!("...but data{} is {} here", span_label_var1, action), + sub_future, + ) + } + }; + + let mut e = struct_span_err!(self.tcx().sess, span, E0623, "lifetime mismatch"); + + e.span_label(span_1, main_label); + e.span_label(span_2, String::new()); + e.span_label(span, span_label); + + if let Some(t) = future_return_type { + let snip = self + .tcx() + .sess + .source_map() + .span_to_snippet(t.span) + .ok() + .and_then(|s| match (&t.kind, s.as_str()) { + (rustc_hir::TyKind::Tup(&[]), "") => Some("()".to_string()), + (_, "") => None, + _ => Some(s), + }) + .unwrap_or("{unnamed_type}".to_string()); + + e.span_label( + t.span, + &format!("this `async fn` implicitly returns an `impl Future<Output = {}>`", snip), + ); + } + e.emit(); + Some(ErrorReported) + } +} 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 00000000000..58eb1e9aa12 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/find_anon_type.rs @@ -0,0 +1,274 @@ +use rustc_hir as hir; +use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; +use rustc_hir::Node; +use rustc_middle::hir::map::Map; +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 +/// ``` +/// 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(crate) fn find_anon_type( + tcx: TyCtxt<'tcx>, + region: Region<'tcx>, + br: &ty::BoundRegionKind, +) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnDecl<'tcx>)> { + if let Some(anon_reg) = tcx.is_suitable_region(region) { + let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id); + let fndecl = match tcx.hir().get(hir_id) { + Node::Item(&hir::Item { kind: hir::ItemKind::Fn(ref m, ..), .. }) + | Node::TraitItem(&hir::TraitItem { + kind: hir::TraitItemKind::Fn(ref m, ..), .. + }) + | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(ref m, ..), .. }) => { + &m.decl + } + _ => return None, + }; + + fndecl + .inputs + .iter() + .find_map(|arg| find_component_for_bound_region(tcx, arg, br)) + .map(|ty| (ty, &**fndecl)) + } else { + None + } +} + +// This method creates a FindNestedTypeVisitor which returns the type corresponding +// to the anonymous region. +fn find_component_for_bound_region( + 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 Visitor<'tcx> for FindNestedTypeVisitor<'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::OnlyBodies(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 anonymous 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::LateBoundAnon(debruijn_index, _, anon_index)), + ty::BrAnon(br_index), + ) => { + debug!( + "LateBoundAnon depth = {:?} anon_index = {:?} br_index={:?}", + debruijn_index, anon_index, br_index + ); + if debruijn_index == self.current_index && anon_index == br_index { + 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::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(_, _, _, _) + | rl::Region::LateBoundAnon(_, _, _), + ) + | 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 Visitor<'tcx> for TyPathVisitor<'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Map<'tcx>> { + NestedVisitorMap::OnlyBodies(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::LateBoundAnon(debruijn_index, _, anon_index)), + ty::BrAnon(br_index), + ) => { + if debruijn_index == self.current_index && anon_index == br_index { + self.found_it = true; + return; + } + } + + (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::LateBoundAnon(_, _, _) + | 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 00000000000..c60a7149e40 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mismatched_static_lifetime.rs @@ -0,0 +1,103 @@ +//! 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::stable_set::FxHashSet; +use rustc_errors::{Applicability, ErrorReported}; +use rustc_hir as hir; +use rustc_hir::intravisit::Visitor; +use rustc_middle::ty::{self, TypeVisitor}; +use rustc_span::MultiSpan; + +impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { + pub(super) fn try_report_mismatched_static_lifetime(&self) -> Option<ErrorReported> { + let error = self.error.as_ref()?; + debug!("try_report_mismatched_static_lifetime {:?}", error); + + let (origin, sub, sup) = match error.clone() { + RegionResolutionError::ConcreteFailure(origin, sub, sup) => (origin, sub, sup), + _ => return None, + }; + if *sub != ty::RegionKind::ReStatic { + return None; + } + let cause = match origin { + SubregionOrigin::Subtype(box TypeTrace { ref cause, .. }) => cause, + _ => return None, + }; + let (parent, impl_def_id) = match &cause.code { + ObligationCauseCode::MatchImpl(parent, impl_def_id) => (parent, impl_def_id), + _ => return None, + }; + let binding_span = match **parent { + ObligationCauseCode::BindingObligation(_def_id, binding_span) => binding_span, + _ => 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".into()); + 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 impl_self_ty = match impl_node { + hir::Node::Item(hir::Item { + kind: hir::ItemKind::Impl(hir::Impl { self_ty, .. }), + .. + }) => self_ty, + _ => bug!("Node not an impl."), + }; + + // Next, let's figure out the set of trait objects with implict 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", + " + '_".to_string(), + 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`"); + } + err.emit(); + Some(ErrorReported) + } +} 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 00000000000..3f27bf67b59 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs @@ -0,0 +1,75 @@ +use crate::infer::lexical_region_resolve::RegionResolutionError; +use crate::infer::lexical_region_resolve::RegionResolutionError::*; +use crate::infer::InferCtxt; +use rustc_errors::{DiagnosticBuilder, ErrorReported}; +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; + +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>> { + // 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<ErrorReported> { + self.try_report_from_nll() + .map(|mut diag| { + diag.emit(); + ErrorReported + }) + .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 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 00000000000..0878f8550da --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/named_anon_conflict.rs @@ -0,0 +1,130 @@ +//! 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}; +use rustc_hir::intravisit::Visitor; +use rustc_hir::FnRetTy; +use rustc_middle::ty; + +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>> { + 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 + }; + + 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, new_ty, new_ty_span, br, is_first, scope_def_id, is_impl_item) = ( + anon_param_info.param, + anon_param_info.param_ty, + anon_param_info.param_ty_span, + anon_param_info.bound_region, + anon_param_info.is_first, + region_info.def_id, + region_info.is_impl_item, + ); + match br { + 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 let Some((_, fndecl)) = find_anon_type(self.tcx(), anon, &br) { + if self.is_self_anon(is_first, scope_def_id) { + return None; + } + + if let FnRetTy::Return(ty) = &fndecl.output { + let mut v = ty::TraitObjectVisitor(vec![], self.tcx().hir()); + v.visit_ty(ty); + + debug!("try_report_named_anon_conflict: ret ty {:?}", ty); + if sub == &ty::ReStatic + && v.0.into_iter().any(|t| t.span.desugaring_kind().is_none()) + { + // If the failure is due to a `'static` requirement coming from a `dyn` or + // `impl` Trait that *isn't* caused by `async fn` desugaring, handle this case + // better in `static_impl_trait`. + debug!("try_report_named_anon_conflict: impl Trait + 'static"); + 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)); + // Suggesting `'static` is nearly always incorrect, and can steer users + // down the wrong path. + if *named != ty::ReStatic { + diag.span_suggestion( + new_ty_span, + &format!("add explicit lifetime `{}` to {}", named, span_label_var), + new_ty.to_string(), + 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 00000000000..4aecc2f40b8 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/placeholder_error.rs @@ -0,0 +1,493 @@ +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_errors::DiagnosticBuilder; +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, TyCtxt}; + +use std::fmt::{self, Write}; + +impl NiceRegionError<'me, '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>> { + 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 @ ty::RePlaceholder(_), + _, + sup_placeholder @ ty::RePlaceholder(_), + )) => self.try_report_trait_placeholder_mismatch( + Some(self.tcx().mk_region(ty::ReVar(*vid))), + cause, + Some(sub_placeholder), + Some(sup_placeholder), + values, + ), + + Some(RegionResolutionError::SubSupConflict( + vid, + _, + SubregionOrigin::Subtype(box TypeTrace { cause, values }), + sub_placeholder @ ty::RePlaceholder(_), + _, + _, + )) => self.try_report_trait_placeholder_mismatch( + Some(self.tcx().mk_region(ty::ReVar(*vid))), + cause, + Some(sub_placeholder), + None, + values, + ), + + Some(RegionResolutionError::SubSupConflict( + vid, + _, + SubregionOrigin::Subtype(box TypeTrace { cause, values }), + _, + _, + sup_placeholder @ ty::RePlaceholder(_), + )) => self.try_report_trait_placeholder_mismatch( + Some(self.tcx().mk_region(ty::ReVar(*vid))), + cause, + None, + Some(*sup_placeholder), + values, + ), + + Some(RegionResolutionError::SubSupConflict( + vid, + _, + _, + _, + SubregionOrigin::Subtype(box TypeTrace { cause, values }), + sup_placeholder @ ty::RePlaceholder(_), + )) => self.try_report_trait_placeholder_mismatch( + Some(self.tcx().mk_region(ty::ReVar(*vid))), + cause, + None, + Some(*sup_placeholder), + values, + ), + + Some(RegionResolutionError::UpperBoundUniverseConflict( + vid, + _, + _, + SubregionOrigin::Subtype(box TypeTrace { cause, values }), + sup_placeholder @ ty::RePlaceholder(_), + )) => self.try_report_trait_placeholder_mismatch( + Some(self.tcx().mk_region(ty::ReVar(*vid))), + cause, + None, + Some(*sup_placeholder), + values, + ), + + Some(RegionResolutionError::ConcreteFailure( + SubregionOrigin::Subtype(box TypeTrace { cause, values }), + sub_region @ ty::RePlaceholder(_), + sup_region @ ty::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 @ ty::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 @ ty::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<ty::Region<'tcx>>, + cause: &ObligationCause<'tcx>, + sub_placeholder: Option<ty::Region<'tcx>>, + sup_placeholder: Option<ty::Region<'tcx>>, + value_pairs: &ValuePairs<'tcx>, + ) -> Option<DiagnosticBuilder<'tcx>> { + 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<ty::Region<'tcx>>, + cause: &ObligationCause<'tcx>, + sub_placeholder: Option<ty::Region<'tcx>>, + sup_placeholder: Option<ty::Region<'tcx>>, + trait_def_id: DefId, + expected_substs: SubstsRef<'tcx>, + actual_substs: SubstsRef<'tcx>, + ) -> DiagnosticBuilder<'tcx> { + let span = cause.span(self.tcx()); + 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 DiagnosticBuilder<'_>, + sub_placeholder: Option<ty::Region<'tcx>>, + sup_placeholder: Option<ty::Region<'tcx>>, + has_sub: Option<usize>, + has_sup: Option<usize>, + expected_trait_ref: ty::TraitRef<'tcx>, + actual_trait_ref: ty::TraitRef<'tcx>, + vid: Option<ty::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, + 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, 'b, 'c> Print< + 'tcx, + FmtPrinter<'a, 'tcx, &'b mut fmt::Formatter<'c>>, + Error = fmt::Error, + >, + { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut printer = ty::print::FmtPrinter::new(self.tcx, f, Namespace::TypeNS); + printer.region_highlight_mode = self.highlight; + + self.value.print(printer)?; + Ok(()) + } + } + + // 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::default(), + 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 00000000000..fde4ec05ffc --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/static_impl_trait.rs @@ -0,0 +1,518 @@ +//! 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::stable_set::FxHashSet; +use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::{walk_ty, ErasedMap, NestedVisitorMap, Visitor}; +use rustc_hir::{self as hir, GenericBound, Item, ItemKind, Lifetime, LifetimeName, Node, TyKind}; +use rustc_middle::ty::{self, AssocItemContainer, RegionKind, Ty, TypeFoldable, TypeVisitor}; +use rustc_span::symbol::Ident; +use rustc_span::{MultiSpan, 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<ErrorReported> { + debug!("try_report_static_impl_trait(error={:?})", self.error); + let tcx = self.tcx(); + let (var_origin, sub_origin, sub_r, sup_origin, sup_r) = match self.error.as_ref()? { + RegionResolutionError::SubSupConflict( + _, + var_origin, + sub_origin, + sub_r, + sup_origin, + sup_r, + ) if **sub_r == RegionKind::ReStatic => { + (var_origin, sub_origin, sub_r, sup_origin, sup_r) + } + RegionResolutionError::ConcreteFailure( + SubregionOrigin::Subtype(box TypeTrace { cause, .. }), + sub_r, + sup_r, + ) if **sub_r == RegionKind::ReStatic => { + // 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.ident, + ); + err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime)); + err.span_label( + cause.span, + &format!( + "...is captured and required to live as long as `'static` here \ + because of an implicit lifetime bound on the {}", + match ctxt.assoc_item.container { + AssocItemContainer::TraitContainer(id) => + 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) { + err.emit(); + return Some(ErrorReported); + } 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, + ); + err.span_label(param.param_ty_span, &format!("this data with {}...", lifetime)); + debug!("try_report_static_impl_trait: param_info={:?}", param); + + // We try to make the output have fewer overlapping spans if possible. + if (sp == sup_origin.span() || !return_sp.overlaps(sup_origin.span())) + && sup_origin.span() != return_sp + { + // FIXME: account for `async fn` like in `async-await/issues/issue-62097.rs` + + // Customize the spans and labels depending on their relative order so + // that split sentences flow correctly. + if sup_origin.span().overlaps(return_sp) && sp == sup_origin.span() { + // Avoid the following: + // + // error: cannot infer an appropriate lifetime + // --> $DIR/must_outlive_least_region_or_bound.rs:18:50 + // | + // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) } + // | ---- ---------^- + // + // and instead show: + // + // error: cannot infer an appropriate lifetime + // --> $DIR/must_outlive_least_region_or_bound.rs:18:50 + // | + // LL | fn foo(x: &i32) -> Box<dyn Debug> { Box::new(x) } + // | ---- ^ + err.span_label( + sup_origin.span(), + "...is captured here, requiring it to live as long as `'static`", + ); + } else { + err.span_label(sup_origin.span(), "...is captured here..."); + if return_sp < sup_origin.span() { + err.span_note( + return_sp, + "...and is required to live as long as `'static` here", + ); + } else { + err.span_label( + return_sp, + "...and is required to live as long as `'static` here", + ); + } + } + } else { + err.span_label( + return_sp, + "...is captured and required to live as long as `'static` here", + ); + } + + 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 { + if 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()`. + if self.find_impl_on_dyn_trait(&mut err, param.param_ty, &ctxt) { + override_error_code = Some(ctxt.assoc_item.ident); + } + } + } + if let SubregionOrigin::Subtype(box TypeTrace { cause, .. }) = &sub_origin { + let code = match &cause.code { + ObligationCauseCode::MatchImpl(parent, ..) => &**parent, + _ => &cause.code, + }; + if let ObligationCauseCode::ItemObligation(item_def_id) = *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) + { + if self.suggest_constrain_dyn_trait_in_impl(&mut err, &v.0, ident, self_ty) { + override_error_code = Some(ident); + } + } + } + } + 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, + )); + } + + 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 arg = match param.param.pat.simple_ident() { + Some(simple_ident) => format!("argument `{}`", simple_ident), + None => "the argument".to_string(), + }; + let explicit = format!("you can add an explicit `{}` lifetime bound", lifetime_name); + let explicit_static = format!("explicit `'static` bound to the lifetime of {}", arg); + let captures = format!("captures data from {}", 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 opaque = if let ItemKind::OpaqueTy(opaque) = &item.kind { + opaque + } else { + err.emit(); + return Some(ErrorReported); + }; + + if let Some(span) = opaque + .bounds + .iter() + .filter_map(|arg| match arg { + GenericBound::Outlives(Lifetime { + name: LifetimeName::Static, + span, + .. + }) => Some(*span), + _ => None, + }) + .next() + { + err.span_suggestion_verbose( + span, + &format!("{} `impl Trait`'s {}", consider, explicit_static), + lifetime_name.clone(), + Applicability::MaybeIncorrect, + ); + err.span_suggestion_verbose( + param.param_ty_span, + add_static_bound, + param.param_ty.to_string(), + 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.clone(), + 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.clone(), + 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> + '_`. + err.span_suggestion_verbose( + lt.span, + &format!("{} trait object's {}", consider, explicit_static), + lifetime_name.clone(), + Applicability::MaybeIncorrect, + ); + err.span_suggestion_verbose( + param.param_ty_span, + add_static_bound, + param.param_ty.to_string(), + Applicability::MaybeIncorrect, + ); + } + _ => {} + }, + _ => {} + } + } + err.emit(); + Some(ErrorReported) + } + + 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(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 parent_id = tcx.hir().get_parent_item(trait_item.hir_id()); + match tcx.hir().find(parent_id) { + 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 = tcx.hir().local_def_id(parent_id).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 DiagnosticBuilder<'_>, + ty: Ty<'_>, + ctxt: &UnifyReceiverContext<'tcx>, + ) -> bool { + let tcx = self.tcx(); + + // Find the method being called. + let instance = match ty::Instance::resolve( + tcx, + ctxt.param_env, + ctxt.assoc_item.def_id, + self.infcx.resolve_vars_if_possible(ctxt.substs), + ) { + Ok(Some(instance)) => instance, + _ => 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 (ident, self_ty) = + match self.get_impl_ident_and_self_ty_from_trait(instance.def_id(), &v.0) { + Some((ident, self_ty)) => (ident, self_ty), + None => 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 DiagnosticBuilder<'_>, + 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".to_string(), + ); + multi_span.push_span_label( + ident.span, + "calling this method introduces the `impl`'s 'static` requirement".to_string(), + ); + 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", + " + '_".to_string(), + Applicability::MaybeIncorrect, + ); + suggested = true; + } + } + suggested + } +} + +/// Collect all the trait objects in a type that could have received an implicit `'static` lifetime. +pub(super) struct TraitObjectVisitor(pub(super) FxHashSet<DefId>); + +impl TypeVisitor<'_> for TraitObjectVisitor { + fn visit_ty(&mut self, t: Ty<'_>) -> ControlFlow<Self::BreakTy> { + match t.kind() { + ty::Dynamic(preds, RegionKind::ReStatic) => { + 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(super) struct HirTraitObjectVisitor<'a>(pub(super) &'a mut Vec<Span>, pub(super) DefId); + +impl<'a, 'tcx> Visitor<'tcx> for HirTraitObjectVisitor<'a> { + type Map = ErasedMap<'tcx>; + + fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> { + NestedVisitorMap::None + } + + 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 00000000000..61c8113d052 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/trait_impl_difference.rs @@ -0,0 +1,151 @@ +//! 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, ValuePairs}; +use crate::traits::ObligationCauseCode::CompareImplMethodObligation; +use rustc_errors::ErrorReported; +use rustc_hir as hir; +use rustc_hir::def::Res; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit::Visitor; +use rustc_middle::ty::error::ExpectedFound; +use rustc_middle::ty::{self, Ty, TyCtxt}; +use rustc_span::{MultiSpan, Span}; + +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<ErrorReported> { + if let Some(ref error) = self.error { + debug!("try_report_impl_not_conforming_to_trait {:?}", error); + if let RegionResolutionError::SubSupConflict( + _, + var_origin, + sub_origin, + _sub, + sup_origin, + _sup, + ) = error.clone() + { + if let (&Subtype(ref sup_trace), &Subtype(ref sub_trace)) = + (&sup_origin, &sub_origin) + { + if let ( + ValuePairs::Types(sub_expected_found), + ValuePairs::Types(sup_expected_found), + CompareImplMethodObligation { trait_item_def_id, .. }, + ) = (&sub_trace.values, &sup_trace.values, &sub_trace.cause.code) + { + if sup_expected_found == sub_expected_found { + self.emit_err( + var_origin.span(), + sub_expected_found.expected, + sub_expected_found.found, + *trait_item_def_id, + ); + return Some(ErrorReported); + } + } + } + } + } + None + } + + fn emit_err(&self, sp: Span, expected: Ty<'tcx>, found: Ty<'tcx>, trait_def_id: DefId) { + 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"); + 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".to_string(), + ); + } + + if let Some((expected, found)) = + self.infcx.expected_found_str_ty(ExpectedFound { expected, found }) + { + // Highlighted the differences when showing the "expected/found" note. + err.note_expected_found(&"", expected, &"", found); + } else { + // This fallback shouldn't be necessary, but let's keep it in just in case. + 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 Visitor<'tcx> for TypeParamSpanVisitor<'tcx> { + type Map = rustc_middle::hir::map::Map<'tcx>; + + fn nested_visit_map(&mut self) -> hir::intravisit::NestedVisitorMap<Self::Map> { + hir::intravisit::NestedVisitorMap::OnlyBodies(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(_, _) | 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 00000000000..8dcdd4b149e --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -0,0 +1,184 @@ +//! Helper functions corresponding to lifetime errors due to +//! anonymous regions. + +use crate::infer::error_reporting::nice_region_error::NiceRegionError; +use rustc_hir as hir; +use rustc_hir::def_id::LocalDefId; +use rustc_middle::ty::{self, DefIdTree, Region, Ty}; +use rustc_span::Span; + +/// Information about the anonymous region we are searching for. +#[derive(Debug)] +pub(super) 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, +} + +impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { + // 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. + pub(super) fn find_param_with_region( + &self, + anon_region: Region<'tcx>, + replace_region: Region<'tcx>, + ) -> Option<AnonymousParamInfo<'_>> { + let (id, bound_region) = match *anon_region { + ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region), + ty::ReEarlyBound(ebr) => ( + self.tcx().parent(ebr.def_id).unwrap(), + ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name), + ), + _ => return None, // not a free region + }; + + let hir = &self.tcx().hir(); + let hir_id = hir.local_def_id_to_hir_id(id.as_local()?); + let body_id = hir.maybe_body_owned_by(hir_id)?; + let body = hir.body(body_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 = self.tcx().fn_sig(id); + let fn_sig = self.tcx().liberate_late_bound_regions(id, poly_fn_sig); + 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 = self.tcx().fold_regions(ty, &mut false, |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 + } + }) + } + + pub(super) fn future_return_type( + &self, + local_def_id: LocalDefId, + ) -> Option<&rustc_hir::Ty<'_>> { + if let Some(hir::IsAsync::Async) = self.asyncness(local_def_id) { + if let rustc_middle::ty::Opaque(def_id, _) = + self.tcx().type_of(local_def_id).fn_sig(self.tcx()).output().skip_binder().kind() + { + match self.tcx().hir().get_if_local(*def_id) { + Some(hir::Node::Item(hir::Item { + kind: + hir::ItemKind::OpaqueTy(hir::OpaqueTy { + bounds, + origin: hir::OpaqueTyOrigin::AsyncFn, + .. + }), + .. + })) => { + for b in bounds.iter() { + if let hir::GenericBound::LangItemTrait( + hir::LangItem::Future, + _span, + _hir_id, + generic_args, + ) = b + { + for type_binding in generic_args.bindings.iter() { + if type_binding.ident.name == rustc_span::sym::Output { + if let hir::TypeBindingKind::Equality { ty } = + type_binding.kind + { + return Some(ty); + } + } + } + } + } + } + _ => {} + } + } + } + None + } + + pub(super) fn asyncness(&self, local_def_id: LocalDefId) -> Option<hir::IsAsync> { + // similar to the asyncness fn in rustc_ty_utils::ty + let hir_id = self.tcx().hir().local_def_id_to_hir_id(local_def_id); + let node = self.tcx().hir().get(hir_id); + let fn_like = rustc_middle::hir::map::blocks::FnLikeNode::from_node(node)?; + + Some(fn_like.asyncness()) + } + + // Here, we check for the case where the anonymous region + // is in the return type. + // FIXME(#42703) - Need to handle certain cases here. + pub(super) fn is_return_type_anon( + &self, + scope_def_id: LocalDefId, + br: ty::BoundRegionKind, + decl: &hir::FnDecl<'_>, + ) -> Option<Span> { + let ret_ty = self.tcx().type_of(scope_def_id); + if let ty::FnDef(_, _) = ret_ty.kind() { + let sig = ret_ty.fn_sig(self.tcx()); + let late_bound_regions = + self.tcx().collect_referenced_late_bound_regions(&sig.output()); + if late_bound_regions.iter().any(|r| *r == br) { + return Some(decl.output.span()); + } + } + None + } + + // 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 00000000000..4bc59a4baf5 --- /dev/null +++ b/compiler/rustc_infer/src/infer/error_reporting/note.rs @@ -0,0 +1,396 @@ +use crate::infer::error_reporting::{note_and_explain_region, ObligationCauseExt}; +use crate::infer::{self, InferCtxt, SubregionOrigin}; +use rustc_errors::{struct_span_err, DiagnosticBuilder}; +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 DiagnosticBuilder<'_>, + origin: &SubregionOrigin<'tcx>, + ) { + let mut label_or_note = |span, msg| { + 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::CallReturn(span) => { + label_or_note(span, "...so that return value is valid for the call"); + } + 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::CompareImplMethodObligation { span, .. } => { + label_or_note( + span, + "...so that the definition in impl matches the definition from the trait", + ); + } + } + } + + pub(super) fn report_concrete_failure( + &self, + origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) -> DiagnosticBuilder<'tcx> { + 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::CallReturn(span) => { + let mut err = struct_span_err!( + self.tcx.sess, + span, + E0482, + "lifetime of return value does not outlive the function call" + ); + note_and_explain_region( + self.tcx, + &mut err, + "the return value is only valid for ", + sup, + "", + 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::CompareImplMethodObligation { + span, + item_name, + impl_item_def_id, + trait_item_def_id, + } => self.report_extra_impl_obligation( + span, + item_name, + impl_item_def_id, + trait_item_def_id, + &format!("`{}: {}`", sup, sub), + ), + } + } + + pub(super) fn report_placeholder_failure( + &self, + placeholder_origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) -> DiagnosticBuilder<'tcx> { + // 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 00000000000..728dc2de370 --- /dev/null +++ b/compiler/rustc_infer/src/infer/free_regions.rs @@ -0,0 +1,160 @@ +//! 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_hir::def_id::DefId; +use rustc_middle::ty::{self, 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 struct RegionRelations<'a, 'tcx> { + pub tcx: TyCtxt<'tcx>, + + /// The context used for debug messages + pub context: DefId, + + /// Free-region relationships. + pub free_regions: &'a FreeRegionMap<'tcx>, +} + +impl<'a, 'tcx> RegionRelations<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'tcx>, context: DefId, free_regions: &'a FreeRegionMap<'tcx>) -> Self { + Self { tcx, context, 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() + } + + 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 self.is_free_or_static(sub) && self.is_free(sup) { + 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`. + /// + /// Also available through the `FreeRegionRelations` trait below. + pub fn sub_free_regions( + &self, + tcx: TyCtxt<'tcx>, + r_a: Region<'tcx>, + r_b: Region<'tcx>, + ) -> bool { + assert!(self.is_free_or_static(r_a) && self.is_free_or_static(r_b)); + 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) + } + + /// True for free regions other than `'static`. + pub fn is_free(&self, r: Region<'_>) -> bool { + matches!(r, ty::ReEarlyBound(_) | ty::ReFree(_)) + } + + /// True if `r` is a free region or static of the sort that this + /// free region map can be used with. + pub fn is_free_or_static(&self, r: Region<'_>) -> bool { + match *r { + ty::ReStatic => true, + _ => self.is_free(r), + } + } + + /// 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!(self.is_free(r_a)); + assert!(self.is_free(r_b)); + 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 + } +} + +/// The NLL region handling code represents free region relations in a +/// slightly different way; this trait allows functions to be abstract +/// over which version is in use. +pub trait FreeRegionRelations<'tcx> { + /// Tests whether `r_a <= r_b`. Both must be free regions or + /// `'static`. + fn sub_free_regions( + &self, + tcx: TyCtxt<'tcx>, + shorter: ty::Region<'tcx>, + longer: ty::Region<'tcx>, + ) -> bool; +} + +impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> { + fn sub_free_regions(&self, tcx: TyCtxt<'tcx>, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool { + // invoke the "inherent method" + self.sub_free_regions(tcx, r_a, r_b) + } +} + +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 00000000000..4af1bdf97a7 --- /dev/null +++ b/compiler/rustc_infer/src/infer/freshen.rs @@ -0,0 +1,266 @@ +//! 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 rustc_middle::ty::fold::TypeFolder; +use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable}; + +use rustc_data_structures::fx::FxHashMap; + +use std::collections::hash_map::Entry; + +use super::unify_key::ToType; +use super::InferCtxt; + +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>, &'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<&'tcx ty::Const<'tcx>>, + key: ty::InferConst<'tcx>, + freshener: F, + ty: Ty<'tcx>, + ) -> &'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: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + match ct.val { + ty::ConstKind::Infer(ty::InferConst::Var(v)) => { + let opt_ct = self + .infcx + .inner + .borrow_mut() + .const_unification_table() + .probe_value(v) + .val + .known(); + return 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, + ); + } + return 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 00000000000..c292b2bdb30 --- /dev/null +++ b/compiler/rustc_infer/src/infer/fudge.rs @@ -0,0 +1,249 @@ +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; +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. + pub fn fudge_inference_if_ok<T, E, F>(&self, f: F) -> Result<T, E> + where + F: FnOnce() -> Result<T, E>, + T: TypeFoldable<'tcx>, + { + debug!("fudge_inference_if_ok()"); + + 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.index - self.type_vars.0.start.index) 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 { + if 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: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + if let ty::Const { val: ty::ConstKind::Infer(ty::InferConst::Var(vid)), ty } = ct { + 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(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 00000000000..60f02b84aa3 --- /dev/null +++ b/compiler/rustc_infer/src/infer/glb.rs @@ -0,0 +1,126 @@ +use super::combine::CombineFields; +use super::lattice::{self, LatticeDir}; +use super::InferCtxt; +use super::Subtype; + +use crate::infer::combine::ConstEquateRelation; +use crate::traits::ObligationCause; +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 TypeRelation<'tcx> for Glb<'combine, 'infcx, '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 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: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'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); + + // 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) + } +} + +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 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(()) + } +} + +impl<'tcx> ConstEquateRelation<'tcx> for Glb<'_, '_, 'tcx> { + fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx 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 00000000000..533d0ef7e6c --- /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 00000000000..d460222df8a --- /dev/null +++ b/compiler/rustc_infer/src/infer/higher_ranked/mod.rs @@ -0,0 +1,150 @@ +//! 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::relate::{Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, Binder, TypeFoldable}; + +impl<'a, 'tcx> CombineFields<'a, 'tcx> { + pub fn higher_ranked_sub<T>( + &mut self, + a: Binder<'tcx, T>, + b: Binder<'tcx, T>, + a_is_expected: bool, + ) -> RelateResult<'tcx, Binder<'tcx, T>> + where + T: Relate<'tcx>, + { + debug!("higher_ranked_sub(a={:?}, b={:?})", a, b); + + // Rather than checking the subtype relationship between `a` and `b` + // as-is, we need to do some extra work here in order to make sure + // that function subtyping works correctly with respect to regions + // + // Note: this is a subtle algorithm. For a full explanation, please see + // the rustc dev guide: + // <https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/placeholders_and_universes.html> + + let span = self.trace.cause.span; + + self.infcx.commit_if_ok(|_| { + // First, we instantiate each bound region in the supertype with a + // fresh placeholder region. + let b_prime = self.infcx.replace_bound_vars_with_placeholders(b); + + // 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 (a_prime, _) = + self.infcx.replace_bound_vars_with_fresh_vars(span, HigherRankedType, a); + + debug!("a_prime={:?}", a_prime); + debug!("b_prime={:?}", b_prime); + + // Compare types now that bound regions have been replaced. + let result = self.sub(a_is_expected).relate(a_prime, b_prime)?; + + debug!("higher_ranked_sub: OK result={:?}", result); + + // We related `a_prime` and `b_prime`, which just had any bound vars + // replaced with placeholders or infer vars, respectively. Relating + // them should not introduce new bound vars. + Ok(ty::Binder::dummy(result)) + }) + } +} + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + /// Replaces all regions (resp. types) bound by `binder` with placeholder + /// regions (resp. types) and return a map indicating which bound-region + /// placeholder region. This is the first step of checking subtyping + /// when higher-ranked things are involved. + /// + /// **Important:** You have to be careful to not leak these placeholders, + /// for more information about how placeholders and HRTBs work, see + /// the [rustc dev guide]. + /// + /// [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/traits/hrtb.html + pub fn replace_bound_vars_with_placeholders<T>(&self, binder: ty::Binder<'tcx, T>) -> T + where + T: TypeFoldable<'tcx>, + { + // Figure out what the next universe will be, but don't actually create + // it until after we've done the substitution (in particular there may + // be no bound variables). This is a performance optimization, since the + // leak check for example can be skipped if no new universes are created + // (i.e., if there are no placeholders). + let next_universe = self.universe().next_universe(); + + let fld_r = |br: ty::BoundRegion| { + self.tcx.mk_region(ty::RePlaceholder(ty::PlaceholderRegion { + universe: next_universe, + name: br.kind, + })) + }; + + let fld_t = |bound_ty: ty::BoundTy| { + self.tcx.mk_ty(ty::Placeholder(ty::PlaceholderType { + universe: next_universe, + name: bound_ty.var, + })) + }; + + let fld_c = |bound_var: ty::BoundVar, ty| { + self.tcx.mk_const(ty::Const { + val: ty::ConstKind::Placeholder(ty::PlaceholderConst { + universe: next_universe, + name: ty::BoundConst { var: bound_var, ty }, + }), + ty, + }) + }; + + let (result, map) = self.tcx.replace_bound_vars(binder, fld_r, fld_t, fld_c); + + // If there were higher-ranked regions to replace, then actually create + // the next universe (this avoids needlessly creating universes). + if !map.is_empty() { + let n_u = self.create_next_universe(); + assert_eq!(n_u, next_universe); + } + + debug!( + "replace_bound_vars_with_placeholders(\ + next_universe={:?}, \ + result={:?}, \ + map={:?})", + next_universe, result, map, + ); + + result + } + + /// See `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.debugging_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 00000000000..c47d4769637 --- /dev/null +++ b/compiler/rustc_infer/src/infer/lattice.rs @@ -0,0 +1,99 @@ +//! # Lattice Variables +//! +//! This file contains generic code for operating on inference variables +//! that are characterized by an upper- and lower-bound. The logic and +//! reasoning is explained in detail in the large comment in `infer.rs`. +//! +//! The code in here 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, who knows. +//! In some cases, the functions are also generic with respect to the +//! operation on the lattice (GLB vs LUB). +//! +//! Although all the functions are generic, we generally write the +//! comments in a way that is specific to type variables and the LUB +//! operation. It's just easier that way. +//! +//! In general all of the functions are defined parametrically +//! over a `LatticeValue`, which is a value defined with respect to +//! a lattice. + +use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use super::InferCtxt; + +use crate::traits::ObligationCause; +use rustc_middle::ty::relate::{RelateResult, TypeRelation}; +use rustc_middle::ty::TyVar; +use rustc_middle::ty::{self, Ty}; + +pub trait LatticeDir<'f, 'tcx>: TypeRelation<'tcx> { + fn infcx(&self) -> &'f InferCtxt<'f, 'tcx>; + + fn cause(&self) -> &ObligationCause<'tcx>; + + // 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, ()>; +} + +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!("{}.lattice_tys({:?}, {:?})", this.tag(), a, b); + + 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 partiular, 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) + } + + _ => 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 00000000000..0a7da8c8063 --- /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 00000000000..869fd225d51 --- /dev/null +++ b/compiler/rustc_infer/src/infer/lexical_region_resolve/mod.rs @@ -0,0 +1,1020 @@ +//! Lexical region resolution. + +use crate::infer::region_constraints::Constraint; +use crate::infer::region_constraints::GenericKind; +use crate::infer::region_constraints::MemberConstraint; +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::RegionckMode; +use crate::infer::SubregionOrigin; +use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::graph::implementation::{ + Direction, Graph, NodeIndex, INCOMING, OUTGOING, +}; +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; + +/// 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. +pub fn resolve<'tcx>( + region_rels: &RegionRelations<'_, 'tcx>, + var_infos: VarInfos, + data: RegionConstraintData<'tcx>, + mode: RegionckMode, +) -> (LexicalRegionResolutions<'tcx>, Vec<RegionResolutionError<'tcx>>) { + debug!("RegionConstraintData: resolve_regions()"); + let mut errors = vec![]; + let mut resolver = LexicalResolver { region_rels, var_infos, data }; + match mode { + RegionckMode::Solve => { + let values = resolver.infer_variable_values(&mut errors); + (values, errors) + } + RegionckMode::Erase { suppress_errors: false } => { + // Do real inference to get errors, then erase the results. + let mut values = resolver.infer_variable_values(&mut errors); + let re_erased = region_rels.tcx.lifetimes.re_erased; + + values.values.iter_mut().for_each(|v| match *v { + VarValue::Value(ref mut r) => *r = re_erased, + VarValue::ErrorValue => {} + }); + (values, errors) + } + RegionckMode::Erase { suppress_errors: true } => { + // Skip region inference entirely. + (resolver.erased_data(region_rels.tcx), Vec::new()) + } + } +} + +/// Contains the result of lexical region resolution. Offers methods +/// to lookup up the final value of a region variable. +pub struct LexicalRegionResolutions<'tcx> { + values: IndexVec<RegionVid, VarValue<'tcx>>, + error_region: ty::Region<'tcx>, +} + +#[derive(Copy, Clone, Debug)] +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>, + ), + + /// 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` + ), + + /// Indicates a failure of a `MemberConstraint`. These arise during + /// impl trait processing explicitly -- basically, the impl trait's hidden type + /// included some region that it was not supposed to. + MemberConstraintFailure { span: Span, hidden_ty: Ty<'tcx>, member_region: Region<'tcx> }, +} + +struct RegionAndOrigin<'tcx> { + region: Region<'tcx>, + origin: SubregionOrigin<'tcx>, +} + +type RegionGraph<'tcx> = Graph<(), Constraint<'tcx>>; + +struct LexicalResolver<'cx, '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()); + + // Dorky hack to cause `dump_constraints` to only get called + // if debug mode is enabled: + debug!( + "----() End constraint listing (context={:?}) {:?}---", + self.region_rels.context, + self.dump_constraints(self.region_rels) + ); + + let graph = self.construct_graph(); + self.expand_givens(&graph); + loop { + self.expansion(&mut var_data); + if !self.enforce_member_constraints(&graph, &mut var_data) { + break; + } + } + 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 { + error_region: tcx.lifetimes.re_static, + 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(), + ), + } + } + + /// An erased version of the lexical region resolutions. Used when we're + /// erasing regions and suppressing errors: in item bodies with + /// `-Zborrowck=mir`. + fn erased_data(&self, tcx: TyCtxt<'tcx>) -> LexicalRegionResolutions<'tcx> { + LexicalRegionResolutions { + error_region: tcx.lifetimes.re_static, + values: IndexVec::from_elem_n( + VarValue::Value(tcx.lifetimes.re_erased), + self.num_vars(), + ), + } + } + + fn dump_constraints(&self, free_regions: &RegionRelations<'_, 'tcx>) { + debug!("----() Start constraint listing (context={:?}) ()----", free_regions.context); + 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)); + } + } + } + } + + /// Enforce all member constraints and return true if anything + /// changed. See `enforce_member_constraint` for more details. + fn enforce_member_constraints( + &self, + graph: &RegionGraph<'tcx>, + var_values: &mut LexicalRegionResolutions<'tcx>, + ) -> bool { + // Note: we don't use the `any` combinator because we don't + // want to stop at the first constraint that makes a change. + let mut any_changed = false; + for member_constraint in &self.data.member_constraints { + any_changed |= self.enforce_member_constraint(graph, member_constraint, var_values); + } + any_changed + } + + /// Enforce a constraint like + /// + /// ``` + /// 'r member of ['c...] + /// ``` + /// + /// We look for all choice regions from the list `'c...` that: + /// + /// (a) are greater than the current value of `'r` (which is a lower bound) + /// + /// and + /// + /// (b) are compatible with the upper bounds of `'r` that we can + /// find by traversing the graph. + /// + /// From that list, we look for a *minimal* option `'c_min`. If we + /// find one, then we can enforce that `'r: 'c_min`. + fn enforce_member_constraint( + &self, + graph: &RegionGraph<'tcx>, + member_constraint: &MemberConstraint<'tcx>, + var_values: &mut LexicalRegionResolutions<'tcx>, + ) -> bool { + debug!("enforce_member_constraint(member_constraint={:#?})", member_constraint); + + // The constraint is some inference variable (`vid`) which + // must be equal to one of the options. + let member_vid = match member_constraint.member_region { + ty::ReVar(vid) => *vid, + _ => return false, + }; + + // The current value of `vid` is a lower bound LB -- i.e., we + // know that `LB <= vid` must be true. + let member_lower_bound: ty::Region<'tcx> = match var_values.value(member_vid) { + VarValue::ErrorValue => return false, + VarValue::Value(r) => r, + }; + + // Find all the "upper bounds" -- that is, each region `b` such that + // `r0 <= b` must hold. + let (member_upper_bounds, ..) = + self.collect_bounding_regions(graph, member_vid, OUTGOING, None); + + // Get an iterator over the *available choice* -- that is, + // each choice region `c` where `lb <= c` and `c <= ub` for all the + // upper bounds `ub`. + debug!("enforce_member_constraint: upper_bounds={:#?}", member_upper_bounds); + let mut options = member_constraint.choice_regions.iter().filter(|option| { + self.sub_concrete_regions(member_lower_bound, option) + && member_upper_bounds + .iter() + .all(|upper_bound| self.sub_concrete_regions(option, upper_bound.region)) + }); + + // If there is more than one option, we only make a choice if + // there is a single *least* choice -- i.e., some available + // region that is `<=` all the others. + let mut least_choice: ty::Region<'tcx> = match options.next() { + Some(&r) => r, + None => return false, + }; + debug!("enforce_member_constraint: least_choice={:?}", least_choice); + for &option in options { + debug!("enforce_member_constraint: option={:?}", option); + if !self.sub_concrete_regions(least_choice, option) { + if self.sub_concrete_regions(option, least_choice) { + debug!("enforce_member_constraint: new least choice"); + least_choice = option; + } else { + debug!("enforce_member_constraint: no least choice"); + return false; + } + } + } + + // (#72087) Different `ty::Regions` can be known to be equal, for + // example, we know that `'a` and `'static` are equal in a function + // with a parameter of type `&'static &'a ()`. + // + // When we have two equal regions like this `expansion` will use + // `lub_concrete_regions` to pick a canonical representative. The same + // choice is needed here so that we don't end up in a cycle of + // `expansion` changing the region one way and the code here changing + // it back. + let lub = self.lub_concrete_regions(least_choice, member_lower_bound); + debug!( + "enforce_member_constraint: final least choice = {:?}\nlub = {:?}", + least_choice, lub + ); + if lub != member_lower_bound { + *var_values.value_mut(member_vid) = VarValue::Value(least_choice); + true + } else { + false + } + } + + 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(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 a_region = match *var_values.value(a_vid) { + VarValue::ErrorValue => return false, + VarValue::Value(a_region) => a_region, + }; + 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(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 { + if *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 { + if 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. + 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 = self.region_rels.free_regions.is_free_or_static(b); + 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 = self.region_rels.free_regions.is_free_or_static(a); + 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"). + 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(_), r @ (ReEarlyBound(_) | ReFree(_))) + | (r @ (ReEarlyBound(_) | ReFree(_)), &ReEmpty(_)) => { + // All empty regions are less than early-bound, free, + // and scope regions. + r + } + + (&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. + fn collect_errors( + &self, + var_data: &mut LexicalRegionResolutions<'tcx>, + errors: &mut Vec<RegionResolutionError<'tcx>>, + ) { + for (constraint, origin) in &self.data.constraints { + debug!("collect_errors: constraint={:?} origin={:?}", 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!( + "collect_errors: 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 a_region = match *a_data { + VarValue::ErrorValue => continue, + VarValue::Value(a_region) => a_region, + }; + + // 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!( + "collect_errors: region error at {:?}: \ + cannot verify that {:?}={:?} <= {:?}", + origin, a_vid, a_region, b_region + ); + *a_data = VarValue::ErrorValue; + } + } + } + } + + // Check that all member constraints are satisfied. + for member_constraint in &self.data.member_constraints { + let member_region = var_data.normalize(self.tcx(), member_constraint.member_region); + let choice_regions = member_constraint + .choice_regions + .iter() + .map(|&choice_region| var_data.normalize(self.tcx(), choice_region)); + if !choice_regions.clone().any(|choice_region| member_region == choice_region) { + let span = self.tcx().def_span(member_constraint.opaque_type_def_id); + errors.push(RegionResolutionError::MemberConstraintFailure { + span, + hidden_ty: member_constraint.hidden_ty, + member_region, + }); + } + } + + 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 + self.collect_error_for_expanding_node(graph, &mut dup_vec, node_vid, errors); + } + } + } + } + + 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>>, + ) { + // 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, + )); + 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(k, b) => { + (var_values.normalize(self.region_rels.tcx, *k) == generic_ty) + && self.bound_is_met(b, var_values, generic_ty, min) + } + + 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, &mut false, |r, _db| match r { + ty::ReVar(rid) => self.resolve_var(*rid), + _ => 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 fn resolve_var(&self, rid: RegionVid) -> ty::Region<'tcx> { + let result = match self.values[rid] { + VarValue::Value(r) => r, + VarValue::ErrorValue => self.error_region, + }; + debug!("resolve_var({:?}) = {:?}", rid, 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 00000000000..a08323535c5 --- /dev/null +++ b/compiler/rustc_infer/src/infer/lub.rs @@ -0,0 +1,126 @@ +use super::combine::CombineFields; +use super::lattice::{self, LatticeDir}; +use super::InferCtxt; +use super::Subtype; + +use crate::infer::combine::ConstEquateRelation; +use crate::traits::ObligationCause; +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 TypeRelation<'tcx> for Lub<'combine, 'infcx, '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 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: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'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); + + // 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) + } +} + +impl<'tcx> ConstEquateRelation<'tcx> for Lub<'_, '_, 'tcx> { + fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx 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 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(()) + } +} diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs new file mode 100644 index 00000000000..f0d63f512fc --- /dev/null +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -0,0 +1,1768 @@ +pub use self::freshen::TypeFreshener; +pub use self::LateBoundRegionConversionTime::*; +pub use self::RegionVariableOrigin::*; +pub use self::SubregionOrigin::*; +pub use self::ValuePairs::*; + +pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog}; + +use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine}; + +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; +use rustc_hir as hir; +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::EvalToConstValueResult; +use rustc_middle::traits::select; +use rustc_middle::ty::error::{ExpectedFound, TypeError, UnconstrainedNumeric}; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder}; +use rustc_middle::ty::relate::RelateResult; +use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef}; +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_session::config::BorrowckMode; +use rustc_span::symbol::Symbol; +use rustc_span::Span; + +use std::cell::{Cell, Ref, RefCell}; +use std::collections::BTreeMap; +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 outlives; +pub mod region_constraints; +pub mod resolve; +mod sub; +pub mod type_variable; +mod undo_log; + +use crate::infer::canonical::OriginalQueryValues; +pub use rustc_middle::infer::unify_key; + +#[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>>, +>; + +/// How we should handle region solving. +/// +/// This is used so that the region values inferred by HIR region solving are +/// not exposed, and so that we can avoid doing work in HIR typeck that MIR +/// typeck will also do. +#[derive(Copy, Clone, Debug)] +pub enum RegionckMode { + /// The default mode: report region errors, don't erase regions. + Solve, + /// Erase the results of region after solving. + Erase { + /// A flag that is used to suppress region errors, when we are doing + /// region checks that the NLL borrow checker will also do -- it might + /// be set to true. + suppress_errors: bool, + }, +} + +impl Default for RegionckMode { + fn default() -> Self { + RegionckMode::Solve + } +} + +impl RegionckMode { + /// Indicates that the MIR borrowck will repeat these region + /// checks, so we should ignore errors if NLL is (unconditionally) + /// enabled. + pub fn for_item_body(tcx: TyCtxt<'_>) -> Self { + // FIXME(Centril): Once we actually remove `::Migrate` also make + // this always `true` and then proceed to eliminate the dead code. + match tcx.borrowck_mode() { + // If we're on Migrate mode, report AST region errors + BorrowckMode::Migrate => RegionckMode::Erase { suppress_errors: false }, + + // If we're on MIR, don't report AST region errors as they should be reported by NLL + BorrowckMode::Mir => RegionckMode::Erase { suppress_errors: true }, + } + } +} + +/// 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`. +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 + /// + /// 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 + /// `process_region_obligations` (defined in `self::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<(hir::HirId, RegionObligation<'tcx>)>, + + undo_log: InferCtxtUndoLogs<'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![], + } + } + + #[inline] + pub fn region_obligations(&self) -> &[(hir::HirId, 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] + 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) + } +} + +pub struct InferCtxt<'a, 'tcx> { + pub tcx: TyCtxt<'tcx>, + + /// 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 + /// `tained_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)] +pub enum ValuePairs<'tcx> { + Types(ExpectedFound<Ty<'tcx>>), + Regions(ExpectedFound<ty::Region<'tcx>>), + Consts(ExpectedFound<&'tcx ty::Const<'tcx>>), + TraitRefs(ExpectedFound<ty::TraitRef<'tcx>>), + PolyTraitRefs(ExpectedFound<ty::PolyTraitRef<'tcx>>), +} + +/// 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> { + cause: ObligationCause<'tcx>, + 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), + + /// Region in return type of invoked fn must enclose call + CallReturn(Span), + + /// Comparing the signature and requirements of an impl method against + /// the containing trait. + CompareImplMethodObligation { + span: Span, + item_name: Symbol, + impl_item_def_id: DefId, + 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, ty::AssocItem), + + /// 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), + + /// The variable we create to represent `'empty(U0)`. + RootEmptyRegion, + + 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"), + } + } +} + +/// Helper type of a temporary returned by `tcx.infer_ctxt()`. +/// Necessary because we can't write the following bound: +/// `F: for<'b, 'tcx> where 'tcx FnOnce(InferCtxt<'b, 'tcx>)`. +pub struct InferCtxtBuilder<'tcx> { + tcx: TyCtxt<'tcx>, + fresh_typeck_results: Option<RefCell<ty::TypeckResults<'tcx>>>, +} + +pub trait TyCtxtInferExt<'tcx> { + fn infer_ctxt(self) -> InferCtxtBuilder<'tcx>; +} + +impl TyCtxtInferExt<'tcx> for TyCtxt<'tcx> { + fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> { + InferCtxtBuilder { tcx: self, 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`. + 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 + } + + /// 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, ref fresh_typeck_results } = *self; + let in_progress_typeck_results = fresh_typeck_results.as_ref(); + f(InferCtxt { + tcx, + 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; + for obligation in obligations { + fulfill_cx.register_predicate_obligation(infcx, obligation); + } + 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> { + 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()) + } + + pub fn type_var_diverges(&'a self, ty: Ty<'_>) -> bool { + match *ty.kind() { + ty::Infer(ty::TyVar(vid)) => self.inner.borrow_mut().type_variables().var_diverges(vid), + _ => false, + } + } + + 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 type_is_unconstrained_numeric(&'a self, ty: Ty<'_>) -> UnconstrainedNumeric { + use rustc_middle::ty::error::UnconstrainedNumeric::Neither; + use rustc_middle::ty::error::UnconstrainedNumeric::{UnconstrainedFloat, UnconstrainedInt}; + match *ty.kind() { + ty::Infer(ty::IntVar(vid)) => { + if self.inner.borrow_mut().int_unification_table().probe_value(vid).is_some() { + Neither + } else { + UnconstrainedInt + } + } + ty::Infer(ty::FloatVar(vid)) => { + if self.inner.borrow_mut().float_unification_table().probe_value(vid).is_some() { + Neither + } else { + UnconstrainedFloat + } + } + _ => Neither, + } + } + + 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>, + ) -> CombineFields<'a, 'tcx> { + CombineFields { + infcx: self, + trace, + cause: None, + param_env, + obligations: PredicateObligations::new(), + } + } + + /// 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()), + } + } + + fn rollback_to(&self, cause: &str, snapshot: CombinedSnapshot<'a, 'tcx>) { + debug!("rollback_to(cause={})", cause); + 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); + } + + fn commit_from(&self, snapshot: CombinedSnapshot<'a, 'tcx>) { + debug!("commit_from()"); + 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); + } + + /// Executes `f` and commit the bindings. + pub fn commit_unconditionally<R, F>(&self, f: F) -> R + where + F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R, + { + debug!("commit_unconditionally()"); + let snapshot = self.start_snapshot(); + let r = f(&snapshot); + self.commit_from(snapshot); + r + } + + /// Execute `f` and commit the bindings if closure `f` returns `Ok(_)`. + pub fn commit_if_ok<T, E, F>(&self, f: F) -> Result<T, E> + where + F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> Result<T, E>, + { + debug!("commit_if_ok()"); + 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. + pub fn probe<R, F>(&self, f: F) -> R + where + F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R, + { + debug!("probe()"); + 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. + pub fn probe_maybe_skip_leak_check<R, F>(&self, should_skip: bool, f: F) -> R + where + F: FnOnce(&CombinedSnapshot<'a, 'tcx>) -> R, + { + debug!("probe()"); + 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 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. + }) + }) + } + + pub fn sub_regions( + &self, + origin: SubregionOrigin<'tcx>, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) { + debug!("sub_regions({:?} <: {:?})", a, b); + 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`. + pub fn member_constraint( + &self, + opaque_type_def_id: DefId, + definition_span: Span, + hidden_ty: Ty<'tcx>, + region: ty::Region<'tcx>, + in_regions: &Lrc<Vec<ty::Region<'tcx>>>, + ) { + debug!("member_constraint({:?} <: {:?})", region, in_regions); + self.inner.borrow_mut().unwrap_region_constraints().member_constraint( + opaque_type_def_id, + definition_span, + hidden_ty, + region, + in_regions, + ); + } + + pub fn subtype_predicate( + &self, + cause: &ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + predicate: ty::PolySubtypePredicate<'tcx>, + ) -> Option<InferResult<'tcx, ()>> { + // Subtle: it's ok to skip the binder here and resolve because + // `shallow_resolve` just ignores anything that is not a type + // variable, and because type variable's can't (at present, at + // least) capture any of the things bound by this binder. + // + // NOTE(nmatsakis): really, there is no *particular* reason to do this + // `shallow_resolve` here except as a micro-optimization. + // Naturally I could not resist. + let two_unbound_type_vars = { + let a = self.shallow_resolve(predicate.skip_binder().a); + let b = self.shallow_resolve(predicate.skip_binder().b); + a.is_ty_var() && b.is_ty_var() + }; + + if two_unbound_type_vars { + // Two unbound type variables? Can't make progress. + 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>, + ) -> UnitResult<'tcx> { + self.commit_if_ok(|_snapshot| { + 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` + Ok(()) + }) + } + + pub fn next_ty_var_id(&self, diverging: bool, origin: TypeVariableOrigin) -> TyVid { + self.inner.borrow_mut().type_variables().new_var(self.universe(), diverging, origin) + } + + pub fn next_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> { + self.tcx.mk_ty_var(self.next_ty_var_id(false, origin)) + } + + pub fn next_ty_var_in_universe( + &self, + origin: TypeVariableOrigin, + universe: ty::UniverseIndex, + ) -> Ty<'tcx> { + let vid = self.inner.borrow_mut().type_variables().new_var(universe, false, origin); + self.tcx.mk_ty_var(vid) + } + + pub fn next_diverging_ty_var(&self, origin: TypeVariableOrigin) -> Ty<'tcx> { + self.tcx.mk_ty_var(self.next_ty_var_id(true, origin)) + } + + pub fn next_const_var( + &self, + ty: Ty<'tcx>, + origin: ConstVariableOrigin, + ) -> &'tcx 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, + ) -> &'tcx 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. + 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(), + false, + 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) + } + + /// 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`. + pub fn resolve_regions_and_report_errors( + &self, + region_context: DefId, + outlives_env: &OutlivesEnvironment<'tcx>, + mode: RegionckMode, + ) { + 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, region_context, outlives_env.free_region_map()); + + let (lexical_region_resolutions, errors) = + lexical_region_resolve::resolve(region_rels, var_infos, data, mode); + + let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions)); + assert!(old_value.is_none()); + + 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(&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()) + } + + /// 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) + } + + /// 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: TypeFoldable<'tcx>, + { + value.visit_with(&mut resolve::UnresolvedTypeFinder::new(self)).break_value() + } + + pub fn probe_const_var( + &self, + vid: ty::ConstVid<'tcx>, + ) -> Result<&'tcx 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> + where + M: FnOnce(String) -> DiagnosticBuilder<'tcx>, + { + let actual_ty = self.resolve_vars_if_possible(actual_ty); + debug!("type_error_struct_with_diag({:?}, {:?})", sp, actual_ty); + + // Don't report an error if actual type is `Error`. + if actual_ty.references_error() { + return self.tcx.sess.diagnostic().struct_dummy(); + } + + mk_diag(self.ty_to_string(actual_ty)) + } + + pub fn report_mismatched_types( + &self, + cause: &ObligationCause<'tcx>, + expected: Ty<'tcx>, + actual: Ty<'tcx>, + err: TypeError<'tcx>, + ) -> DiagnosticBuilder<'tcx> { + 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: &'tcx ty::Const<'tcx>, + actual: &'tcx ty::Const<'tcx>, + err: TypeError<'tcx>, + ) -> DiagnosticBuilder<'tcx> { + 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, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>) + where + T: TypeFoldable<'tcx>, + { + let fld_r = + |br: ty::BoundRegion| self.next_region_var(LateBoundRegion(span, br.kind, lbrct)); + let fld_t = |_| { + self.next_ty_var(TypeVariableOrigin { + kind: TypeVariableOriginKind::MiscVariable, + span, + }) + }; + let fld_c = |_, ty| { + self.next_const_var( + ty, + ConstVariableOrigin { kind: ConstVariableOriginKind::MiscVariable, span }, + ) + }; + self.tcx.replace_bound_vars(value, fld_r, fld_t, fld_c) + } + + /// 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(); + } + + 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 + } + + /// 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. + pub fn const_eval_resolve( + &self, + param_env: ty::ParamEnv<'tcx>, + ty::Unevaluated { def, substs, promoted }: ty::Unevaluated<'tcx>, + span: Option<Span>, + ) -> EvalToConstValueResult<'tcx> { + let mut original_values = OriginalQueryValues::default(); + let canonical = self.canonicalize_query((param_env, substs), &mut original_values); + + let (param_env, substs) = canonical.value; + // 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(param_env, ty::Unevaluated { def, substs, promoted }, span) + } + + /// If `typ` 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. + // FIXME(eddyb) inline into `ShallowResolver::visit_ty`. + fn shallow_resolve_ty(&self, typ: Ty<'tcx>) -> Ty<'tcx> { + match *typ.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.inner.borrow_mut().type_variables().probe(v).known(); + known.map_or(typ, |t| self.shallow_resolve_ty(t)) + } + + ty::Infer(ty::IntVar(v)) => self + .inner + .borrow_mut() + .int_unification_table() + .probe_value(v) + .map(|v| v.to_type(self.tcx)) + .unwrap_or(typ), + + ty::Infer(ty::FloatVar(v)) => self + .inner + .borrow_mut() + .float_unification_table() + .probe_value(v) + .map(|v| v.to_type(self.tcx)) + .unwrap_or(typ), + + _ => typ, + } + } + + /// `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 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: &'tcx ty::Const<'tcx>) -> Option<Self> { + match ct.val { + ty::ConstKind::Infer(InferConst::Var(v)) => Some(TyOrConstInferVar::Const(v)), + _ => None, + } + } +} + +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 + } + + fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + self.infcx.shallow_resolve_ty(ty) + } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + if let ty::Const { val: ty::ConstKind::Infer(InferConst::Var(vid)), .. } = ct { + 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: Types(ExpectedFound::new(a_is_expected, a, b)) } + } + + pub fn consts( + cause: &ObligationCause<'tcx>, + a_is_expected: bool, + a: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> TypeTrace<'tcx> { + TypeTrace { cause: cause.clone(), values: Consts(ExpectedFound::new(a_is_expected, a, b)) } + } +} + +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, + CallReturn(a) => a, + CompareImplMethodObligation { span, .. } => 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::CompareImplMethodObligation { + item_name, + impl_item_def_id, + trait_item_def_id, + } => SubregionOrigin::CompareImplMethodObligation { + span: cause.span, + item_name, + impl_item_def_id, + trait_item_def_id, + }, + + _ => 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 + ) + } +} 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 00000000000..20be06adfd0 --- /dev/null +++ b/compiler/rustc_infer/src/infer/nll_relate/mod.rs @@ -0,0 +1,1035 @@ +//! 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 rustc_data_structures::fx::FxHashMap; +use rustc_middle::ty::error::TypeError; +use rustc_middle::ty::fold::{TypeFoldable, TypeVisitor}; +use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation}; +use rustc_middle::ty::{self, InferConst, Ty, TyCtxt}; +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>; + + /// 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: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'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 crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; + 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, + }); + 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 a (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 a 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<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 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<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 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<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<D> TypeRelation<'tcx> for TypeRelating<'me, '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 + } + + fn relate_with_variance<T: Relate<'tcx>>( + &mut self, + variance: ty::Variance, + info: ty::VarianceDiagInfo<'tcx>, + a: T, + b: T, + ) -> RelateResult<'tcx, T> { + debug!("relate_with_variance(variance={:?}, a={:?}, b={:?})", variance, a, b); + + let old_ambient_variance = self.ambient_variance; + self.ambient_variance = self.ambient_variance.xform(variance); + self.ambient_variance_info = self.ambient_variance_info.clone().xform(info); + + debug!("relate_with_variance: ambient_variance = {:?}", self.ambient_variance); + + let r = self.relate(a, b)?; + + self.ambient_variance = old_ambient_variance; + + debug!("relate_with_variance: r={:?}", r); + + Ok(r) + } + + fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + 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 lazilly + // 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::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!("tys(a={:?}, b={:?}, variance={:?})", a, b, self.ambient_variance); + + // Will also handle unification of `IntVar` and `FloatVar`. + self.infcx.super_combine_tys(self, a, b) + } + } + } + + fn regions( + &mut self, + a: ty::Region<'tcx>, + b: ty::Region<'tcx>, + ) -> RelateResult<'tcx, ty::Region<'tcx>> { + debug!("regions(a={:?}, b={:?}, variance={:?})", a, b, 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!("regions: v_a = {:?}", v_a); + debug!("regions: v_b = {:?}", v_b); + + if self.ambient_covariance() { + // Covariance: a <= b. Hence, `b: a`. + self.push_outlives(v_b, v_a, self.ambient_variance_info.clone()); + } + + if self.ambient_contravariance() { + // Contravariant: b <= a. Hence, `a: b`. + self.push_outlives(v_a, v_b, self.ambient_variance_info.clone()); + } + + Ok(a) + } + + fn consts( + &mut self, + a: &'tcx ty::Const<'tcx>, + mut b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + let a = self.infcx.shallow_resolve(a); + + if !D::forbid_inference_vars() { + b = self.infcx.shallow_resolve(b); + } + + match b.val { + ty::ConstKind::Infer(InferConst::Var(_)) if D::forbid_inference_vars() => { + // Forbid inference variables in the RHS. + bug!("unexpected inference var {:?}", b) + } + // FIXME(invariance): see the related FIXME above. + _ => self.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>, + { + // 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!("binders({:?}: {:?}, ambient_variance={:?})", a, b, 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!("binders: a_scope = {:?} (existential)", a_scope); + debug!("binders: b_scope = {:?} (universal)", b_scope); + + 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!("binders: a_scope = {:?} (universal)", a_scope); + debug!("binders: b_scope = {:?} (existential)", b_scope); + + 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: &'tcx ty::Const<'tcx>, b: &'tcx 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: TypeFoldable<'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 relative 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<D> TypeRelation<'tcx> for TypeGeneralizer<'me, '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, false, 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 { + if *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: &'tcx ty::Const<'tcx>, + _: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'tcx ty::Const<'tcx>> { + match a.val { + 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/outlives/env.rs b/compiler/rustc_infer/src/infer/outlives/env.rs new file mode 100644 index 00000000000..9e04773c5fa --- /dev/null +++ b/compiler/rustc_infer/src/infer/outlives/env.rs @@ -0,0 +1,195 @@ +use crate::infer::free_regions::FreeRegionMap; +use crate::infer::{GenericKind, InferCtxt}; +use crate::traits::query::OutlivesBound; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir as hir; +use rustc_middle::ty; + +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, for each body B that we are checking (that is, the fn + // item, but also any nested closures), the set of implied region + // bounds that are in scope in that particular body. + // + // Example: + // + // ``` + // fn foo<'a, 'b, T>(x: &'a T, y: &'b ()) { + // bar(x, y, |y: &'b T| { .. } // body B1) + // } // body B0 + // ``` + // + // Here, for 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, 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. + // + // We collect this map as we descend the tree. We then use the + // results when proving outlives obligations like `T: 'x` later + // (e.g., if `T: 'x` must be proven within the body B1, then we + // know it is true if either `'a: 'x` or `'b: 'x`). + region_bound_pairs_map: FxHashMap<hir::HirId, RegionBoundPairs<'tcx>>, + + // Used to compute `region_bound_pairs_map`: contains the set of + // in-scope region-bound pairs thus far. + region_bound_pairs_accum: 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> = Vec<(ty::Region<'tcx>, GenericKind<'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_map: Default::default(), + region_bound_pairs_accum: vec![], + }; + + 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 value of the `region_bound_pairs`. + pub fn region_bound_pairs_map(&self) -> &FxHashMap<hir::HirId, RegionBoundPairs<'tcx>> { + &self.region_bound_pairs_map + } + + /// This is a hack to support the old-skool regionck, which + /// processes region constraints from the main function and the + /// closure together. In that context, when we enter a closure, we + /// want to be able to "save" the state of the surrounding a + /// function. We can then add implied bounds and the like from the + /// closure arguments into the environment -- these should only + /// apply in the closure body, so once we exit, we invoke + /// `pop_snapshot_post_closure` to remove them. + /// + /// Example: + /// + /// ``` + /// fn foo<T>() { + /// callback(for<'a> |x: &'a T| { + /// // ^^^^^^^ not legal syntax, but probably should be + /// // within this closure body, `T: 'a` holds + /// }) + /// } + /// ``` + /// + /// This "containment" of closure's effects only works so well. In + /// particular, we (intentionally) leak relationships between free + /// regions that are created by the closure's bounds. The case + /// where this is useful is when you have (e.g.) a closure with a + /// signature like `for<'a, 'b> fn(x: &'a &'b u32)` -- in this + /// case, we want to keep the relationship `'b: 'a` in the + /// free-region-map, so that later if we have to take `LUB('b, + /// 'a)` we can get the result `'b`. + /// + /// I have opted to keep **all modifications** to the + /// free-region-map, however, and not just those that concern free + /// variables bound in the closure. The latter seems more correct, + /// but it is not the existing behavior, and I could not find a + /// case where the existing behavior went wrong. In any case, it + /// seems like it'd be readily fixed if we wanted. There are + /// similar leaks around givens that seem equally suspicious, to + /// be honest. --nmatsakis + pub fn push_snapshot_pre_closure(&self) -> usize { + self.region_bound_pairs_accum.len() + } + + /// See `push_snapshot_pre_closure`. + pub fn pop_snapshot_post_closure(&mut self, len: usize) { + self.region_bound_pairs_accum.truncate(len); + } + + /// Save the current set of region-bound pairs under the given `body_id`. + pub fn save_implied_bounds(&mut self, body_id: hir::HirId) { + let old = + self.region_bound_pairs_map.insert(body_id, self.region_bound_pairs_accum.clone()); + assert!(old.is_none()); + } + + /// 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::RegionSubRegion( + r_a @ (&ty::ReEarlyBound(_) | &ty::ReFree(_)), + &ty::ReVar(vid_b), + ) => { + infcx.expect("no infcx provided but region vars found").add_given(r_a, vid_b); + } + OutlivesBound::RegionSubParam(r_a, param_b) => { + self.region_bound_pairs_accum.push((r_a, GenericKind::Param(param_b))); + } + OutlivesBound::RegionSubProjection(r_a, projection_b) => { + self.region_bound_pairs_accum + .push((r_a, GenericKind::Projection(projection_b))); + } + OutlivesBound::RegionSubRegion(r_a, r_b) => { + // 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 00000000000..07c75d50d91 --- /dev/null +++ b/compiler/rustc_infer/src/infer/outlives/mod.rs @@ -0,0 +1,34 @@ +//! Various code related to computing outlives relations. + +pub mod env; +pub mod obligations; +pub mod verify; + +use rustc_middle::traits::query::OutlivesBound; +use rustc_middle::ty; + +pub fn explicit_outlives_bounds<'tcx>( + param_env: ty::ParamEnv<'tcx>, +) -> impl Iterator<Item = OutlivesBound<'tcx>> + 'tcx { + debug!("explicit_outlives_bounds()"); + 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::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 00000000000..437083c68dc --- /dev/null +++ b/compiler/rustc_infer/src/infer/outlives/obligations.rs @@ -0,0 +1,468 @@ +//! Code that handles "type-outlives" constraints like `T: 'a`. This +//! is based on the `push_outlives_components` function defined on the tcx, +//! 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::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_middle::ty::outlives::Component; +use rustc_middle::ty::subst::GenericArgKind; +use rustc_middle::ty::{self, Region, Ty, TyCtxt, TypeFoldable}; + +use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::undo_log::UndoLogs; +use rustc_hir as hir; +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). + pub fn register_region_obligation( + &self, + body_id: hir::HirId, + obligation: RegionObligation<'tcx>, + ) { + debug!("register_region_obligation(body_id={:?}, obligation={:?})", body_id, obligation); + + let mut inner = self.inner.borrow_mut(); + inner.undo_log.push(UndoLog::PushRegionObligation); + inner.region_obligations.push((body_id, 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( + cause.body_id, + 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<(hir::HirId, RegionObligation<'tcx>)> { + std::mem::take(&mut self.inner.borrow_mut().region_obligations) + } + + /// 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`: 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`. + /// - `implicit_region_bound`: if some, this is a region bound + /// that is considered to hold for all type parameters (the + /// function body). + /// - `param_env` is the parameter environment for the enclosing function. + /// - `body_id` is the body-id whose region obligations are being + /// processed. + /// + /// # Returns + /// + /// This function may have to perform normalizations, and hence it + /// returns an `InferOk` with subobligations that must be + /// processed. + pub fn process_registered_region_obligations( + &self, + region_bound_pairs_map: &FxHashMap<hir::HirId, RegionBoundPairs<'tcx>>, + implicit_region_bound: Option<ty::Region<'tcx>>, + param_env: ty::ParamEnv<'tcx>, + ) { + assert!( + !self.in_snapshot.get(), + "cannot process registered region obligations in a snapshot" + ); + + debug!("process_registered_region_obligations()"); + + let my_region_obligations = self.take_registered_region_obligations(); + + for (body_id, 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); + + if let Some(region_bound_pairs) = region_bound_pairs_map.get(&body_id) { + let outlives = &mut TypeOutlives::new( + self, + self.tcx, + ®ion_bound_pairs, + implicit_region_bound, + param_env, + ); + outlives.type_must_outlive(origin, sup_type, sub_region); + } else { + self.tcx.sess.delay_span_bug( + origin.span(), + &format!("no region-bound-pairs for {:?}", body_id), + ) + } + } + } +} + +/// 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![]; + self.tcx.push_outlives_components(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); + } + + fn projection_must_outlive( + &mut self, + origin: infer::SubregionOrigin<'tcx>, + region: ty::Region<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + ) { + debug!( + "projection_must_outlive(region={:?}, projection_ty={:?}, origin={:?})", + region, projection_ty, origin + ); + + // 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(); + + // 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 deducable 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| 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() + .chain(approx_env_bounds.iter().map(|b| &b.1)) + .all(|b| *b == 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); + 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/verify.rs b/compiler/rustc_infer/src/infer/outlives/verify.rs new file mode 100644 index 00000000000..f69212c599b --- /dev/null +++ b/compiler/rustc_infer/src/infer/outlives/verify.rs @@ -0,0 +1,355 @@ +use crate::infer::outlives::env::RegionBoundPairs; +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, GenericArgKind, Subst}; +use rustc_middle::ty::{self, Ty, TyCtxt}; + +/// 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>, + 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 type_bound( + &self, + ty: Ty<'tcx>, + visited: &mut SsoHashSet<GenericArg<'tcx>>, + ) -> VerifyBound<'tcx> { + match *ty.kind() { + ty::Param(p) => self.param_bound(p), + ty::Projection(data) => self.projection_bound(data, visited), + ty::FnDef(_, substs) => { + // HACK(eddyb) ignore lifetimes found shallowly in `substs`. + // This is inconsistent with `ty::Adt` (including all substs), + // but consistent with previous (accidental) behavior. + // See https://github.com/rust-lang/rust/issues/70917 + // for further background and discussion. + let mut bounds = substs + .iter() + .filter_map(|child| match child.unpack() { + GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)), + GenericArgKind::Lifetime(_) => None, + GenericArgKind::Const(_) => Some(self.recursive_bound(child, 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(), + ), + } + } + _ => self.recursive_bound(ty.into(), 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 + let param_bounds = self + .declared_generic_bounds_from_env(GenericKind::Param(param_ty)) + .into_iter() + .map(|outlives| outlives.1); + + // Add in the default bound of fn body that applies to all in + // scope type parameters: + let param_bounds = param_bounds.chain(self.implicit_region_bound); + + let any_bounds: Vec<_> = param_bounds.map(|r| VerifyBound::OutlivedBy(r)).collect(); + + if any_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 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(any_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::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_with_compare_fn(|ty| { + if let ty::Projection(..) = ty.kind() { + let erased_ty = self.tcx.erase_regions(ty); + erased_ty == erased_projection_ty + } else { + false + } + }) + } + + /// 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(|ty::OutlivesPredicate(ty, r)| { + let vb = VerifyBound::OutlivedBy(r); + if ty == projection_ty_as_ty { + // Micro-optimize if this is an exact match (this + // occurs often when there are no region variables + // involved). + vb + } else { + VerifyBound::IfEq(ty, Box::new(vb)) + } + }); + + // 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 ty = self.tcx.mk_projection(projection_ty.item_def_id, projection_ty.substs); + let recursive_bound = self.recursive_bound(ty.into(), visited); + + VerifyBound::AnyBound(env_bounds.chain(trait_bounds).collect()).or(recursive_bound) + } + + fn recursive_bound( + &self, + parent: GenericArg<'tcx>, + visited: &mut SsoHashSet<GenericArg<'tcx>>, + ) -> VerifyBound<'tcx> { + let mut bounds = parent + .walk_shallow(visited) + .filter_map(|child| match child.unpack() { + GenericArgKind::Type(ty) => Some(self.type_bound(ty, visited)), + GenericArgKind::Lifetime(lt) => { + // Ignore late-bound regions. + if !lt.is_late_bound() { Some(VerifyBound::OutlivedBy(lt)) } else { None } + } + GenericArgKind::Const(_) => Some(self.recursive_bound(child, 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()) + } + } + } + + /// 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, + generic: GenericKind<'tcx>, + ) -> Vec<ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> { + let generic_ty = generic.to_ty(self.tcx); + self.declared_generic_bounds_from_env_with_compare_fn(|ty| ty == generic_ty) + } + + fn declared_generic_bounds_from_env_with_compare_fn( + &self, + compare_ty: impl Fn(Ty<'tcx>) -> bool, + ) -> Vec<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. Comparing using `==` is a bit + // dubious for projections, but it will work for simple cases + // like `T` and `T::Item`. It may not work as well for things + // like `<T as Foo<'a>>::Item`. + let c_b = self.param_env.caller_bounds(); + let param_bounds = self.collect_outlives_from_predicate_list(&compare_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 be lower-generic by `'a`, but we + // don't know that this holds from first principles. + let from_region_bound_pairs = self.region_bound_pairs.iter().filter_map(|&(r, p)| { + debug!( + "declared_generic_bounds_from_env_with_compare_fn: region_bound_pair = {:?}", + (r, p) + ); + let p_ty = p.to_ty(tcx); + compare_ty(p_ty).then_some(ty::OutlivesPredicate(p_ty, r)) + }); + + param_bounds + .chain(from_region_bound_pairs) + .inspect(|bound| { + debug!( + "declared_generic_bounds_from_env_with_compare_fn: 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| 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: + /// + /// ```rust + /// 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, + compare_ty: impl Fn(Ty<'tcx>) -> bool, + predicates: impl Iterator<Item = ty::Predicate<'tcx>>, + ) -> impl Iterator<Item = ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>> { + predicates + .filter_map(|p| p.to_opt_type_outlives()) + .filter_map(|p| p.no_bound_vars()) + .filter(move |p| compare_ty(p.0)) + } +} 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 00000000000..0231dd06677 --- /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 00000000000..2d4c1e5d050 --- /dev/null +++ b/compiler/rustc_infer/src/infer/region_constraints/leak_check.rs @@ -0,0 +1,446 @@ +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 solveable. + /// + /// # 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, + 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 succssor `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_placholder[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 00000000000..7f4c33c5792 --- /dev/null +++ b/compiler/rustc_infer/src/infer/region_constraints/mod.rs @@ -0,0 +1,812 @@ +//! 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::sync::Lrc; +use rustc_data_structures::undo_log::UndoLogs; +use rustc_data_structures::unify as ut; +use rustc_hir::def_id::DefId; +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(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 constriant, 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 std::ops::Deref for RegionConstraintCollector<'_, 'tcx> { + type Target = RegionConstraintStorage<'tcx>; + #[inline] + fn deref(&self) -> &RegionConstraintStorage<'tcx> { + self.storage + } +} + +impl 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)] +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: +/// +/// 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: +/// +/// fn(min: Region) -> bool { +/// ('a: min) || ('b: min) +/// } +/// +/// This is described with a `AnyRegion('a, 'b)` node. +#[derive(Debug, Clone)] +pub enum VerifyBound<'tcx> { + /// Given a kind K and a bound B, expands to a function like the + /// following, where `G` is the generic for which this verify + /// bound was created: + /// + /// ```rust + /// fn(min) -> bool { + /// if G == K { + /// B(min) + /// } else { + /// false + /// } + /// } + /// ``` + /// + /// In other words, if the generic `G` that we are checking is + /// equal to `K`, then check the associated verify bound + /// (otherwise, false). + /// + /// This is used when we have something in the environment that + /// may or may not be relevant, depending on the region inference + /// results. For example, we may have `where <T as + /// Trait<'a>>::Item: 'b` in our where-clauses. If we are + /// generating the verify-bound for `<T as Trait<'0>>::Item`, then + /// this where-clause is only relevant if `'0` winds up inferred + /// to `'a`. + /// + /// So we would compile to a verify-bound like + /// + /// ``` + /// IfEq(<T as Trait<'a>>::Item, AnyRegion('a)) + /// ``` + /// + /// meaning, if the subject G is equal to `<T as Trait<'a>>::Item` + /// (after inference), and `'a: min`, then `G: min`. + IfEq(Ty<'tcx>, Box<VerifyBound<'tcx>>), + + /// Given a region `R`, expands to the function: + /// + /// ``` + /// 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: + /// + /// ```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: + /// + /// ```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>>), +} + +#[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 + } + + 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 { + if 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) { + (&ty::ReVar(sub), &ty::ReVar(sup)) => { + debug!("make_eqregion: unifying {:?} with {:?}", sub, sup); + self.unification_table().union(sub, sup); + self.any_unifications = true; + } + (&ty::ReVar(vid), value) | (value, &ty::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, + opaque_type_def_id: DefId, + 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 { + opaque_type_def_id, + definition_span, + hidden_ty, + member_region, + choice_regions: choice_regions.clone(), + }); + } + + pub fn make_subregion( + &mut self, + origin: SubregionOrigin<'tcx>, + sub: Region<'tcx>, + sup: Region<'tcx>, + ) { + // cannot add constraints once regions are resolved + debug!( + "RegionConstraintCollector: make_subregion({:?}, {:?}) due to {:?}", + sub, sup, 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); + match (a, b) { + (r @ &ReStatic, _) | (_, r @ &ReStatic) => { + r // nothing lives longer than static + } + + _ if a == b => { + a // LUB(a,a) = a + } + + _ => 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); + match (a, b) { + (&ReStatic, r) | (r, &ReStatic) => { + r // static lives longer than everything else + } + + _ if a == b => { + a // GLB(a,a) = a + } + + _ => 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(ty::ReStatic) => true, + VerifyBound::OutlivedBy(_) => false, + 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(_, b) => b.cannot_hold(), + 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 00000000000..48b8ee17594 --- /dev/null +++ b/compiler/rustc_infer/src/infer/resolve.rs @@ -0,0 +1,250 @@ +use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use super::{FixupError, FixupResult, InferCtxt, Span}; +use rustc_middle::mir; +use rustc_middle::ty::fold::{TypeFolder, TypeVisitor}; +use rustc_middle::ty::{self, Const, InferConst, Ty, TyCtxt, TypeFoldable}; + +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: &'tcx Const<'tcx>) -> &'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 +/// normlizing 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); + self.tcx().reuse_or_mk_region(r, ty::ReVar(resolved)) + } + _ => r, + } + } + + fn fold_const(&mut self, ct: &'tcx ty::Const<'tcx>) -> &'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>, +{ + let mut full_resolver = FullTypeResolver { infcx, err: None }; + let result = value.fold_with(&mut full_resolver); + match full_resolver.err { + None => Ok(result), + Some(e) => Err(e), + } +} + +// 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>, + err: Option<FixupError<'tcx>>, +} + +impl<'a, 'tcx> TypeFolder<'tcx> for FullTypeResolver<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { + self.infcx.tcx + } + + fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { + if !t.needs_infer() { + 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)) => { + self.err = Some(FixupError::UnresolvedTy(vid)); + self.tcx().ty_error() + } + ty::Infer(ty::IntVar(vid)) => { + self.err = Some(FixupError::UnresolvedIntTy(vid)); + self.tcx().ty_error() + } + ty::Infer(ty::FloatVar(vid)) => { + self.err = Some(FixupError::UnresolvedFloatTy(vid)); + self.tcx().ty_error() + } + ty::Infer(_) => { + bug!("Unexpected type in full type resolver: {:?}", t); + } + _ => t.super_fold_with(self), + } + } + } + + fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { + match *r { + ty::ReVar(rid) => self + .infcx + .lexical_region_resolutions + .borrow() + .as_ref() + .expect("region resolution not performed") + .resolve_var(rid), + _ => r, + } + } + + fn fold_const(&mut self, c: &'tcx ty::Const<'tcx>) -> &'tcx ty::Const<'tcx> { + if !c.needs_infer() { + c // micro-optimize -- if there is nothing in this const that this fold affects... + } else { + let c = self.infcx.shallow_resolve(c); + match c.val { + ty::ConstKind::Infer(InferConst::Var(vid)) => { + self.err = Some(FixupError::UnresolvedConst(vid)); + return self.tcx().const_error(c.ty); + } + ty::ConstKind::Infer(InferConst::Fresh(_)) => { + bug!("Unexpected const in full const resolver: {:?}", c); + } + _ => {} + } + c.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 00000000000..b3131936ae0 --- /dev/null +++ b/compiler/rustc_infer/src/infer/sub.rs @@ -0,0 +1,180 @@ +use super::combine::{CombineFields, RelationDir}; +use super::SubregionOrigin; + +use crate::infer::combine::ConstEquateRelation; +use crate::traits::Obligation; +use rustc_middle::ty::fold::TypeFoldable; +use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation}; +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 TypeRelation<'tcx> for Sub<'combine, 'infcx, '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)), + } + } + + fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { + debug!("{}.tys({:?}, {:?})", self.tag(), a, b); + + 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(a_vid)), &ty::Infer(TyVar(b_vid))) => { + // 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. We also + // have to record in the `type_variables` tracker that + // the two variables are equal modulo subtyping, which + // is important to the occurs check later on. + infcx.inner.borrow_mut().type_variables().sub(a_vid, b_vid); + self.fields.obligations.push(Obligation::new( + self.fields.trace.cause.clone(), + self.fields.param_env, + 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()) + } + + _ => { + 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 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: &'tcx ty::Const<'tcx>, + b: &'tcx ty::Const<'tcx>, + ) -> RelateResult<'tcx, &'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) + } +} + +impl<'tcx> ConstEquateRelation<'tcx> for Sub<'_, '_, 'tcx> { + fn const_equate_obligation(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx 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 00000000000..13b78b26af4 --- /dev/null +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -0,0 +1,446 @@ +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. +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), + } + } +} + +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: + /// + /// ?1 <: ?3 + /// Box<?3> <: ?1 + /// + /// This works because `?1` and `?3` are unified in the + /// `sub_relations` relation (not in `eq_relations`). Then when we + /// process the `Box<?3> <: ?1` constraint, we do an occurs check + /// on `Box<?3>` and find a potential cycle. + /// + /// 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`. + 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, + DivergingFn, + LatticeVariable, +} + +pub(crate) struct TypeVariableData { + origin: TypeVariableOrigin, + diverging: bool, +} + +#[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, + } + } +} + +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 diverges flag 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_diverges(&self, vid: ty::TyVid) -> bool { + self.storage.values.get(vid.index as usize).diverging + } + + /// 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.index 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, + diverging: bool, + 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, diverging }); + assert_eq!(eq_key.vid.index, index as u32); + + debug!( + "new_var(index={:?}, universe={:?}, diverging={:?}, origin={:?}", + eq_key.vid, universe, diverging, 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: + /// + /// 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 { index: value_count as u32 }..TyVid { index: self.num_vars() as u32 }; + ( + range.start..range.end, + (range.start.index..range.end.index) + .map(|index| self.storage.values.get(index as usize).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 { index: i as u32 }; + 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> { + 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.index + } + fn from_index(i: u32) -> Self { + TyVidEqKey::from(ty::TyVid { index: 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 00000000000..5ad2519a93c --- /dev/null +++ b/compiler/rustc_infer/src/infer/undo_log.rs @@ -0,0 +1,212 @@ +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; + +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. +pub(crate) enum UndoLog<'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::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. +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 a 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 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] + } +} diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs new file mode 100644 index 00000000000..ee358c52c2f --- /dev/null +++ b/compiler/rustc_infer/src/lib.rs @@ -0,0 +1,38 @@ +//! This crates defines the type inference engine. +//! +//! - **Type inference.** The type inference code can be found in the `infer` module; +//! this code handles low-level equality and subtyping operations. The +//! type check pass in the compiler is found in the `rustc_typeck` crate. +//! +//! For more information about how rustc works, see the [rustc dev guide]. +//! +//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] +#![feature(bool_to_option)] +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(extend_one)] +#![feature(iter_zip)] +#![feature(never_type)] +#![feature(in_band_lifetimes)] +#![feature(control_flow_enum)] +#![feature(min_specialization)] +#![recursion_limit = "512"] // For rustdoc + +#[macro_use] +extern crate rustc_macros; +#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] +#[macro_use] +extern crate rustc_data_structures; +#[macro_use] +extern crate tracing; +#[macro_use] +extern crate rustc_middle; + +pub mod infer; +pub mod traits; diff --git a/compiler/rustc_infer/src/traits/engine.rs b/compiler/rustc_infer/src/traits/engine.rs new file mode 100644 index 00000000000..2710debea94 --- /dev/null +++ b/compiler/rustc_infer/src/traits/engine.rs @@ -0,0 +1,78 @@ +use crate::infer::InferCtxt; +use crate::traits::Obligation; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::{self, ToPredicate, Ty, WithConstness}; + +use super::FulfillmentError; +use super::{ObligationCause, PredicateObligation}; + +pub trait TraitEngine<'tcx>: 'tcx { + fn normalize_projection_type( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + ) -> Ty<'tcx>; + + /// Requires that `ty` must implement the trait with `def_id` in + /// the given environment. This trait must not have any type + /// parameters (except for `Self`). + fn register_bound( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + param_env: ty::ParamEnv<'tcx>, + ty: Ty<'tcx>, + def_id: DefId, + cause: ObligationCause<'tcx>, + ) { + let trait_ref = ty::TraitRef { def_id, substs: infcx.tcx.mk_substs_trait(ty, &[]) }; + self.register_predicate_obligation( + infcx, + Obligation { + cause, + recursion_depth: 0, + param_env, + predicate: trait_ref.without_const().to_predicate(infcx.tcx), + }, + ); + } + + fn register_predicate_obligation( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + obligation: PredicateObligation<'tcx>, + ); + + fn select_all_or_error( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + ) -> Result<(), Vec<FulfillmentError<'tcx>>>; + + fn select_where_possible( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + ) -> Result<(), Vec<FulfillmentError<'tcx>>>; + + fn pending_obligations(&self) -> Vec<PredicateObligation<'tcx>>; +} + +pub trait TraitEngineExt<'tcx> { + fn register_predicate_obligations( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>, + ); +} + +impl<T: ?Sized + TraitEngine<'tcx>> TraitEngineExt<'tcx> for T { + fn register_predicate_obligations( + &mut self, + infcx: &InferCtxt<'_, 'tcx>, + obligations: impl IntoIterator<Item = PredicateObligation<'tcx>>, + ) { + for obligation in obligations { + self.register_predicate_obligation(infcx, obligation); + } + } +} diff --git a/compiler/rustc_infer/src/traits/error_reporting/mod.rs b/compiler/rustc_infer/src/traits/error_reporting/mod.rs new file mode 100644 index 00000000000..0ac4b6b25bb --- /dev/null +++ b/compiler/rustc_infer/src/traits/error_reporting/mod.rs @@ -0,0 +1,114 @@ +use super::ObjectSafetyViolation; + +use crate::infer::InferCtxt; +use rustc_data_structures::fx::FxHashSet; +use rustc_errors::{struct_span_err, DiagnosticBuilder}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_middle::ty::TyCtxt; +use rustc_span::symbol::Symbol; +use rustc_span::{MultiSpan, Span}; +use std::fmt; +use std::iter; + +impl<'a, 'tcx> InferCtxt<'a, 'tcx> { + pub fn report_extra_impl_obligation( + &self, + error_span: Span, + item_name: Symbol, + _impl_item_def_id: DefId, + trait_item_def_id: DefId, + requirement: &dyn fmt::Display, + ) -> DiagnosticBuilder<'tcx> { + let msg = "impl has stricter requirements than trait"; + let sp = self.tcx.sess.source_map().guess_head_span(error_span); + + let mut err = struct_span_err!(self.tcx.sess, sp, E0276, "{}", msg); + + if let Some(trait_item_span) = self.tcx.hir().span_if_local(trait_item_def_id) { + let span = self.tcx.sess.source_map().guess_head_span(trait_item_span); + err.span_label(span, format!("definition of `{}` from trait", item_name)); + } + + err.span_label(sp, format!("impl has extra requirement {}", requirement)); + + err + } +} + +pub fn report_object_safety_error( + tcx: TyCtxt<'tcx>, + span: Span, + trait_def_id: DefId, + violations: &[ObjectSafetyViolation], +) -> DiagnosticBuilder<'tcx> { + let trait_str = tcx.def_path_str(trait_def_id); + let trait_span = tcx.hir().get_if_local(trait_def_id).and_then(|node| match node { + hir::Node::Item(item) => Some(item.ident.span), + _ => None, + }); + let span = tcx.sess.source_map().guess_head_span(span); + let mut err = struct_span_err!( + tcx.sess, + span, + E0038, + "the trait `{}` cannot be made into an object", + trait_str + ); + err.span_label(span, format!("`{}` cannot be made into an object", trait_str)); + + let mut reported_violations = FxHashSet::default(); + let mut multi_span = vec![]; + let mut messages = vec![]; + for violation in violations { + if let ObjectSafetyViolation::SizedSelf(sp) = &violation { + if !sp.is_empty() { + // Do not report `SizedSelf` without spans pointing at `SizedSelf` obligations + // with a `Span`. + reported_violations.insert(ObjectSafetyViolation::SizedSelf(vec![].into())); + } + } + if reported_violations.insert(violation.clone()) { + let spans = violation.spans(); + let msg = if trait_span.is_none() || spans.is_empty() { + format!("the trait cannot be made into an object because {}", violation.error_msg()) + } else { + format!("...because {}", violation.error_msg()) + }; + if spans.is_empty() { + err.note(&msg); + } else { + for span in spans { + multi_span.push(span); + messages.push(msg.clone()); + } + } + if trait_span.is_some() { + // Only provide the help if its a local trait, otherwise it's not actionable. + violation.solution(&mut err); + } + } + } + let has_multi_span = !multi_span.is_empty(); + let mut note_span = MultiSpan::from_spans(multi_span.clone()); + if let (Some(trait_span), true) = (trait_span, has_multi_span) { + note_span + .push_span_label(trait_span, "this trait cannot be made into an object...".to_string()); + } + for (span, msg) in iter::zip(multi_span, messages) { + note_span.push_span_label(span, msg); + } + err.span_note( + note_span, + "for a trait to be \"object safe\" it needs to allow building a vtable to allow the call \ + to be resolvable dynamically; for more information visit \ + <https://doc.rust-lang.org/reference/items/traits.html#object-safety>", + ); + + if tcx.sess.trait_methods_not_found.borrow().iter().any(|full_span| full_span.contains(span)) { + // Avoid emitting error caused by non-existing method (#58734) + err.cancel(); + } + + err +} diff --git a/compiler/rustc_infer/src/traits/mod.rs b/compiler/rustc_infer/src/traits/mod.rs new file mode 100644 index 00000000000..d5c17ede214 --- /dev/null +++ b/compiler/rustc_infer/src/traits/mod.rs @@ -0,0 +1,139 @@ +//! Trait Resolution. See the [rustc-dev-guide] for more information on how this works. +//! +//! [rustc-dev-guide]: https://rustc-dev-guide.rust-lang.org/traits/resolution.html + +mod engine; +pub mod error_reporting; +mod project; +mod structural_impls; +pub mod util; + +use rustc_hir as hir; +use rustc_middle::ty::error::{ExpectedFound, TypeError}; +use rustc_middle::ty::{self, Const, Ty}; +use rustc_span::Span; + +pub use self::FulfillmentErrorCode::*; +pub use self::ImplSource::*; +pub use self::ObligationCauseCode::*; +pub use self::SelectionError::*; + +pub use self::engine::{TraitEngine, TraitEngineExt}; +pub use self::project::MismatchedProjectionTypes; +pub(crate) use self::project::UndoLog; +pub use self::project::{ + Normalized, NormalizedTy, ProjectionCache, ProjectionCacheEntry, ProjectionCacheKey, + ProjectionCacheStorage, Reveal, +}; +pub use rustc_middle::traits::*; + +/// An `Obligation` represents some trait reference (e.g., `i32: Eq`) for +/// which the "impl_source" must be found. The process of finding a "impl_source" is +/// called "resolving" the `Obligation`. This process consists of +/// either identifying an `impl` (e.g., `impl Eq for i32`) that +/// satisfies the obligation, or else finding a bound that is in +/// scope. The eventual result is usually a `Selection` (defined below). +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct Obligation<'tcx, T> { + /// The reason we have to prove this thing. + pub cause: ObligationCause<'tcx>, + + /// The environment in which we should prove this thing. + pub param_env: ty::ParamEnv<'tcx>, + + /// The thing we are trying to prove. + pub predicate: T, + + /// If we started proving this as a result of trying to prove + /// something else, track the total depth to ensure termination. + /// If this goes over a certain threshold, we abort compilation -- + /// in such cases, we can not say whether or not the predicate + /// holds for certain. Stupid halting problem; such a drag. + pub recursion_depth: usize, +} + +pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>; +pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>; + +// `PredicateObligation` 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!(PredicateObligation<'_>, 32); + +pub type PredicateObligations<'tcx> = Vec<PredicateObligation<'tcx>>; + +pub type Selection<'tcx> = ImplSource<'tcx, PredicateObligation<'tcx>>; + +pub struct FulfillmentError<'tcx> { + pub obligation: PredicateObligation<'tcx>, + pub code: FulfillmentErrorCode<'tcx>, + /// Diagnostics only: we opportunistically change the `code.span` when we encounter an + /// obligation error caused by a call argument. When this is the case, we also signal that in + /// this field to ensure accuracy of suggestions. + pub points_at_arg_span: bool, + /// Diagnostics only: the 'root' obligation which resulted in + /// the failure to process `obligation`. This is the obligation + /// that was initially passed to `register_predicate_obligation` + pub root_obligation: PredicateObligation<'tcx>, +} + +#[derive(Clone)] +pub enum FulfillmentErrorCode<'tcx> { + CodeSelectionError(SelectionError<'tcx>), + CodeProjectionError(MismatchedProjectionTypes<'tcx>), + CodeSubtypeError(ExpectedFound<Ty<'tcx>>, TypeError<'tcx>), // always comes from a SubtypePredicate + CodeConstEquateError(ExpectedFound<&'tcx Const<'tcx>>, TypeError<'tcx>), + CodeAmbiguity, +} + +impl<'tcx, O> Obligation<'tcx, O> { + pub fn new( + cause: ObligationCause<'tcx>, + param_env: ty::ParamEnv<'tcx>, + predicate: O, + ) -> Obligation<'tcx, O> { + Obligation { cause, param_env, recursion_depth: 0, predicate } + } + + pub fn with_depth( + cause: ObligationCause<'tcx>, + recursion_depth: usize, + param_env: ty::ParamEnv<'tcx>, + predicate: O, + ) -> Obligation<'tcx, O> { + Obligation { cause, param_env, recursion_depth, predicate } + } + + pub fn misc( + span: Span, + body_id: hir::HirId, + param_env: ty::ParamEnv<'tcx>, + trait_ref: O, + ) -> Obligation<'tcx, O> { + Obligation::new(ObligationCause::misc(span, body_id), param_env, trait_ref) + } + + pub fn with<P>(&self, value: P) -> Obligation<'tcx, P> { + Obligation { + cause: self.cause.clone(), + param_env: self.param_env, + recursion_depth: self.recursion_depth, + predicate: value, + } + } +} + +impl<'tcx> FulfillmentError<'tcx> { + pub fn new( + obligation: PredicateObligation<'tcx>, + code: FulfillmentErrorCode<'tcx>, + root_obligation: PredicateObligation<'tcx>, + ) -> FulfillmentError<'tcx> { + FulfillmentError { obligation, code, points_at_arg_span: false, root_obligation } + } +} + +impl<'tcx> TraitObligation<'tcx> { + pub fn self_ty(&self) -> ty::Binder<'tcx, Ty<'tcx>> { + self.predicate.map_bound(|p| p.self_ty()) + } +} diff --git a/compiler/rustc_infer/src/traits/project.rs b/compiler/rustc_infer/src/traits/project.rs new file mode 100644 index 00000000000..33bddf1dedc --- /dev/null +++ b/compiler/rustc_infer/src/traits/project.rs @@ -0,0 +1,226 @@ +//! Code for projecting associated types out of trait references. + +use super::PredicateObligation; + +use crate::infer::InferCtxtUndoLogs; + +use rustc_data_structures::{ + snapshot_map::{self, SnapshotMapRef, SnapshotMapStorage}, + undo_log::Rollback, +}; +use rustc_middle::ty::{self, Ty}; + +pub use rustc_middle::traits::Reveal; + +pub(crate) type UndoLog<'tcx> = + snapshot_map::UndoLog<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>; + +#[derive(Clone)] +pub struct MismatchedProjectionTypes<'tcx> { + pub err: ty::error::TypeError<'tcx>, +} + +#[derive(Clone, TypeFoldable)] +pub struct Normalized<'tcx, T> { + pub value: T, + pub obligations: Vec<PredicateObligation<'tcx>>, +} + +pub type NormalizedTy<'tcx> = Normalized<'tcx, Ty<'tcx>>; + +impl<'tcx, T> Normalized<'tcx, T> { + pub fn with<U>(self, value: U) -> Normalized<'tcx, U> { + Normalized { value, obligations: self.obligations } + } +} + +// # Cache + +/// The projection cache. Unlike the standard caches, this can include +/// infcx-dependent type variables, therefore we have to roll the +/// cache back each time we roll a snapshot back, to avoid assumptions +/// on yet-unresolved inference variables. Types with placeholder +/// regions also have to be removed when the respective snapshot ends. +/// +/// Because of that, projection cache entries can be "stranded" and left +/// inaccessible when type variables inside the key are resolved. We make no +/// attempt to recover or remove "stranded" entries, but rather let them be +/// (for the lifetime of the infcx). +/// +/// Entries in the projection cache might contain inference variables +/// that will be resolved by obligations on the projection cache entry (e.g., +/// when a type parameter in the associated type is constrained through +/// an "RFC 447" projection on the impl). +/// +/// When working with a fulfillment context, the derived obligations of each +/// projection cache entry will be registered on the fulfillcx, so any users +/// that can wait for a fulfillcx fixed point need not care about this. However, +/// users that don't wait for a fixed point (e.g., trait evaluation) have to +/// resolve the obligations themselves to make sure the projected result is +/// ok and avoid issues like #43132. +/// +/// If that is done, after evaluation the obligations, it is a good idea to +/// call `ProjectionCache::complete` to make sure the obligations won't be +/// re-evaluated and avoid an exponential worst-case. +// +// FIXME: we probably also want some sort of cross-infcx cache here to +// reduce the amount of duplication. Let's see what we get with the Chalk reforms. +pub struct ProjectionCache<'a, 'tcx> { + map: &'a mut SnapshotMapStorage<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, +} + +#[derive(Default)] +pub struct ProjectionCacheStorage<'tcx> { + map: SnapshotMapStorage<ProjectionCacheKey<'tcx>, ProjectionCacheEntry<'tcx>>, +} + +#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] +pub struct ProjectionCacheKey<'tcx> { + ty: ty::ProjectionTy<'tcx>, +} + +impl ProjectionCacheKey<'tcx> { + pub fn new(ty: ty::ProjectionTy<'tcx>) -> Self { + Self { ty } + } +} + +#[derive(Clone, Debug)] +pub enum ProjectionCacheEntry<'tcx> { + InProgress, + Ambiguous, + Recur, + Error, + NormalizedTy(NormalizedTy<'tcx>), +} + +impl<'tcx> ProjectionCacheStorage<'tcx> { + #[inline] + pub(crate) fn with_log<'a>( + &'a mut self, + undo_log: &'a mut InferCtxtUndoLogs<'tcx>, + ) -> ProjectionCache<'a, 'tcx> { + ProjectionCache { map: &mut self.map, undo_log } + } +} + +impl<'tcx> ProjectionCache<'_, 'tcx> { + #[inline] + fn map( + &mut self, + ) -> SnapshotMapRef< + '_, + ProjectionCacheKey<'tcx>, + ProjectionCacheEntry<'tcx>, + InferCtxtUndoLogs<'tcx>, + > { + self.map.with_log(self.undo_log) + } + + pub fn clear(&mut self) { + self.map().clear(); + } + + /// Try to start normalize `key`; returns an error if + /// normalization already occurred (this error corresponds to a + /// cache hit, so it's actually a good thing). + pub fn try_start( + &mut self, + key: ProjectionCacheKey<'tcx>, + ) -> Result<(), ProjectionCacheEntry<'tcx>> { + let mut map = self.map(); + if let Some(entry) = map.get(&key) { + return Err(entry.clone()); + } + + map.insert(key, ProjectionCacheEntry::InProgress); + Ok(()) + } + + /// Indicates that `key` was normalized to `value`. + pub fn insert_ty(&mut self, key: ProjectionCacheKey<'tcx>, value: NormalizedTy<'tcx>) { + debug!( + "ProjectionCacheEntry::insert_ty: adding cache entry: key={:?}, value={:?}", + key, value + ); + let mut map = self.map(); + if let Some(ProjectionCacheEntry::Recur) = map.get(&key) { + debug!("Not overwriting Recur"); + return; + } + let fresh_key = map.insert(key, ProjectionCacheEntry::NormalizedTy(value)); + assert!(!fresh_key, "never started projecting `{:?}`", key); + } + + /// Mark the relevant projection cache key as having its derived obligations + /// complete, so they won't have to be re-computed (this is OK to do in a + /// snapshot - if the snapshot is rolled back, the obligations will be + /// marked as incomplete again). + pub fn complete(&mut self, key: ProjectionCacheKey<'tcx>) { + let mut map = self.map(); + let ty = match map.get(&key) { + Some(&ProjectionCacheEntry::NormalizedTy(ref ty)) => { + debug!("ProjectionCacheEntry::complete({:?}) - completing {:?}", key, ty); + ty.value + } + ref value => { + // Type inference could "strand behind" old cache entries. Leave + // them alone for now. + debug!("ProjectionCacheEntry::complete({:?}) - ignoring {:?}", key, value); + return; + } + }; + + map.insert( + key, + ProjectionCacheEntry::NormalizedTy(Normalized { value: ty, obligations: vec![] }), + ); + } + + /// A specialized version of `complete` for when the key's value is known + /// to be a NormalizedTy. + pub fn complete_normalized(&mut self, key: ProjectionCacheKey<'tcx>, ty: &NormalizedTy<'tcx>) { + // We want to insert `ty` with no obligations. If the existing value + // already has no obligations (as is common) we don't insert anything. + if !ty.obligations.is_empty() { + self.map().insert( + key, + ProjectionCacheEntry::NormalizedTy(Normalized { + value: ty.value, + obligations: vec![], + }), + ); + } + } + + /// Indicates that trying to normalize `key` resulted in + /// ambiguity. No point in trying it again then until we gain more + /// type information (in which case, the "fully resolved" key will + /// be different). + pub fn ambiguous(&mut self, key: ProjectionCacheKey<'tcx>) { + let fresh = self.map().insert(key, ProjectionCacheEntry::Ambiguous); + assert!(!fresh, "never started projecting `{:?}`", key); + } + + /// Indicates that while trying to normalize `key`, `key` was required to + /// be normalized again. Selection or evaluation should eventually report + /// an error here. + pub fn recur(&mut self, key: ProjectionCacheKey<'tcx>) { + let fresh = self.map().insert(key, ProjectionCacheEntry::Recur); + assert!(!fresh, "never started projecting `{:?}`", key); + } + + /// Indicates that trying to normalize `key` resulted in + /// error. + pub fn error(&mut self, key: ProjectionCacheKey<'tcx>) { + let fresh = self.map().insert(key, ProjectionCacheEntry::Error); + assert!(!fresh, "never started projecting `{:?}`", key); + } +} + +impl<'tcx> Rollback<UndoLog<'tcx>> for ProjectionCacheStorage<'tcx> { + fn reverse(&mut self, undo: UndoLog<'tcx>) { + self.map.reverse(undo); + } +} diff --git a/compiler/rustc_infer/src/traits/structural_impls.rs b/compiler/rustc_infer/src/traits/structural_impls.rs new file mode 100644 index 00000000000..c4a2ecee096 --- /dev/null +++ b/compiler/rustc_infer/src/traits/structural_impls.rs @@ -0,0 +1,75 @@ +use crate::traits; +use crate::traits::project::Normalized; +use rustc_middle::ty; +use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeVisitor}; + +use std::fmt; +use std::ops::ControlFlow; + +// Structural impls for the structs in `traits`. + +impl<'tcx, T: fmt::Debug> fmt::Debug for Normalized<'tcx, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Normalized({:?}, {:?})", self.value, self.obligations) + } +} + +impl<'tcx, O: fmt::Debug> fmt::Debug for traits::Obligation<'tcx, O> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if ty::tls::with(|tcx| tcx.sess.verbose()) { + write!( + f, + "Obligation(predicate={:?}, cause={:?}, param_env={:?}, depth={})", + self.predicate, self.cause, self.param_env, self.recursion_depth + ) + } else { + write!(f, "Obligation(predicate={:?}, depth={})", self.predicate, self.recursion_depth) + } + } +} + +impl<'tcx> fmt::Debug for traits::FulfillmentError<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "FulfillmentError({:?},{:?})", self.obligation, self.code) + } +} + +impl<'tcx> fmt::Debug for traits::FulfillmentErrorCode<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match *self { + super::CodeSelectionError(ref e) => write!(f, "{:?}", e), + super::CodeProjectionError(ref e) => write!(f, "{:?}", e), + super::CodeSubtypeError(ref a, ref b) => { + write!(f, "CodeSubtypeError({:?}, {:?})", a, b) + } + super::CodeConstEquateError(ref a, ref b) => { + write!(f, "CodeConstEquateError({:?}, {:?})", a, b) + } + super::CodeAmbiguity => write!(f, "Ambiguity"), + } + } +} + +impl<'tcx> fmt::Debug for traits::MismatchedProjectionTypes<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "MismatchedProjectionTypes({:?})", self.err) + } +} + +/////////////////////////////////////////////////////////////////////////// +// TypeFoldable implementations. + +impl<'tcx, O: TypeFoldable<'tcx>> TypeFoldable<'tcx> for traits::Obligation<'tcx, O> { + fn super_fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self { + traits::Obligation { + cause: self.cause, + recursion_depth: self.recursion_depth, + predicate: self.predicate.fold_with(folder), + param_env: self.param_env.fold_with(folder), + } + } + + fn super_visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> { + self.predicate.visit_with(visitor) + } +} diff --git a/compiler/rustc_infer/src/traits/util.rs b/compiler/rustc_infer/src/traits/util.rs new file mode 100644 index 00000000000..ce1445f8a47 --- /dev/null +++ b/compiler/rustc_infer/src/traits/util.rs @@ -0,0 +1,354 @@ +use smallvec::smallvec; + +use crate::traits::{Obligation, ObligationCause, PredicateObligation}; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; +use rustc_middle::ty::outlives::Component; +use rustc_middle::ty::{self, ToPredicate, TyCtxt, WithConstness}; +use rustc_span::symbol::Ident; + +pub fn anonymize_predicate<'tcx>( + tcx: TyCtxt<'tcx>, + pred: ty::Predicate<'tcx>, +) -> ty::Predicate<'tcx> { + let new = tcx.anonymize_late_bound_regions(pred.kind()); + tcx.reuse_or_mk_predicate(pred, new) +} + +pub struct PredicateSet<'tcx> { + tcx: TyCtxt<'tcx>, + set: FxHashSet<ty::Predicate<'tcx>>, +} + +impl PredicateSet<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>) -> Self { + Self { tcx, set: Default::default() } + } + + pub fn insert(&mut self, pred: ty::Predicate<'tcx>) -> bool { + // We have to be careful here because we want + // + // for<'a> Foo<&'a i32> + // + // and + // + // for<'b> Foo<&'b i32> + // + // to be considered equivalent. So normalize all late-bound + // regions before we throw things into the underlying set. + self.set.insert(anonymize_predicate(self.tcx, pred)) + } +} + +impl Extend<ty::Predicate<'tcx>> for PredicateSet<'tcx> { + fn extend<I: IntoIterator<Item = ty::Predicate<'tcx>>>(&mut self, iter: I) { + for pred in iter { + self.insert(pred); + } + } + + fn extend_one(&mut self, pred: ty::Predicate<'tcx>) { + self.insert(pred); + } + + fn extend_reserve(&mut self, additional: usize) { + Extend::<ty::Predicate<'tcx>>::extend_reserve(&mut self.set, additional); + } +} + +/////////////////////////////////////////////////////////////////////////// +// `Elaboration` iterator +/////////////////////////////////////////////////////////////////////////// + +/// "Elaboration" is the process of identifying all the predicates that +/// are implied by a source predicate. Currently, this basically means +/// walking the "supertraits" and other similar assumptions. For example, +/// if we know that `T: Ord`, the elaborator would deduce that `T: PartialOrd` +/// holds as well. Similarly, if we have `trait Foo: 'static`, and we know that +/// `T: Foo`, then we know that `T: 'static`. +pub struct Elaborator<'tcx> { + stack: Vec<PredicateObligation<'tcx>>, + visited: PredicateSet<'tcx>, +} + +pub fn elaborate_trait_ref<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> Elaborator<'tcx> { + elaborate_predicates(tcx, std::iter::once(trait_ref.without_const().to_predicate(tcx))) +} + +pub fn elaborate_trait_refs<'tcx>( + tcx: TyCtxt<'tcx>, + trait_refs: impl Iterator<Item = ty::PolyTraitRef<'tcx>>, +) -> Elaborator<'tcx> { + let predicates = trait_refs.map(|trait_ref| trait_ref.without_const().to_predicate(tcx)); + elaborate_predicates(tcx, predicates) +} + +pub fn elaborate_predicates<'tcx>( + tcx: TyCtxt<'tcx>, + predicates: impl Iterator<Item = ty::Predicate<'tcx>>, +) -> Elaborator<'tcx> { + let obligations = predicates + .map(|predicate| { + predicate_obligation(predicate, ty::ParamEnv::empty(), ObligationCause::dummy()) + }) + .collect(); + elaborate_obligations(tcx, obligations) +} + +pub fn elaborate_obligations<'tcx>( + tcx: TyCtxt<'tcx>, + mut obligations: Vec<PredicateObligation<'tcx>>, +) -> Elaborator<'tcx> { + let mut visited = PredicateSet::new(tcx); + obligations.retain(|obligation| visited.insert(obligation.predicate)); + Elaborator { stack: obligations, visited } +} + +fn predicate_obligation<'tcx>( + predicate: ty::Predicate<'tcx>, + param_env: ty::ParamEnv<'tcx>, + cause: ObligationCause<'tcx>, +) -> PredicateObligation<'tcx> { + Obligation { cause, param_env, recursion_depth: 0, predicate } +} + +impl Elaborator<'tcx> { + pub fn filter_to_traits(self) -> FilterToTraits<Self> { + FilterToTraits::new(self) + } + + fn elaborate(&mut self, obligation: &PredicateObligation<'tcx>) { + let tcx = self.visited.tcx; + + let bound_predicate = obligation.predicate.kind(); + match bound_predicate.skip_binder() { + ty::PredicateKind::Trait(data, _) => { + // Get predicates declared on the trait. + let predicates = tcx.super_predicates_of(data.def_id()); + + let obligations = predicates.predicates.iter().map(|&(pred, _)| { + predicate_obligation( + pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)), + obligation.param_env, + obligation.cause.clone(), + ) + }); + debug!("super_predicates: data={:?}", data); + + // Only keep those bounds that we haven't already seen. + // This is necessary to prevent infinite recursion in some + // cases. One common case is when people define + // `trait Sized: Sized { }` rather than `trait Sized { }`. + let visited = &mut self.visited; + let obligations = obligations.filter(|o| visited.insert(o.predicate)); + + self.stack.extend(obligations); + } + ty::PredicateKind::WellFormed(..) => { + // Currently, we do not elaborate WF predicates, + // although we easily could. + } + ty::PredicateKind::ObjectSafe(..) => { + // Currently, we do not elaborate object-safe + // predicates. + } + ty::PredicateKind::Subtype(..) => { + // Currently, we do not "elaborate" predicates like `X <: Y`, + // though conceivably we might. + } + ty::PredicateKind::Projection(..) => { + // Nothing to elaborate in a projection predicate. + } + ty::PredicateKind::ClosureKind(..) => { + // Nothing to elaborate when waiting for a closure's kind to be inferred. + } + ty::PredicateKind::ConstEvaluatable(..) => { + // Currently, we do not elaborate const-evaluatable + // predicates. + } + ty::PredicateKind::ConstEquate(..) => { + // Currently, we do not elaborate const-equate + // predicates. + } + ty::PredicateKind::RegionOutlives(..) => { + // Nothing to elaborate from `'a: 'b`. + } + ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_max, r_min)) => { + // We know that `T: 'a` for some type `T`. We can + // often elaborate this. For example, if we know that + // `[U]: 'a`, that implies that `U: 'a`. Similarly, if + // we know `&'a U: 'b`, then we know that `'a: 'b` and + // `U: 'b`. + // + // We can basically ignore bound regions here. So for + // example `for<'c> Foo<'a,'c>: 'b` can be elaborated to + // `'a: 'b`. + + // Ignore `for<'a> T: 'a` -- we might in the future + // consider this as evidence that `T: 'static`, but + // I'm a bit wary of such constructions and so for now + // I want to be conservative. --nmatsakis + if r_min.is_late_bound() { + return; + } + + let visited = &mut self.visited; + let mut components = smallvec![]; + tcx.push_outlives_components(ty_max, &mut components); + self.stack.extend( + components + .into_iter() + .filter_map(|component| match component { + Component::Region(r) => { + if r.is_late_bound() { + None + } else { + Some(ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate( + r, r_min, + ))) + } + } + + Component::Param(p) => { + let ty = tcx.mk_ty_param(p.index, p.name); + Some(ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate( + ty, r_min, + ))) + } + + Component::UnresolvedInferenceVariable(_) => None, + + Component::Projection(_) | Component::EscapingProjection(_) => { + // We can probably do more here. This + // corresponds to a case like `<T as + // Foo<'a>>::U: 'b`. + None + } + }) + .map(|predicate_kind| predicate_kind.to_predicate(tcx)) + .filter(|&predicate| visited.insert(predicate)) + .map(|predicate| { + predicate_obligation( + predicate, + obligation.param_env, + obligation.cause.clone(), + ) + }), + ); + } + ty::PredicateKind::TypeWellFormedFromEnv(..) => { + // Nothing to elaborate + } + } + } +} + +impl Iterator for Elaborator<'tcx> { + type Item = PredicateObligation<'tcx>; + + fn size_hint(&self) -> (usize, Option<usize>) { + (self.stack.len(), None) + } + + fn next(&mut self) -> Option<Self::Item> { + // Extract next item from top-most stack frame, if any. + if let Some(obligation) = self.stack.pop() { + self.elaborate(&obligation); + Some(obligation) + } else { + None + } + } +} + +/////////////////////////////////////////////////////////////////////////// +// Supertrait iterator +/////////////////////////////////////////////////////////////////////////// + +pub type Supertraits<'tcx> = FilterToTraits<Elaborator<'tcx>>; + +pub fn supertraits<'tcx>( + tcx: TyCtxt<'tcx>, + trait_ref: ty::PolyTraitRef<'tcx>, +) -> Supertraits<'tcx> { + elaborate_trait_ref(tcx, trait_ref).filter_to_traits() +} + +pub fn transitive_bounds<'tcx>( + tcx: TyCtxt<'tcx>, + bounds: impl Iterator<Item = ty::PolyTraitRef<'tcx>>, +) -> Supertraits<'tcx> { + elaborate_trait_refs(tcx, bounds).filter_to_traits() +} + +/// A specialized variant of `elaborate_trait_refs` that only elaborates trait references that may +/// define the given associated type `assoc_name`. It uses the +/// `super_predicates_that_define_assoc_type` query to avoid enumerating super-predicates that +/// aren't related to `assoc_item`. This is used when resolving types like `Self::Item` or +/// `T::Item` and helps to avoid cycle errors (see e.g. #35237). +pub fn transitive_bounds_that_define_assoc_type<'tcx>( + tcx: TyCtxt<'tcx>, + bounds: impl Iterator<Item = ty::PolyTraitRef<'tcx>>, + assoc_name: Ident, +) -> impl Iterator<Item = ty::PolyTraitRef<'tcx>> { + let mut stack: Vec<_> = bounds.collect(); + let mut visited = FxIndexSet::default(); + + std::iter::from_fn(move || { + while let Some(trait_ref) = stack.pop() { + let anon_trait_ref = tcx.anonymize_late_bound_regions(trait_ref); + if visited.insert(anon_trait_ref) { + let super_predicates = tcx.super_predicates_that_define_assoc_type(( + trait_ref.def_id(), + Some(assoc_name), + )); + for (super_predicate, _) in super_predicates.predicates { + let subst_predicate = super_predicate.subst_supertrait(tcx, &trait_ref); + if let Some(binder) = subst_predicate.to_opt_poly_trait_ref() { + stack.push(binder.value); + } + } + + return Some(trait_ref); + } + } + + return None; + }) +} + +/////////////////////////////////////////////////////////////////////////// +// Other +/////////////////////////////////////////////////////////////////////////// + +/// A filter around an iterator of predicates that makes it yield up +/// just trait references. +pub struct FilterToTraits<I> { + base_iterator: I, +} + +impl<I> FilterToTraits<I> { + fn new(base: I) -> FilterToTraits<I> { + FilterToTraits { base_iterator: base } + } +} + +impl<'tcx, I: Iterator<Item = PredicateObligation<'tcx>>> Iterator for FilterToTraits<I> { + type Item = ty::PolyTraitRef<'tcx>; + + fn next(&mut self) -> Option<ty::PolyTraitRef<'tcx>> { + while let Some(obligation) = self.base_iterator.next() { + if let Some(data) = obligation.predicate.to_opt_poly_trait_ref() { + return Some(data.value); + } + } + None + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let (_, upper) = self.base_iterator.size_hint(); + (0, upper) + } +}  | 
