Skip to content

Commit fd967a6

Browse files
Auto merge of #143545 - compiler-errors:coroutine-obl, r=<try>
[experiment] Consider WF of coroutine witness when proving outlives assumptions This needs to be majorly cleaned up --- cc #110338 Consider, for example: ```rust use std::future::Future; trait Client { type Connecting<'a>: Future + Send where Self: 'a; fn connect(&'_ self) -> Self::Connecting<'_>; } fn call_connect<C>(c: &'_ C) -> impl Future + Send where C: Client + Send + Sync, { async move { c.connect().await } } ```
2 parents e9182f1 + 6999485 commit fd967a6

File tree

74 files changed

+1636
-146
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

74 files changed

+1636
-146
lines changed

compiler/rustc_borrowck/src/type_check/constraint_conversion.rs

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use rustc_data_structures::fx::FxHashSet;
12
use rustc_hir::def_id::LocalDefId;
23
use rustc_infer::infer::canonical::QueryRegionConstraints;
34
use rustc_infer::infer::outlives::env::RegionBoundPairs;
@@ -7,7 +8,7 @@ use rustc_infer::infer::{InferCtxt, SubregionOrigin};
78
use rustc_infer::traits::query::type_op::DeeplyNormalize;
89
use rustc_middle::bug;
910
use rustc_middle::ty::{
10-
self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, fold_regions,
11+
self, GenericArgKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt, elaborate, fold_regions,
1112
};
1213
use rustc_span::Span;
1314
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
@@ -70,10 +71,12 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
7071

7172
#[instrument(skip(self), level = "debug")]
7273
pub(super) fn convert_all(&mut self, query_constraints: &QueryRegionConstraints<'tcx>) {
73-
let QueryRegionConstraints { outlives } = query_constraints;
74+
let QueryRegionConstraints { outlives, assumptions } = query_constraints;
75+
let assumptions =
76+
elaborate::elaborate_outlives_assumptions(self.infcx.tcx, assumptions.iter().copied());
7477

7578
for &(predicate, constraint_category) in outlives {
76-
self.convert(predicate, constraint_category);
79+
self.convert(predicate, constraint_category, &assumptions);
7780
}
7881
}
7982

@@ -112,7 +115,11 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
112115

113116
self.category = outlives_requirement.category;
114117
self.span = outlives_requirement.blame_span;
115-
self.convert(ty::OutlivesPredicate(subject, outlived_region), self.category);
118+
self.convert(
119+
ty::OutlivesPredicate(subject, outlived_region),
120+
self.category,
121+
&Default::default(),
122+
);
116123
}
117124
(self.category, self.span, self.from_closure) = backup;
118125
}
@@ -121,6 +128,7 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
121128
&mut self,
122129
predicate: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
123130
constraint_category: ConstraintCategory<'tcx>,
131+
higher_ranked_assumptions: &FxHashSet<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>>,
124132
) {
125133
let tcx = self.infcx.tcx;
126134
debug!("generate: constraints at: {:#?}", self.locations);
@@ -150,7 +158,15 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
150158
}
151159

