Skip to content

Commit 71f8fd5

Browse files
committed
improve infer var handling for implied bounds
1 parent efa717b commit 71f8fd5

File tree

4 files changed

+41
-34
lines changed

4 files changed

+41
-34
lines changed

compiler/rustc_infer/src/infer/outlives/env.rs

+2
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ pub struct OutlivesEnvironment<'tcx> {
5353
}
5454

5555
/// Builder of OutlivesEnvironment.
56+
#[derive(Debug)]
5657
struct OutlivesEnvironmentBuilder<'tcx> {
5758
param_env: ty::ParamEnv<'tcx>,
5859
region_relation: TransitiveRelationBuilder<Region<'tcx>>,
@@ -109,6 +110,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
109110

110111
impl<'a, 'tcx> OutlivesEnvironmentBuilder<'tcx> {
111112
#[inline]
113+
#[instrument(level = "debug")]
112114
fn build(self) -> OutlivesEnvironment<'tcx> {
113115
OutlivesEnvironment {
114116
param_env: self.param_env,

compiler/rustc_trait_selection/src/traits/outlives_bounds.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl<'a, 'cx, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'cx, 'tcx> {
4646
/// Note that this may cause outlives obligations to be injected
4747
/// into the inference context with this body-id.
4848
/// - `ty`, the type that we are supposed to assume is WF.
49-
#[instrument(level = "debug", skip(self, param_env, body_id))]
49+
#[instrument(level = "debug", skip(self, param_env, body_id), ret)]
5050
fn implied_outlives_bounds(
5151
&self,
5252
param_env: ty::ParamEnv<'tcx>,
@@ -71,6 +71,7 @@ impl<'a, 'cx, 'tcx: 'a> InferCtxtExt<'a, 'tcx> for InferCtxt<'cx, 'tcx> {
7171
let TypeOpOutput { output, constraints, .. } = result;
7272

7373
if let Some(constraints) = constraints {
74+
debug!(?constraints);
7475
// Instantiation may have produced new inference variables and constraints on those
7576
// variables. Process these constraints.
7677
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self.tcx);

compiler/rustc_traits/src/implied_outlives_bounds.rs

+36-33
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ fn compute_implied_outlives_bounds<'tcx>(
4949
let mut checked_wf_args = rustc_data_structures::fx::FxHashSet::default();
5050
let mut wf_args = vec![ty.into()];
5151

52-
let mut implied_bounds = vec![];
52+
let mut outlives_bounds: Vec<ty::OutlivesPredicate<ty::GenericArg<'tcx>, ty::Region<'tcx>>> =
53+
vec![];
5354

5455
let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(tcx);
5556

@@ -65,41 +66,28 @@ fn compute_implied_outlives_bounds<'tcx>(
6566
// than the ultimate set. (Note: normally there won't be
6667
// unresolved inference variables here anyway, but there might be
6768
// during typeck under some circumstances.)
69+
//
70+
// FIXME(@lcnr): It's not really "always fine", having fewer implied
71+
// bounds can be backward incompatible, e.g. #101951 was caused by
72+
// us not dealing with inference vars in `TypeOutlives` predicates.
6873
let obligations = wf::obligations(infcx, param_env, hir::CRATE_HIR_ID, 0, arg, DUMMY_SP)
6974
.unwrap_or_default();
7075

71-
// N.B., all of these predicates *ought* to be easily proven
72-
// true. In fact, their correctness is (mostly) implied by
73-
// other parts of the program. However, in #42552, we had
74-
// an annoying scenario where:
75-
//
76-
// - Some `T::Foo` gets normalized, resulting in a
77-
// variable `_1` and a `T: Trait<Foo=_1>` constraint
78-
// (not sure why it couldn't immediately get
79-
// solved). This result of `_1` got cached.
80-
// - These obligations were dropped on the floor here,
81-
// rather than being registered.
82-
// - Then later we would get a request to normalize
83-
// `T::Foo` which would result in `_1` being used from
84-
// the cache, but hence without the `T: Trait<Foo=_1>`
85-
// constraint. As a result, `_1` never gets resolved,
86-
// and we get an ICE (in dropck).
87-
//
88-
// Therefore, we register any predicates involving
89-
// inference variables. We restrict ourselves to those
90-
// involving inference variables both for efficiency and
91-
// to avoids duplicate errors that otherwise show up.
76+
// While these predicates should all be implied by other parts of
77+
// the program, they are still relevant as they may constrain
78+
// inference variables, which is necessary to add the correct
79+
// implied bounds in some cases, mostly when dealing with projections.
9280
fulfill_cx.register_predicate_obligations(
9381
infcx,
9482
obligations.iter().filter(|o| o.predicate.has_infer_types_or_consts()).cloned(),
9583
);
9684

9785
// From the full set of obligations, just filter down to the
9886
// region relationships.
99-
implied_bounds.extend(obligations.into_iter().flat_map(|obligation| {
87+
outlives_bounds.extend(obligations.into_iter().filter_map(|obligation| {
10088
assert!(!obligation.has_escaping_bound_vars());
10189
match obligation.predicate.kind().no_bound_vars() {
102-
None => vec![],
90+
None => None,
10391
Some(pred) => match pred {
10492
ty::PredicateKind::Trait(..)
10593
| ty::PredicateKind::Subtype(..)
@@ -109,21 +97,18 @@ fn compute_implied_outlives_bounds<'tcx>(
10997
| ty::PredicateKind::ObjectSafe(..)
11098
| ty::PredicateKind::ConstEvaluatable(..)
11199
| ty::PredicateKind::ConstEquate(..)
112-
| ty::PredicateKind::TypeWellFormedFromEnv(..) => vec![],
100+
| ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
113101
ty::PredicateKind::WellFormed(arg) => {
114102
wf_args.push(arg);
115-
vec![]
103+
None
116104
}
117105

118106
ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => {
119-
vec![OutlivesBound::RegionSubRegion(r_b, r_a)]
107+
Some(ty::OutlivesPredicate(r_a.into(), r_b))
120108
}
121109

122110
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, r_b)) => {
123-
let ty_a = infcx.resolve_vars_if_possible(ty_a);
124-
let mut components = smallvec![];
125-
push_outlives_components(tcx, ty_a, &mut components);
126-
implied_bounds_from_components(r_b, components)
111+
Some(ty::OutlivesPredicate(ty_a.into(), r_b))
127112
}
128113
},
129114
}
@@ -133,9 +118,27 @@ fn compute_implied_outlives_bounds<'tcx>(
133118
// Ensure that those obligations that we had to solve
134119
// get solved *here*.
135120
match fulfill_cx.select_all_or_error(infcx).as_slice() {
136-
[] => Ok(implied_bounds),
137-
_ => Err(NoSolution),
121+
[] => (),
122+
_ => return Err(NoSolution),
138123
}
124+
125+
// We lazily compute the outlives components as
126+
// `select_all_or_error` constrains inference variables.
127+
let implied_bounds = outlives_bounds
128+
.into_iter()
129+
.flat_map(|ty::OutlivesPredicate(a, r_b)| match a.unpack() {
130+
ty::GenericArgKind::Lifetime(r_a) => vec![OutlivesBound::RegionSubRegion(r_b, r_a)],
131+
ty::GenericArgKind::Type(ty_a) => {
132+
let ty_a = infcx.resolve_vars_if_possible(ty_a);
133+
let mut components = smallvec![];
134+
push_outlives_components(tcx, ty_a, &mut components);
135+
implied_bounds_from_components(r_b, components)
136+
}
137+
ty::GenericArgKind::Const(_) => unreachable!(),
138+
})
139+
.collect();
140+
141+
Ok(implied_bounds)
139142
}
140143

141144
/// When we have an implied bound that `T: 'a`, we can further break

compiler/rustc_typeck/src/check/compare_method.rs

+1
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ pub(crate) fn compare_impl_method<'tcx>(
140140
///
141141
/// Finally we register each of these predicates as an obligation and check that
142142
/// they hold.
143+
#[instrument(level = "debug", skip(tcx, impl_m_span, impl_trait_ref))]
143144
fn compare_predicate_entailment<'tcx>(
144145
tcx: TyCtxt<'tcx>,
145146
impl_m: &AssocItem,

0 commit comments

Comments
 (0)