Skip to content

Commit a697573

Browse files
committed
Auto merge of #101680 - jackh726:implied-cleanup, r=lcnr
Fix implied outlives bounds logic for projections The logic here is subtly wrong. I put a bit of an explanation in a767d7b5165cea8ee5cbe494a4a636c50ef67c9c. TL;DR: we register outlives predicates to be proved, because wf code normalizes projections (from the unnormalized types) to type variables. This causes us to register those as constraints instead of implied. This was "fine", because we later added that implied bound in the normalized type, and delayed registering constraints. When I went to cleanup `free_region_relations` to *not* delay adding constraints, this bug was uncovered. cc. `@aliemjay` because this caused your test failure in #99832 (I only realized as I was writing this) r? `@nikomatsakis`
2 parents a12d31d + 0637b6b commit a697573

File tree

7 files changed

+144
-106
lines changed

7 files changed

+144
-106
lines changed

compiler/rustc_borrowck/src/region_infer/values.rs

+1
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ pub(crate) struct PlaceholderIndices {
187187
}
188188

189189
impl PlaceholderIndices {
190+
/// Returns the `PlaceholderIndex` for the inserted `PlaceholderRegion`
190191
pub(crate) fn insert(&mut self, placeholder: ty::PlaceholderRegion) -> PlaceholderIndex {
191192
let (index, _) = self.indices.insert_full(placeholder);
192193
index.into()

compiler/rustc_borrowck/src/type_check/free_region_relations.rs

+87-68
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use rustc_infer::infer::InferCtxt;
88
use rustc_middle::mir::ConstraintCategory;
99
use rustc_middle::traits::query::OutlivesBound;
1010
use rustc_middle::ty::{self, RegionVid, Ty};
11+
use rustc_span::Span;
1112
use rustc_trait_selection::traits::query::type_op::{self, TypeOp};
1213
use std::rc::Rc;
1314
use type_op::TypeOpOutput;
@@ -217,8 +218,27 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
217218
self.inverse_outlives.add(fr_b, fr_a);
218219
}
219220

221+
#[instrument(level = "debug", skip(self))]
220222
pub(crate) fn create(mut self) -> CreateResult<'tcx> {
221223
let span = self.infcx.tcx.def_span(self.universal_regions.defining_ty.def_id());
224+
225+
// Insert the facts we know from the predicates. Why? Why not.
226+
let param_env = self.param_env;
227+
self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env));
228+
229+
// - outlives is reflexive, so `'r: 'r` for every region `'r`
230+
// - `'static: 'r` for every region `'r`
231+
// - `'r: 'fn_body` for every (other) universally quantified
232+
// region `'r`, all of which are provided by our caller
233+
let fr_static = self.universal_regions.fr_static;
234+
let fr_fn_body = self.universal_regions.fr_fn_body;
235+
for fr in self.universal_regions.universal_regions() {
236+
debug!("build: relating free region {:?} to itself and to 'static", fr);
237+
self.relate_universal_regions(fr, fr);
238+
self.relate_universal_regions(fr_static, fr);
239+
self.relate_universal_regions(fr, fr_fn_body);
240+
}
241+
222242
let unnormalized_input_output_tys = self
223243
.universal_regions
224244
.unnormalized_input_tys
@@ -236,78 +256,58 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
236256
// the `relations` is built.
237257
let mut normalized_inputs_and_output =
238258
Vec::with_capacity(self.universal_regions.unnormalized_input_tys.len() + 1);
239-
let constraint_sets: Vec<_> = unnormalized_input_output_tys
240-
.flat_map(|ty| {
241-
debug!("build: input_or_output={:?}", ty);
242-
// We add implied bounds from both the unnormalized and normalized ty.
243-
// See issue #87748
244-
let constraints_implied1 = self.add_implied_bounds(ty);
245-
let TypeOpOutput { output: norm_ty, constraints: constraints1, .. } = self
246-
.param_env
247-
.and(type_op::normalize::Normalize::new(ty))
248-
.fully_perform(self.infcx)
249-
.unwrap_or_else(|_| {
250-
let reported = self
251-
.infcx
252-
.tcx
253-
.sess
254-
.delay_span_bug(span, &format!("failed to normalize {:?}", ty));
255-
TypeOpOutput {
256-
output: self.infcx.tcx.ty_error_with_guaranteed(reported),
257-
constraints: None,
258-
error_info: None,
259-
}
260-
});
261-
// Note: we need this in examples like
262-
// ```
263-
// trait Foo {
264-
// type Bar;
265-
// fn foo(&self) -> &Self::Bar;
266-
// }
267-
// impl Foo for () {
268-
// type Bar = ();
269-
// fn foo(&self) -> &() {}
270-
// }
271-
// ```
272-
// Both &Self::Bar and &() are WF
273-
let constraints_implied2 =
274-
if ty != norm_ty { self.add_implied_bounds(norm_ty) } else { None };
275-
normalized_inputs_and_output.push(norm_ty);
276-
constraints1.into_iter().chain(constraints_implied1).chain(constraints_implied2)
277-
})
278-
.collect();
259+
let mut constraints = vec![];
260+
for ty in unnormalized_input_output_tys {
261+
debug!("build: input_or_output={:?}", ty);
262+
// We add implied bounds from both the unnormalized and normalized ty.
263+
// See issue #87748
264+
let constraints_unnorm = self.add_implied_bounds(ty);
265+
if let Some(c) = constraints_unnorm {
266+
constraints.push(c)
267+
}
268+
let TypeOpOutput { output: norm_ty, constraints: constraints_normalize, .. } = self
269+
.param_env
270+
.and(type_op::normalize::Normalize::new(ty))
271+
.fully_perform(self.infcx)
272+
.unwrap_or_else(|_| {
273+
self.infcx
274+
.tcx
275+
.sess
276+
.delay_span_bug(span, &format!("failed to normalize {:?}", ty));
277+
TypeOpOutput {
278+
output: self.infcx.tcx.ty_error(),
279+
constraints: None,
280+
error_info: None,
281+
}
282+
});
283+
if let Some(c) = constraints_normalize {
284+
constraints.push(c)
285+
}
279286

280-
// Insert the facts we know from the predicates. Why? Why not.
281-
let param_env = self.param_env;
282-
self.add_outlives_bounds(outlives::explicit_outlives_bounds(param_env));
287+
// Note: we need this in examples like
288+
// ```
289+
// trait Foo {
290+
// type Bar;
291+
// fn foo(&self) -> &Self::Bar;
292+
// }
293+
// impl Foo for () {
294+
// type Bar = ();
295+
// fn foo(&self) ->&() {}
296+
// }
297+
// ```
298+
// Both &Self::Bar and &() are WF
299+
if ty != norm_ty {
300+
let constraints_norm = self.add_implied_bounds(norm_ty);
301+
if let Some(c) = constraints_norm {
302+
constraints.push(c)
303+
}
304+
}
283305

284-
// Finally:
285-
// - outlives is reflexive, so `'r: 'r` for every region `'r`
286-
// - `'static: 'r` for every region `'r`
287-
// - `'r: 'fn_body` for every (other) universally quantified
288-
// region `'r`, all of which are provided by our caller
289-
let fr_static = self.universal_regions.fr_static;
290-
let fr_fn_body = self.universal_regions.fr_fn_body;
291-
for fr in self.universal_regions.universal_regions() {
292-
debug!("build: relating free region {:?} to itself and to 'static", fr);
293-
self.relate_universal_regions(fr, fr);
294-
self.relate_universal_regions(fr_static, fr);
295-
self.relate_universal_regions(fr, fr_fn_body);
306+
normalized_inputs_and_output.push(norm_ty);
296307
}
297308

298-
for data in &constraint_sets {
299-
constraint_conversion::ConstraintConversion::new(
300-
self.infcx,
301-
&self.universal_regions,
302-
&self.region_bound_pairs,
303-
self.implicit_region_bound,
304-
self.param_env,
305-
Locations::All(span),
306-
span,
307-
ConstraintCategory::Internal,
308-
&mut self.constraints,
309-
)
310-
.convert_all(data);
309+
for c in constraints {
310+
self.push_region_constraints(c, span);
311311
}
312312

313313
CreateResult {
@@ -321,6 +321,24 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
321321
}
322322
}
323323

324+
#[instrument(skip(self, data), level = "debug")]
325+
fn push_region_constraints(&mut self, data: &QueryRegionConstraints<'tcx>, span: Span) {
326+
debug!("constraints generated: {:#?}", data);
327+
328+
constraint_conversion::ConstraintConversion::new(
329+
self.infcx,
330+
&self.universal_regions,
331+
&self.region_bound_pairs,
332+
self.implicit_region_bound,
333+
self.param_env,
334+
Locations::All(span),
335+
span,
336+
ConstraintCategory::Internal,
337+
&mut self.constraints,
338+
)
339+
.convert_all(data);
340+
}
341+
324342
/// Update the type of a single local, which should represent
325343
/// either the return type of the MIR or one of its arguments. At
326344
/// the same time, compute and add any implied bounds that come
@@ -332,6 +350,7 @@ impl<'tcx> UniversalRegionRelationsBuilder<'_, 'tcx> {
332350
.and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty })
333351
.fully_perform(self.infcx)
334352
.unwrap_or_else(|_| bug!("failed to compute implied bounds {:?}", ty));
353+
debug!(?bounds, ?constraints);
335354
self.add_outlives_bounds(bounds);
336355
constraints
337356
}

