Skip to content

Commit 112c9bd

Browse files
committed
Auto merge of #143545 - compiler-errors:coroutine-obl, r=oli-obk
`-Zhigher-ranked-assumptions`: Consider WF of coroutine witness when proving outlives assumptions ### TL;DR This PR introduces an unstable flag `-Zhigher-ranked-assumptions` which tests out a new algorithm for dealing with some of the higher-ranked outlives problems that come from auto trait bounds on coroutines. See: * #110338 While it doesn't fix all of the issues, it certainly fixed many of them, so I'd like to get this landed so people can test the flag on their own code. ### Background 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 } } ``` Due to the fact that we erase the lifetimes in a coroutine, we can think of the interior type of the async block as something like: `exists<'r, 's> { C, &'r C, C::Connecting<'s> }`. The first field is the `c` we capture, the second is the auto-ref that we perform on the call to `.connect()`, and the third is the resulting future we're awaiting at the first and only await point. Note that every region is uniquified differently in the interior types. For the async block to be `Send`, we must prove that both of the interior types are `Send`. First, we have an `exists<'r, 's>` binder, which needs to be instantiated universally since we treat the regions in this binder as *unknown*[^exist]. This gives us two types: `{ &'!r C, C::Connecting<'!s> }`. Proving `&'!r C: Send` is easy due to a [`Send`](https://doc.rust-lang.org/nightly/std/marker/trait.Send.html#impl-Send-for-%26T) impl for references. Proving `C::Connecting<'!s>: Send` can only be done via the item bound, which then requires `C: '!s` to hold (due to the `where Self: 'a` on the associated type definition). Unfortunately, we don't know that `C: '!s` since we stripped away any relationship between the interior type and the param `C`. This leads to a bogus borrow checker error today! ### Approach Coroutine interiors are well-formed by virtue of them being borrow-checked, as long as their callers are invoking their parent functions in a well-formed way, then substitutions should also be well-formed. Therefore, in our example above, we should be able to deduce the assumption that `C: '!s` holds from the well-formedness of the interior type `C::Connecting<'!s>`. This PR introduces the notion of *coroutine assumptions*, which are the outlives assumptions that we can assume hold due to the well-formedness of a coroutine's interior types. These are computed alongside the coroutine types in the `CoroutineWitnessTypes` struct. When we instantiate the binder when proving an auto trait for a coroutine, we instantiate the `CoroutineWitnessTypes` and stash these newly instantiated assumptions in the region storage in the `InferCtxt`. Later on in lexical region resolution or MIR borrowck, we use these registered assumptions to discharge any placeholder outlives obligations that we would otherwise not be able to prove. ### How well does it work? I've added a ton of tests of different reported situations that users have shared on issues like #110338, and an (anecdotally) large number of those examples end up working straight out of the box! Some limitations are described below. ### How badly does it not work? The behavior today is quite rudimentary, since we currently discharge the placeholder assumptions pretty early in region resolution. This manifests itself as some limitations on the code that we accept. For example, `tests/ui/async-await/higher-ranked-auto-trait-11.rs` continues to fail. In that test, we must prove that a placeholder is equal to a universal for a param-env candidate to hold when proving an auto trait, e.g. `'!1 = 'a` is required to prove `T: Trait<'!1>` in a param-env that has `T: Trait<'a>`. Unfortunately, at that point in the MIR body, we only know that the placeholder is equal to some body-local existential NLL var `'?2`, which only gets equated to the universal `'a` when being stored into the return local later on in MIR borrowck. This could be fixed by integrating these assumptions into the type outlives machinery in a more first-class way, and delaying things to the end of MIR typeck when we know the full relationship between existential and universal NLL vars. Doing this integration today is quite difficult today. `tests/ui/async-await/higher-ranked-auto-trait-11.rs` fails because we don't compute the full transitive outlives relations between placeholders. In that test, we have in our region assumptions that some `'!1 = '!2` and `'!2 = '!3`, but we must prove `'!1 = '!3`. This can be fixed by computing the set of coroutine outlives assumptions in a more transitive way, or as I mentioned above, integrating these assumptions into the type outlives machinery in a more first-class way, since it's already responsible for the transitive outlives assumptions of universals. ### Moving forward I'm still quite happy with this implementation, and I'd like to land it for testing. I may work on overhauling both the way we compute these coroutine assumptions and also how we deal with the assumptions during (lexical/nll) region checking. But for now, I'd like to give users a chance to try out this new `-Zhigher-ranked-assumptions` flag to uncover more shortcomings. [^exist]: Instantiating this binder with infer regions would be incomplete, since we'd be asking for *some* instantiation of the interior types, not proving something for *all* instantiations of the interior types.
2 parents 3014e79 + 3634f46 commit 112c9bd

