Skip to content

Commit 8301de1

Browse files
committed
Auto merge of #61775 - nikomatsakis:issue-56238-multiple-lifetimes-async-fn-region-solver, r=MatthewJasper
generalize impl trait to permit multiple lifetime bounds Generalizes the region solver to support "pick constraints". These have the form: ``` pick R0 from [R1..Rn] ``` where `R1..Rn` are called the "option regions". The idea is that `R0` must be equal to *some* region in the set `R1..Rn`. These constraints are then used to handle cases like this: ```rust fn foo<'a, 'b>(...) -> impl Trait<'a, 'b> { .. } ``` The problem here is that every region R in the hidden type must be equal to *either* `'a` *or* `'b` (or `'static`) -- in the past, the only kinds of constraints we had were outlives constraints, and since `'a` and `'b` are unrelated, there was no outlives constraint we could issue that would enforce that (`R: 'a` and `R: 'b` are both too strict, for example). But now we can issue a pick constraint: `pick R from ['a, 'b]`. In general, solving pick constraints is tricky. We integrate them into the solver as follows. In general, during the propagation phase, we are monotonically growing a set of inference regions. To handle a case like `pick R from [O...]`, where `O...` represents the option regions, we do the following: - Look for all the *lower bounds* of the region R -- that is, every region LB such that `R: LB` must hold. - Look for all the *upper bounds* of the region R -- that is, every region UB such that `UB: R` must hold. - Let the *viable options* be each option region O such that `UB: O` and `O: LB` for each UB, LB bound. - Find the *minimal viable option* M, where `O: M` holds for every option region O. If there is such a *minimal viable option*, then we make `R: M`. (This may in turn influence other bits of inference.) If there is no minimal viable option, either because all options were eliminated or because none of the remaining options are minimal, we do nothing. Ultimately, if the pick constraint is not satisfied, an error is reported. For this logic, we currently require that the option regions O are always lifetime parameters. To determine the bounds, we walk the various outlives edges that were otherwise introduced. r? @matthewjasper cc @cramertj Fixes #56238 TODO: - [ ] Error messages include region variable info sometimes, how to fix? - [ ] Tests for bare `existential type` and other impl Trait usage
2 parents 0beb2ba + f7e00a5 commit 8301de1

File tree

71 files changed

+2580
-548
lines changed

Some content is hidden

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

71 files changed

+2580
-548
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# `member_constraints`
2+
3+
The tracking issue for this feature is: [#61977]
4+
5+
[#61977]: https://github.com/rust-lang/rust/issues/61977
6+
7+
------------------------
8+
9+
The `member_constraints` feature gate lets you use `impl Trait` syntax with
10+
multiple unrelated lifetime parameters.
11+
12+
A simple example is:
13+
14+
```rust
15+
#![feature(member_constraints)]
16+
17+
trait Trait<'a, 'b> { }
18+
impl<T> Trait<'_, '_> for T {}
19+
20+
fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> {
21+
(x, y)
22+
}
23+
24+
fn main() { }
25+
```
26+
27+
Without the `member_constraints` feature gate, the above example is an
28+
error because both `'a` and `'b` appear in the impl Trait bounds, but
29+
neither outlives the other.

Diff for: src/librustc/infer/canonical/mod.rs

+31-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
2424
use crate::infer::{InferCtxt, RegionVariableOrigin, TypeVariableOrigin, TypeVariableOriginKind};
2525
use crate::infer::{ConstVariableOrigin, ConstVariableOriginKind};
26+
use crate::infer::region_constraints::MemberConstraint;
2627
use crate::mir::interpret::ConstValue;
2728
use rustc_data_structures::indexed_vec::IndexVec;
2829
use rustc_macros::HashStable;
@@ -189,11 +190,25 @@ pub enum CanonicalTyVarKind {
189190
#[derive(Clone, Debug, HashStable)]
190191
pub struct QueryResponse<'tcx, R> {
191192
pub var_values: CanonicalVarValues<'tcx>,
192-
pub region_constraints: Vec<QueryRegionConstraint<'tcx>>,
193+
pub region_constraints: QueryRegionConstraints<'tcx>,
193194
pub certainty: Certainty,
194195
pub value: R,
195196
}
196197

198+
#[derive(Clone, Debug, Default, HashStable)]
199+
pub struct QueryRegionConstraints<'tcx> {
200+
pub outlives: Vec<QueryOutlivesConstraint<'tcx>>,
201+
pub member_constraints: Vec<MemberConstraint<'tcx>>,
202+
}
203+
204+
impl QueryRegionConstraints<'_> {
205+
/// Represents an empty (trivially true) set of region
206+
/// constraints.
207+
pub fn is_empty(&self) -> bool {
208+
self.outlives.is_empty() && self.member_constraints.is_empty()
209+
}
210+
}
211+
197212
pub type Canonicalized<'tcx, V> = Canonical<'tcx, V>;
198213