152160
let mut next_outlives_predicates = vec![];
153-
for (ty::OutlivesPredicate(k1, r2), constraint_category) in outlives_predicates {
161+
for (pred, constraint_category) in outlives_predicates {
162+
// Constraint is implied by a coroutine's well-formedness.
163+
if self.infcx.tcx.sess.opts.unstable_opts.higher_ranked_assumptions
164+
&& higher_ranked_assumptions.contains(&pred)
165+
{
166+
continue;
167+
}
168+
169+
let ty::OutlivesPredicate(k1, r2) = pred;
154170
match k1.kind() {
155171
GenericArgKind::Lifetime(r1) => {
156172
let r1_vid = self.to_region_vid(r1);
@@ -270,7 +286,8 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
270286
match self.param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, self.span)
271287
{
272288
Ok(TypeOpOutput { output: ty, constraints, .. }) => {
273-
if let Some(QueryRegionConstraints { outlives }) = constraints {
289+
// FIXME(higher_ranked_auto): What should we do with the assumptions here?
290+
if let Some(QueryRegionConstraints { outlives, assumptions: _ }) = constraints {
274291
next_outlives_predicates.extend(outlives.iter().copied());
275292
}
276293
ty

compiler/rustc_borrowck/src/type_check/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,11 @@ pub(crate) fn type_check<'tcx>(
131131
pre_obligations.is_empty(),
132132
"there should be no incoming region obligations = {pre_obligations:#?}",
133133
);
134+
let pre_assumptions = infcx.take_registered_region_assumptions();
135+
assert!(
136+
pre_assumptions.is_empty(),
137+
"there should be no incoming region assumptions = {pre_assumptions:#?}",
138+
);
134139

135140
debug!(?normalized_inputs_and_output);
136141

compiler/rustc_infer/src/infer/canonical/query_response.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@ impl<'tcx> InferCtxt<'tcx> {
115115
}
116116

117117
let region_obligations = self.take_registered_region_obligations();
118+
let region_assumptions = self.take_registered_region_assumptions();
118119
debug!(?region_obligations);
119120
let region_constraints = self.with_region_constraints(|region_constraints| {
120121
make_query_region_constraints(
@@ -123,6 +124,7 @@ impl<'tcx> InferCtxt<'tcx> {
123124
.iter()
124125
.map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category())),
125126
region_constraints,
127+
region_assumptions,
126128
)
127129
});
128130
debug!(?region_constraints);
@@ -174,6 +176,11 @@ impl<'tcx> InferCtxt<'tcx> {
174176
self.register_outlives_constraint(predicate, cause);
175177
}
176178

179+
for assumption in &query_response.value.region_constraints.assumptions {
180+
let assumption = instantiate_value(self.tcx, &result_args, *assumption);
181+
self.register_region_assumption(assumption);
182+
}
183+
177184
let user_result: R =
178185
query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone());
179186

@@ -297,6 +304,18 @@ impl<'tcx> InferCtxt<'tcx> {
297304
}),
298305
);
299306

307+
// FIXME(higher_ranked_auto): Optimize this to instantiate all assumptions
308+
// at once, rather than calling `instantiate_value` repeatedly which may
309+
// create more universes.
310+
output_query_region_constraints.assumptions.extend(
311+
query_response
312+
.value
313+
.region_constraints
314+
.assumptions
315+
.iter()
316+
.map(|&r_c| instantiate_value(self.tcx, &result_args, r_c)),
317+
);
318+
300319
let user_result: R =
301320
query_response.instantiate_projected(self.tcx, &result_args, |q_r| q_r.value.clone());
302321

@@ -572,6 +591,7 @@ pub fn make_query_region_constraints<'tcx>(
572591
tcx: TyCtxt<'tcx>,
573592
outlives_obligations: impl Iterator<Item = (Ty<'tcx>, ty::Region<'tcx>, ConstraintCategory<'tcx>)>,
574593
region_constraints: &RegionConstraintData<'tcx>,
594+
assumptions: Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>>,
575595
) -> QueryRegionConstraints<'tcx> {
576596
let RegionConstraintData { constraints, verifys } = region_constraints;
577597

@@ -604,5 +624,5 @@ pub fn make_query_region_constraints<'tcx>(
604624
}))
605625
.collect();
606626

607-
QueryRegionConstraints { outlives }
627+
QueryRegionConstraints { outlives, assumptions }
608628
}

compiler/rustc_infer/src/infer/mod.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,13 @@ pub struct InferCtxtInner<'tcx> {
149149
/// that all type inference variables have been bound and so forth.
150150
region_obligations: Vec<TypeOutlivesConstraint<'tcx>>,
151151

152+
/// The outlives bounds that we assume must hold about placeholders that
153+
/// come from instantiating the binder of coroutine-witnesses. These bounds
154+
/// are deduced from the well-formedness of the witness's types, and are
155+
/// necessary because of the way we anonymize the regions in a coroutine,
156+
/// which may cause types to no longer be considered well-formed.
157+
region_assumptions: Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>>,
158+
152159
/// Caches for opaque type inference.
153160
opaque_type_storage: OpaqueTypeStorage<'tcx>,
154161
}
@@ -164,7 +171,8 @@ impl<'tcx> InferCtxtInner<'tcx> {
164171
int_unification_storage: Default::default(),
165172
float_unification_storage: Default::default(),
166173
region_constraint_storage: Some(Default::default()),
167-
region_obligations: vec![],
174+
region_obligations: Default::default(),
175+
region_assumptions: Default::default(),
168176
opaque_type_storage: Default::default(),
169177
}
170178
}
@@ -174,6 +182,11 @@ impl<'tcx> InferCtxtInner<'tcx> {
174182
&self.region_obligations
175183
}
176184