File tree

77 files changed

+1645
-159
lines changed

Some content is hidden

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

77 files changed

+1645
-159
lines changed

compiler/rustc_borrowck/src/type_check/constraint_conversion.rs

Lines changed: 25 additions & 8 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,15 +115,20 @@ 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
}
119126

120127
fn convert(
121128
&mut self,
122-
predicate: ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
129+
predicate: ty::ArgOutlivesPredicate<'tcx>,
123130
constraint_category: ConstraintCategory<'tcx>,
131+
higher_ranked_assumptions: &FxHashSet<ty::ArgOutlivesPredicate<'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);
@@ -266,14 +282,15 @@ impl<'a, 'tcx> ConstraintConversion<'a, 'tcx> {
266282
&self,
267283
ty: Ty<'tcx>,
268284
next_outlives_predicates: &mut Vec<(
269-
ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
285+
ty::ArgOutlivesPredicate<'tcx>,
270286
ConstraintCategory<'tcx>,
271287
)>,
272288
) -> Ty<'tcx> {
273289
match self.param_env.and(DeeplyNormalize { value: ty }).fully_perform(self.infcx, self.span)
274290
{
275291
Ok(TypeOpOutput { output: ty, constraints, .. }) => {
276-
if let Some(QueryRegionConstraints { outlives }) = constraints {
292+
// FIXME(higher_ranked_auto): What should we do with the assumptions here?
293+
if let Some(QueryRegionConstraints { outlives, assumptions: _ }) = constraints {
277294
next_outlives_predicates.extend(outlives.iter().copied());
278295
}
279296
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_hir_analysis/src/outlives/utils.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ use smallvec::smallvec;
77

88
/// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred
99
/// must be added to the struct header.
10-
pub(crate) type RequiredPredicates<'tcx> =
11-
FxIndexMap<ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>, Span>;
10+
pub(crate) type RequiredPredicates<'tcx> = FxIndexMap<ty::ArgOutlivesPredicate<'tcx>, Span>;
1211

1312
/// Given a requirement `T: 'a` or `'b: 'a`, deduce the
1413
/// outlives_component and add it to `required_predicates`

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,15 @@ impl<'tcx> InferCtxt<'tcx> {
116116
}
117117

118118
let region_obligations = self.take_registered_region_obligations();
119+
let region_assumptions = self.take_registered_region_assumptions();
119120
debug!(?region_obligations);
120121
let region_constraints = self.with_region_constraints(|region_constraints| {
121-
make_query_region_constraints(tcx, region_obligations, region_constraints)
122+
make_query_region_constraints(
123+
tcx,
124+
region_obligations,
125+
region_constraints,
126+
region_assumptions,
127+
)
122128
});
123129
debug!(?region_constraints);
124130

@@ -169,6 +175,11 @@ impl<'tcx> InferCtxt<'tcx> {
169175
self.register_outlives_constraint(predicate, cause);
170176
}
171177

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

@@ -292,6 +303,18 @@ impl<'tcx> InferCtxt<'tcx> {
292303
}),
293304
);
294305

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

@@ -567,6 +590,7 @@ pub fn make_query_region_constraints<'tcx>(
567590
tcx: TyCtxt<'tcx>,
568591
outlives_obligations: Vec<TypeOutlivesConstraint<'tcx>>,
569592
region_constraints: &RegionConstraintData<'tcx>,
593+
assumptions: Vec<ty::ArgOutlivesPredicate<'tcx>>,
570594
) -> QueryRegionConstraints<'tcx> {
571595
let RegionConstraintData { constraints, verifys } = region_constraints;
572596

@@ -602,5 +626,5 @@ pub fn make_query_region_constraints<'tcx>(
602626
}))
603627
.collect();
604628

