Skip to content

Commit 16de1fd

Browse files
authored
Rollup merge of #102016 - lcnr:given-OutlivesEnvironment, r=jackh726
implied_bounds: deal with inference vars fixes #101951 while computing implied bounds for `<<T as ConstructionFirm>::Builder as BuilderFn<'_>>::Output` normalization replaces a projection with an inference var (adding a `Projection` obligation). Until we prove that obligation, this inference var remains unknown, which caused us to miss an implied bound necessary to prove that the unnormalized projection from the trait method signature is wf. r? types
2 parents e20fabb + 72a2102 commit 16de1fd

File tree

5 files changed

+91
-34
lines changed

5 files changed

+91
-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,
+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Taken directly from that issue.
2+
//
3+
// This test detected that we didn't correctly resolve
4+
// inference variables when computing implied bounds.
5+
//
6+
// check-pass
7+
pub trait BuilderFn<'a> {
8+
type Output;
9+
}
10+
11+
impl<'a, F, Out> BuilderFn<'a> for F
12+
where
13+
F: FnOnce(&'a mut ()) -> Out,
14+
{
15+
type Output = Out;
16+
}
17+
18+
pub trait ConstructionFirm {
19+
type Builder: for<'a> BuilderFn<'a>;
20+
}
21+
22+
pub trait Campus<T>
23+
where
24+
T: ConstructionFirm,
25+
{
26+
fn add_building(
27+
&mut self,
28+
building: &mut <<T as ConstructionFirm>::Builder as BuilderFn<'_>>::Output,
29+
);
30+
}
31+
32+
struct ArchitectsInc {}
33+
34+
impl ConstructionFirm for ArchitectsInc {
35+
type Builder = fn(&mut ()) -> PrettyCondo<'_>;
36+
}
37+
38+
struct PrettyCondo<'a> {
39+
_marker: &'a mut (),
40+
}
41+
42+
struct CondoEstate {}
43+
44+
impl Campus<ArchitectsInc> for CondoEstate {
45+
fn add_building(&mut self, _building: &mut PrettyCondo<'_>) {
46+
todo!()
47+
}
48+
}
49+
50+
fn main() {}

0 commit comments

Comments
 (0)