185+
#[inline]
186+
pub fn region_assumptions(&self) -> &[ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>] {
187+
&self.region_assumptions
188+
}
189+
177190
#[inline]
178191
pub fn projection_cache(&mut self) -> traits::ProjectionCache<'_, 'tcx> {
179192
self.projection_cache.with_log(&mut self.undo_log)

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

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use rustc_data_structures::fx::FxIndexSet;
1+
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
22
use rustc_data_structures::transitive_relation::TransitiveRelationBuilder;
33
use rustc_middle::{bug, ty};
44
use tracing::debug;
@@ -39,6 +39,9 @@ pub struct OutlivesEnvironment<'tcx> {
3939
/// optimized in the future, though.
4040
region_bound_pairs: RegionBoundPairs<'tcx>,
4141
known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
42+
/// Assumptions that come from the well-formedness of coroutines that we prove
43+
/// auto trait bounds for during the type checking of this body.
44+
higher_ranked_assumptions: FxHashSet<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>>,
4245
}
4346

4447
/// "Region-bound pairs" tracks outlives relations that are known to
@@ -52,6 +55,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
5255
param_env: ty::ParamEnv<'tcx>,
5356
known_type_outlives: Vec<ty::PolyTypeOutlivesPredicate<'tcx>>,
5457
extra_bounds: impl IntoIterator<Item = OutlivesBound<'tcx>>,
58+
higher_ranked_assumptions: FxHashSet<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>>,
5559
) -> Self {
5660
let mut region_relation = TransitiveRelationBuilder::default();
5761
let mut region_bound_pairs = RegionBoundPairs::default();
@@ -88,6 +92,7 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
8892
known_type_outlives,
8993
free_region_map: FreeRegionMap { relation: region_relation.freeze() },
9094
region_bound_pairs,
95+
higher_ranked_assumptions,
9196
}
9297
}
9398

@@ -102,4 +107,10 @@ impl<'tcx> OutlivesEnvironment<'tcx> {
102107
pub fn known_type_outlives(&self) -> &[ty::PolyTypeOutlivesPredicate<'tcx>] {
103108
&self.known_type_outlives
104109
}
110+
111+
pub fn higher_ranked_assumptions(
112+
&self,
113+
) -> &FxHashSet<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>> {
114+
&self.higher_ranked_assumptions
115+
}
105116
}

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

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use super::region_constraints::{RegionConstraintData, UndoLog};
1010
use super::{InferCtxt, RegionResolutionError, SubregionOrigin};
1111
use crate::infer::free_regions::RegionRelations;
1212
use crate::infer::lexical_region_resolve;
13+
use crate::infer::region_constraints::Constraint;
1314

1415
pub mod env;
1516
pub mod for_liveness;
@@ -54,18 +55,29 @@ impl<'tcx> InferCtxt<'tcx> {
5455
}
5556
};
5657