199214
pub type CanonicalizedQueryResponse<'tcx, T> =
@@ -292,7 +307,8 @@ impl<'tcx, V> Canonical<'tcx, V> {
292307
}
293308
}
294309

295-
pub type QueryRegionConstraint<'tcx> = ty::Binder<ty::OutlivesPredicate<Kind<'tcx>, Region<'tcx>>>;
310+
pub type QueryOutlivesConstraint<'tcx> =
311+
ty::Binder<ty::OutlivesPredicate<Kind<'tcx>, Region<'tcx>>>;
296312

297313
impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
298314
/// Creates a substitution S for the canonical value with fresh
@@ -540,6 +556,19 @@ BraceStructLiftImpl! {
540556
} where R: Lift<'tcx>
541557
}
542558

559+
BraceStructTypeFoldableImpl! {
560+
impl<'tcx> TypeFoldable<'tcx> for QueryRegionConstraints<'tcx> {
561+
outlives, member_constraints
562+
}
563+
}
564+
565+
BraceStructLiftImpl! {
566+
impl<'a, 'tcx> Lift<'tcx> for QueryRegionConstraints<'a> {
567+
type Lifted = QueryRegionConstraints<'tcx>;
568+
outlives, member_constraints
569+
}
570+
}
571+
543572
impl<'tcx> Index<BoundVar> for CanonicalVarValues<'tcx> {
544573
type Output = Kind<'tcx>;
545574

Diff for: src/librustc/infer/canonical/query_response.rs

+28-18
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::arena::ArenaAllocatable;
1111
use crate::infer::canonical::substitute::substitute_value;
1212
use crate::infer::canonical::{
1313
Canonical, CanonicalVarValues, CanonicalizedQueryResponse, Certainty,
14-
OriginalQueryValues, QueryRegionConstraint, QueryResponse,
14+
OriginalQueryValues, QueryRegionConstraints, QueryOutlivesConstraint, QueryResponse,
1515
};
1616
use crate::infer::region_constraints::{Constraint, RegionConstraintData};
1717
use crate::infer::InferCtxtBuilder;
@@ -132,7 +132,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
132132
{
133133
self.canonicalize_response(&QueryResponse {
134134
var_values: inference_vars,
135-
region_constraints: vec![],
135+
region_constraints: QueryRegionConstraints::default(),
136136
certainty: Certainty::Proven, // Ambiguities are OK!
137137
value: answer,
138138
})
@@ -174,7 +174,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
174174

175175
let region_obligations = self.take_registered_region_obligations();
176176
let region_constraints = self.with_region_constraints(|region_constraints| {
177-
make_query_outlives(
177+
make_query_region_constraints(
178178
tcx,
179179
region_obligations
180180
.iter()
@@ -222,10 +222,10 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
222222
mut obligations,
223223
} = self.query_response_substitution(cause, param_env, original_values, query_response)?;
224224

225-
obligations.extend(self.query_region_constraints_into_obligations(
225+
obligations.extend(self.query_outlives_constraints_into_obligations(
226226
cause,
227227
param_env,
228-
&query_response.value.region_constraints,
228+
&query_response.value.region_constraints.outlives,
229229
&result_subst,
230230
));
231231

@@ -248,9 +248,9 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
248248
/// that come out of these queries, which it wants to convert into
249249
/// MIR-based constraints and solve. Therefore, it is most
250250
/// convenient for the NLL Type Checker to **directly consume**
251-
/// the `QueryRegionConstraint` values that arise from doing a
251+
/// the `QueryOutlivesConstraint` values that arise from doing a
252252
/// query. This is contrast to other parts of the compiler, which
253-
/// would prefer for those `QueryRegionConstraint` to be converted
253+
/// would prefer for those `QueryOutlivesConstraint` to be converted
254254
/// into the older infcx-style constraints (e.g., calls to
255255
/// `sub_regions` or `register_region_obligation`).
256256
///
@@ -263,7 +263,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
263263
/// result. If any errors arise, they are propagated back as an
264264
/// `Err` result.
265265
/// - In the case of a successful substitution, we will append
266-
/// `QueryRegionConstraint` values onto the
266+
/// `QueryOutlivesConstraint` values onto the
267267
/// `output_query_region_constraints` vector for the solver to
268268
/// use (if an error arises, some values may also be pushed, but
269269
/// they should be ignored).
@@ -279,15 +279,15 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
279279
param_env: ty::ParamEnv<'tcx>,
280280
original_values: &OriginalQueryValues<'tcx>,
281281
query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>,
282-
output_query_region_constraints: &mut Vec<QueryRegionConstraint<'tcx>>,
282+
output_query_region_constraints: &mut QueryRegionConstraints<'tcx>,
283283
) -> InferResult<'tcx, R>
284284
where
285285
R: Debug + TypeFoldable<'tcx>,
286286
{
287287
let result_subst =
288288
self.query_response_substitution_guess(cause, original_values, query_response);
289289

290-
// Compute `QueryRegionConstraint` values that unify each of
290+
// Compute `QueryOutlivesConstraint` values that unify each of
291291
// the original values `v_o` that was canonicalized into a
292292
// variable...
293293
let mut obligations = vec![];
@@ -306,8 +306,10 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
306306
// To make `v_o = v_r`, we emit `v_o: v_r` and `v_r: v_o`.
307307
if v_o != v_r {
308308
output_query_region_constraints
309+
.outlives
309310
.push(ty::Binder::dummy(ty::OutlivesPredicate(v_o.into(), v_r)));
310311
output_query_region_constraints
312+
.outlives
311313
.push(ty::Binder::dummy(ty::OutlivesPredicate(v_r.into(), v_o)));
312314
}
313315
}
@@ -333,12 +335,12 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
333335
}
334336

335337
// ...also include the other query region constraints from the query.
336-
output_query_region_constraints.extend(
337-
query_response.value.region_constraints.iter().filter_map(|r_c| {
338+
output_query_region_constraints.outlives.extend(
339+
query_response.value.region_constraints.outlives.iter().filter_map(|r_c| {
338340
let r_c = substitute_value(self.tcx, &result_subst, r_c);
339341

340342
// Screen out `'a: 'a` cases -- we skip the binder here but
341-
// only care the inner values to one another, so they are still at
343+
// only compare the inner values to one another, so they are still at
342344
// consistent binding levels.
343345
let &ty::OutlivesPredicate(k1, r2) = r_c.skip_binder();
344346
if k1 != r2.into() {
@@ -349,6 +351,13 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
349351
})
350352
);
351353

354+
// ...also include the query member constraints.
355+
output_query_region_constraints.member_constraints.extend(
356+
query_response.value.region_constraints.member_constraints.iter().map(|p_c| {
357+
substitute_value(self.tcx, &result_subst, p_c)
358+
})
359+
);
360+
352361
let user_result: R =
353362
query_response.substitute_projected(self.tcx, &result_subst, |q_r| &q_r.value);
354363

@@ -560,11 +569,11 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
560569

561570
/// Converts the region constraints resulting from a query into an
562571
/// iterator of obligations.
563-
fn query_region_constraints_into_obligations<'a>(
572+
fn query_outlives_constraints_into_obligations<'a>(
564573
&'a self,
565574
cause: &'a ObligationCause<'tcx>,
566575
param_env: ty::ParamEnv<'tcx>,
567-
unsubstituted_region_constraints: &'a [QueryRegionConstraint<'tcx>],
576+
unsubstituted_region_constraints: &'a [QueryOutlivesConstraint<'tcx>],
568577
result_subst: &'a CanonicalVarValues<'tcx>,
569578
) -> impl Iterator<Item = PredicateObligation<'tcx>> + 'a + Captures<'tcx> {
570579
unsubstituted_region_constraints
@@ -645,15 +654,16 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
645654

646655
/// Given the region obligations and constraints scraped from the infcx,
647656
/// creates query region constraints.
648-
pub fn make_query_outlives<'tcx>(
657+
pub fn make_query_region_constraints<'tcx>(
649658
tcx: TyCtxt<'tcx>,
650659
outlives_obligations: impl Iterator<Item = (Ty<'tcx>, ty::Region<'tcx>)>,
651660
region_constraints: &RegionConstraintData<'tcx>,
652-
) -> Vec<QueryRegionConstraint<'tcx>> {
661+
) -> QueryRegionConstraints<'tcx> {
653662
let RegionConstraintData {
654663
constraints,
655664
verifys,
656665
givens,
666+
member_constraints,
657667
} = region_constraints;
658668

659669
assert!(verifys.is_empty());
@@ -684,5 +694,5 @@ pub fn make_query_outlives<'tcx>(
684694
)
685695
.collect();
686696

687-
outlives
697+
QueryRegionConstraints { outlives, member_constraints: member_constraints.clone() }
688698
}

Diff for: src/librustc/infer/error_reporting/mod.rs

+21-1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ use crate::infer::{self, SuppressRegionErrors};
5353
use crate::hir;
5454
use crate::hir::def_id::DefId;
5555
use crate::hir::Node;
56+
use crate::infer::opaque_types;
5657
use crate::middle::region;
5758
use crate::traits::{ObligationCause, ObligationCauseCode};
5859
use crate::ty::error::TypeError;
@@ -375,6 +376,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
375376
);
376377
}
377378
}
379+
380+
RegionResolutionError::MemberConstraintFailure {
381+
opaque_type_def_id,
382+
hidden_ty,
383+
member_region,
384+
span: _,
385+
choice_regions: _,
386+
} => {
387+
let hidden_ty = self.resolve_vars_if_possible(&hidden_ty);
388+
opaque_types::unexpected_hidden_region_diagnostic(
389+
self.tcx,
390+
Some(region_scope_tree),
391+
opaque_type_def_id,
392+
hidden_ty,
393+
member_region,
394+
).emit();
395+
}
378396
}
379397
}
380398
}
@@ -411,7 +429,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
411429
let is_bound_failure = |e: &RegionResolutionError<'tcx>| match *e {
412430
RegionResolutionError::GenericBoundFailure(..) => true,
413431
RegionResolutionError::ConcreteFailure(..)
414-
| RegionResolutionError::SubSupConflict(..) => false,
432+
| RegionResolutionError::SubSupConflict(..)
433+
| RegionResolutionError::MemberConstraintFailure { .. } => false,
415434
};
416435

417436
let mut errors = if errors.iter().all(|e| is_bound_failure(e)) {
@@ -429,6 +448,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
429448
RegionResolutionError::ConcreteFailure(ref sro, _, _) => sro.span(),
430449
RegionResolutionError::GenericBoundFailure(ref sro, _, _) => sro.span(),
431450
RegionResolutionError::SubSupConflict(_, ref rvo, _, _, _, _) => rvo.span(),
451+
RegionResolutionError::MemberConstraintFailure { span, .. } => span,
432452
});
433453
errors
434454
}

0 commit comments

Comments
 (0)