compiler/rustc_borrowck/src/type_check/mod.rs

+2
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,8 @@ pub(crate) struct MirTypeckRegionConstraints<'tcx> {
910910
}
911911

912912
impl<'tcx> MirTypeckRegionConstraints<'tcx> {
913+
/// Creates a `Region` for a given `PlaceholderRegion`, or returns the
914+
/// region that corresponds to a previously created one.
913915
fn placeholder_region(
914916
&mut self,
915917
infcx: &InferCtxt<'tcx>,

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

+1
Original file line numberDiff line numberDiff line change
@@ -207,6 +207,7 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
207207
///
208208
/// In some cases, such as when `erased_ty` represents a `ty::Param`, however,
209209
/// the result is precise.
210+
#[instrument(level = "debug", skip(self))]
210211
fn declared_generic_bounds_from_env_for_erased_ty(
211212
&self,
212213
erased_ty: Ty<'tcx>,

compiler/rustc_traits/src/implied_outlives_bounds.rs

+50-36
Original file line numberDiff line numberDiff line change
@@ -70,47 +70,61 @@ fn compute_implied_outlives_bounds<'tcx>(
7070
let obligations = wf::obligations(ocx.infcx, param_env, CRATE_DEF_ID, 0, arg, DUMMY_SP)
7171
.unwrap_or_default();
7272

73-
// While these predicates should all be implied by other parts of
74-
// the program, they are still relevant as they may constrain
75-
// inference variables, which is necessary to add the correct
76-
// implied bounds in some cases, mostly when dealing with projections.
77-
ocx.register_obligations(
78-
obligations.iter().filter(|o| o.predicate.has_non_region_infer()).cloned(),
79-
);
80-
81-
// From the full set of obligations, just filter down to the
82-
// region relationships.
83-
outlives_bounds.extend(obligations.into_iter().filter_map(|obligation| {
73+
for obligation in obligations {
74+
debug!(?obligation);
8475
assert!(!obligation.has_escaping_bound_vars());
85-
match obligation.predicate.kind().no_bound_vars() {
86-
None => None,
87-
Some(pred) => match pred {
88-
ty::PredicateKind::Clause(ty::Clause::Trait(..))
89-
| ty::PredicateKind::Subtype(..)
90-
| ty::PredicateKind::Coerce(..)
91-
| ty::PredicateKind::Clause(ty::Clause::Projection(..))
92-
| ty::PredicateKind::ClosureKind(..)
93-
| ty::PredicateKind::ObjectSafe(..)
94-
| ty::PredicateKind::ConstEvaluatable(..)
95-
| ty::PredicateKind::ConstEquate(..)
96-
| ty::PredicateKind::Ambiguous
97-
| ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
98-
ty::PredicateKind::WellFormed(arg) => {
99-
wf_args.push(arg);
100-
None
76+
77+
// While these predicates should all be implied by other parts of
78+
// the program, they are still relevant as they may constrain
79+
// inference variables, which is necessary to add the correct
80+
// implied bounds in some cases, mostly when dealing with projections.
81+
//
82+
// Another important point here: we only register `Projection`
83+
// predicates, since otherwise we might register outlives
84+
// predicates containing inference variables, and we don't
85+
// learn anything new from those.
86+
if obligation.predicate.has_non_region_infer() {
87+
match obligation.predicate.kind().skip_binder() {
88+
ty::PredicateKind::Clause(ty::Clause::Projection(..)) => {
89+
ocx.register_obligation(obligation.clone());
10190
}
91+
_ => {}
92+
}
93+
}
10294

103-
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(
104-
ty::OutlivesPredicate(r_a, r_b),
105-
)) => Some(ty::OutlivesPredicate(r_a.into(), r_b)),
95+
let pred = match obligation.predicate.kind().no_bound_vars() {
96+
None => continue,
97+
Some(pred) => pred,
98+
};
99+
match pred {
100+
ty::PredicateKind::Clause(ty::Clause::Trait(..))
101+
| ty::PredicateKind::Subtype(..)
102+
| ty::PredicateKind::Coerce(..)
103+
| ty::PredicateKind::Clause(ty::Clause::Projection(..))
104+
| ty::PredicateKind::ClosureKind(..)
105+
| ty::PredicateKind::ObjectSafe(..)
106+
| ty::PredicateKind::ConstEvaluatable(..)
107+
| ty::PredicateKind::ConstEquate(..)
108+
| ty::PredicateKind::Ambiguous
109+
| ty::PredicateKind::TypeWellFormedFromEnv(..) => {}
110+
111+
// We need to search through *all* WellFormed predicates
112+
ty::PredicateKind::WellFormed(arg) => {
113+
wf_args.push(arg);
114+
}
115+
116+
// We need to register region relationships
117+
ty::PredicateKind::Clause(ty::Clause::RegionOutlives(ty::OutlivesPredicate(
118+
r_a,
119+
r_b,
120+
))) => outlives_bounds.push(ty::OutlivesPredicate(r_a.into(), r_b)),
106121

107-
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
108-
ty_a,
109-
r_b,
110-
))) => Some(ty::OutlivesPredicate(ty_a.into(), r_b)),
111-
},
122+
ty::PredicateKind::Clause(ty::Clause::TypeOutlives(ty::OutlivesPredicate(
123+
ty_a,
124+
r_b,
125+
))) => outlives_bounds.push(ty::OutlivesPredicate(ty_a.into(), r_b)),
112126
}
113-
}));
127+
}
114128
}
115129

116130
// This call to `select_all_or_error` is necessary to constrain inference variables, which we

compiler/rustc_traits/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#![deny(rustc::untranslatable_diagnostic)]
55
#![deny(rustc::diagnostic_outside_of_impl)]
66
#![feature(let_chains)]
7+
#![feature(drain_filter)]
78
#![recursion_limit = "256"]
89

910
#[macro_use]

tests/ui/nll/issue-52057.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Regression test for #52057. There is an implied bound
2-
// that `I: 'a` where `'a` is the lifetime of `self` in `parse_first`;
3-
// but to observe that, one must normalize first.
2+
// that `I: 'x` where `'x` is the lifetime of the reference `&mut Self::Input`
3+
// in `parse_first`; but to observe that, one must normalize first.
44
//
55
// run-pass
66

0 commit comments

Comments
 (0)