57-
let storage = {
58+
let mut storage = {
5859
let mut inner = self.inner.borrow_mut();
5960
let inner = &mut *inner;
6061
assert!(
6162
self.tainted_by_errors().is_some() || inner.region_obligations.is_empty(),
6263
"region_obligations not empty: {:#?}",
63-
inner.region_obligations
64+
inner.region_obligations,
6465
);
6566
assert!(!UndoLogs::<UndoLog<'_>>::in_snapshot(&inner.undo_log));
6667
inner.region_constraint_storage.take().expect("regions already resolved")
6768
};
6869

70+
// Filter out any region-region outlives assumptions that are implied by
71+
// coroutine well-formedness.
72+
if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions {
73+
storage.data.constraints.retain(|(constraint, _)| match *constraint {
74+
Constraint::RegSubReg(r1, r2) => !outlives_env
75+
.higher_ranked_assumptions()
76+
.contains(&ty::OutlivesPredicate(r2.into(), r1)),
77+
_ => true,
78+
});
79+
}
80+
6981
let region_rels = &RegionRelations::new(self.tcx, outlives_env.free_region_map());
7082

7183
let (lexical_region_resolutions, errors) =
@@ -93,6 +105,11 @@ impl<'tcx> InferCtxt<'tcx> {
93105
"region_obligations not empty: {:#?}",
94106
self.inner.borrow().region_obligations
95107
);
108+
assert!(
109+
self.inner.borrow().region_assumptions.is_empty(),
110+
"region_assumptions not empty: {:#?}",
111+
self.inner.borrow().region_assumptions
112+
);
96113

97114
self.inner.borrow_mut().unwrap_region_constraints().take_and_reset_data()
98115
}

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,21 @@ impl<'tcx> InferCtxt<'tcx> {
169169
std::mem::take(&mut self.inner.borrow_mut().region_obligations)
170170
}
171171

172+
pub fn register_region_assumption(
173+
&self,
174+
assumption: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
175+
) {
176+
let mut inner = self.inner.borrow_mut();
177+
inner.undo_log.push(UndoLog::PushRegionAssumption);
178+
inner.region_assumptions.push(assumption);
179+
}
180+
181+
pub fn take_registered_region_assumptions(
182+
&self,
183+
) -> Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>> {
184+
std::mem::take(&mut self.inner.borrow_mut().region_assumptions)
185+
}
186+
172187
/// Process the region obligations that must be proven (during
173188
/// `regionck`) for the given `body_id`, given information about
174189
/// the region bounds in scope and so forth.
@@ -219,6 +234,14 @@ impl<'tcx> InferCtxt<'tcx> {
219234
let (sup_type, sub_region) =
220235
(sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self));
221236

237+
if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions
238+
&& outlives_env
239+
.higher_ranked_assumptions()
240+
.contains(&ty::OutlivesPredicate(sup_type.into(), sub_region))
241+
{
242+
continue;
243+
}
244+
222245
debug!(?sup_type, ?sub_region, ?origin);
223246

224247
let outlives = &mut TypeOutlives::new(

compiler/rustc_infer/src/infer/snapshot/undo_log.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ pub(crate) enum UndoLog<'tcx> {
2727
RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
2828
ProjectionCache(traits::UndoLog<'tcx>),
2929
PushTypeOutlivesConstraint,
30+
PushRegionAssumption,
3031
}
3132

3233
macro_rules! impl_from {
@@ -75,6 +76,9 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
7576
UndoLog::PushTypeOutlivesConstraint => {
7677
self.region_obligations.pop();
7778
}
79+
UndoLog::PushRegionAssumption => {
80+
self.region_assumptions.pop();
81+
}
7882
}
7983
}
8084
}

compiler/rustc_middle/src/infer/canonical.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,18 @@ pub struct QueryResponse<'tcx, R> {
8181
#[derive(HashStable, TypeFoldable, TypeVisitable)]
8282
pub struct QueryRegionConstraints<'tcx> {
8383
pub outlives: Vec<QueryOutlivesConstraint<'tcx>>,
84+
pub assumptions: Vec<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>>,
8485
}
8586

8687
impl QueryRegionConstraints<'_> {
87-
/// Represents an empty (trivially true) set of region
88-
/// constraints.
88+
/// Represents an empty (trivially true) set of region constraints.
89+
///
90+
/// FIXME(higher_ranked_auto): This could still just be true if there are only assumptions?
91+
/// Because I don't expect for us to get cases where an assumption from one query would
92+
/// discharge a requirement from another query, which is a potential problem if we did throw
93+
/// away these assumptions because there were no constraints.
8994
pub fn is_empty(&self) -> bool {
90-
self.outlives.is_empty()
95+
self.outlives.is_empty() && self.assumptions.is_empty()
9196
}
9297
}
9398

compiler/rustc_middle/src/query/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -988,7 +988,7 @@ rustc_queries! {
988988
}
989989

990990
query coroutine_hidden_types(
991-
def_id: DefId
991+
def_id: DefId,
992992
) -> ty::EarlyBinder<'tcx, ty::Binder<'tcx, ty::CoroutineWitnessTypes<TyCtxt<'tcx>>>> {
993993
desc { "looking up the hidden types stored across await points in a coroutine" }
994994
}

0 commit comments

Comments
 (0)