605-
QueryRegionConstraints { outlives }
629+
QueryRegionConstraints { outlives, assumptions }
606630
}

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::ArgOutlivesPredicate<'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::ArgOutlivesPredicate<'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: 10 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::ArgOutlivesPredicate<'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::ArgOutlivesPredicate<'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,8 @@ 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(&self) -> &FxHashSet<ty::ArgOutlivesPredicate<'tcx>> {
112+
&self.higher_ranked_assumptions
113+
}
105114
}

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: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ use crate::traits::{ObligationCause, ObligationCauseCode};
8484
impl<'tcx> InferCtxt<'tcx> {
8585
pub fn register_outlives_constraint(
8686
&self,
87-
ty::OutlivesPredicate(arg, r2): ty::OutlivesPredicate<'tcx, ty::GenericArg<'tcx>>,
87+
ty::OutlivesPredicate(arg, r2): ty::ArgOutlivesPredicate<'tcx>,
8888
cause: &ObligationCause<'tcx>,
8989
) {
9090
match arg.kind() {
@@ -170,6 +170,16 @@ impl<'tcx> InferCtxt<'tcx> {
170170
std::mem::take(&mut self.inner.borrow_mut().region_obligations)
171171
}
172172

173+
pub fn register_region_assumption(&self, assumption: ty::ArgOutlivesPredicate<'tcx>) {
174+
let mut inner = self.inner.borrow_mut();
175+
inner.undo_log.push(UndoLog::PushRegionAssumption);
176+
inner.region_assumptions.push(assumption);
177+
}
178+
179+
pub fn take_registered_region_assumptions(&self) -> Vec<ty::ArgOutlivesPredicate<'tcx>> {
180+
std::mem::take(&mut self.inner.borrow_mut().region_assumptions)
181+
}
182+
173183
/// Process the region obligations that must be proven (during
174184
/// `regionck`) for the given `body_id`, given information about
175185
/// the region bounds in scope and so forth.
@@ -220,6 +230,14 @@ impl<'tcx> InferCtxt<'tcx> {
220230
let (sup_type, sub_region) =
221231
(sup_type, sub_region).fold_with(&mut OpportunisticRegionResolver::new(self));
222232

233+
if self.tcx.sess.opts.unstable_opts.higher_ranked_assumptions
234+
&& outlives_env
235+
.higher_ranked_assumptions()
236+
.contains(&ty::OutlivesPredicate(sup_type.into(), sub_region))
237+
{
238+
continue;
239+
}
240+
223241
debug!(?sup_type, ?sub_region, ?origin);
224242

225243
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
@@ -28,6 +28,7 @@ pub(crate) enum UndoLog<'tcx> {
2828
RegionUnificationTable(sv::UndoLog<ut::Delegate<RegionVidKey<'tcx>>>),
2929
ProjectionCache(traits::UndoLog<'tcx>),
3030
PushTypeOutlivesConstraint,
31+
PushRegionAssumption,
3132
}
3233

3334
macro_rules! impl_from {
@@ -77,6 +78,9 @@ impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
7778
let popped = self.region_obligations.pop();
7879
assert_matches!(popped, Some(_), "pushed region constraint but could not pop it");
7980
}
81+
UndoLog::PushRegionAssumption => {
82+
self.region_assumptions.pop();
83+
}
8084
}
8185
}
8286
}

compiler/rustc_middle/src/infer/canonical.rs

Lines changed: 9 additions & 5 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::ArgOutlivesPredicate<'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

@@ -130,8 +135,7 @@ impl<'tcx, R> QueryResponse<'tcx, R> {
130135
}
131136
}
132137

133-
pub type QueryOutlivesConstraint<'tcx> =
134-
(ty::OutlivesPredicate<'tcx, GenericArg<'tcx>>, ConstraintCategory<'tcx>);
138+
pub type QueryOutlivesConstraint<'tcx> = (ty::ArgOutlivesPredicate<'tcx>, ConstraintCategory<'tcx>);
135139

136140
#[derive(Default)]
137141
pub struct CanonicalParamEnvCache<'tcx> {

0 commit comments

Comments
 (0)