Skip to content

Commit cae27c0

Browse files
authored
Rollup merge of rust-lang#84701 - nikomatsakis:stabilize-member-constraints-61997, r=jackh726
stabilize member constraints Stabilizes the use of "member constraints" in solving `impl Trait` bindings. This is a step towards stabilizing a "MVP" of "named impl Trait". # Member constraint stabilization report | Info | | | --- | --- | | Tracking issue | [rust-lang#61997](rust-lang#61997) | | Implementation history | [rust-lang#61775] | | rustc-dev-guide coverage | [link](https://rustc-dev-guide.rust-lang.org/borrow_check/region_inference/member_constraints.html) | | Complications | [rust-lang#61773] | [rust-lang#61775]: rust-lang#61775 [rust-lang#61773]: rust-lang#61773 ## Background Member constraints are an extension to our region solver that was introduced to make async fn region solving tractable. There are used in situations like the following: ```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`). This cannot be expressed simply via 'outlives constriants' like `R: 'a`. Therefore, we introduce a 'member constraint' `R member of ['a, 'b]`. These constraints were introduced in [rust-lang#61775]. At the time, we kept them feature gated and used them only for `impl Trait` return types that are derived from `async fn`. The intention, however, was always to support them in other contexts once we had time to gain more experience with them. **In the time since their introduction, we have encountered no surprises or bugs due to these member constraints.** They are tested extensively as part of every async function that involves multiple unrelated lifetimes in its arguments. ## Tests The behavior of member constraints is covered by the following tests: * [`src/test/ui/async-await/multiple-lifetimes`](https://github.com/rust-lang/rust/tree/20e032e65007ff1376e8480c1fbdb0a5068028fa/src/test/ui/async-await/multiple-lifetimes) -- tests using the async await, which are mostly already stabilized * [`src/test/ui/impl-trait/multiple-lifetimes.rs`](https://github.com/rust-lang/rust/blob/20e032e65007ff1376e8480c1fbdb0a5068028fa/src/test/ui/impl-trait/multiple-lifetimes.rs) * [`src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs`](https://github.com/rust-lang/rust/blob/20e032e65007ff1376e8480c1fbdb0a5068028fa/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs) * [`src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-fg.rs`](https://github.com/rust-lang/rust/blob/20e032e65007ff1376e8480c1fbdb0a5068028fa/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-fg.rs) * [`src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs`](https://github.com/rust-lang/rust/blob/20e032e65007ff1376e8480c1fbdb0a5068028fa/src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs) These tests cover a number of scenarios: * `-> implTrait<'a, 'b>` with unrelated lifetimes `'a` and `'b`, as described above * `async fn` that returns an `impl Trait` like the previous case, which desugars to a kind of "nested" impl trait like `impl Future<Output = impl Trait<'a, 'b>>` ## Potential concerns There is a potential interaction with `impl Trait` on local variables, described in [rust-lang#61773]. The challenge is that if you have a program like: ```rust= trait Foo<'_> { } impl Foo<'_> for &u32 { } fn bar() { let x: impl Foo<'_> = &44; // let's call the region variable for `'_` `'1` } ``` then we would wind up with `'0 member of ['1, 'static]`, where `'0` is the region variable in the hidden type (`&'0 u32`) and `'1` is the region variable in the bounds `Foo<'1>`. This is tricky because both `'0` and `'1` are being inferred -- so making them equal may have other repercussions. That said, `impl Trait` in bindings are not stable, and the implementation is pretty far from stabilization. Moreover, the difficulty highlighted here is not due to the presence of member constraints -- it's inherent to the design of the language. In other words, stabilizing member constraints does not actually cause us to accept anything that would make this problem any harder. So I don't see this as a blocker to stabilization of member constraints; it is potentially a blocker to stablization of `impl trait` in let bindings.
2 parents ff2c947 + a937f25 commit cae27c0

27 files changed

+28
-235
lines changed

compiler/rustc_feature/src/accepted.rs

+2
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,8 @@ declare_features! (
285285
(accepted, extended_key_value_attributes, "1.54.0", Some(78835), None),
286286
/// Allows unsizing coercions in `const fn`.
287287
(accepted, const_fn_unsize, "1.54.0", Some(64992), None),
288+
/// Allows `impl Trait` with multiple unrelated lifetimes.
289+
(accepted, member_constraints, "1.54.0", Some(61997), None),
288290

289291
// -------------------------------------------------------------------------
290292
// feature-group-end: accepted features

compiler/rustc_feature/src/active.rs

-3
Original file line numberDiff line numberDiff line change
@@ -472,9 +472,6 @@ declare_features! (
472472
/// Allows explicit discriminants on non-unit enum variants.
473473
(active, arbitrary_enum_discriminant, "1.37.0", Some(60553), None),
474474

475-
/// Allows `impl Trait` with multiple unrelated lifetimes.
476-
(active, member_constraints, "1.37.0", Some(61997), None),
477-
478475
/// Allows `async || body` closures.
479476
(active, async_closure, "1.37.0", Some(62290), None),
480477

compiler/rustc_trait_selection/src/opaque_types.rs

-66
Original file line numberDiff line numberDiff line change
@@ -140,15 +140,6 @@ pub trait InferCtxtExt<'tcx> {
140140
first_own_region_index: usize,
141141
);
142142

143-
/*private*/
144-
fn member_constraint_feature_gate(
145-
&self,
146-
opaque_defn: &OpaqueTypeDecl<'tcx>,
147-
opaque_type_def_id: DefId,
148-
conflict1: ty::Region<'tcx>,
149-
conflict2: ty::Region<'tcx>,
150-
) -> bool;
151-
152143
fn infer_opaque_definition_from_instantiation(
153144
&self,
154145
def_id: DefId,
@@ -490,9 +481,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
490481
// ['a, 'b, 'c]`, where `'a..'c` are the
491482
// regions that appear in the impl trait.
492483

493-
// For now, enforce a feature gate outside of async functions.
494-
self.member_constraint_feature_gate(opaque_defn, def_id, lr, subst_region);
495-
496484
return self.generate_member_constraint(
497485
concrete_ty,
498486
opaque_defn,
@@ -559,60 +547,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
559547
});
560548
}
561549

562-
/// Member constraints are presently feature-gated except for
563-
/// async-await. We expect to lift this once we've had a bit more
564-
/// time.
565-
fn member_constraint_feature_gate(
566-
&self,
567-
opaque_defn: &OpaqueTypeDecl<'tcx>,
568-
opaque_type_def_id: DefId,
569-
conflict1: ty::Region<'tcx>,
570-
conflict2: ty::Region<'tcx>,
571-
) -> bool {
572-
// If we have `#![feature(member_constraints)]`, no problems.
573-
if self.tcx.features().member_constraints {
574-
return false;
575-
}
576-
577-
let span = self.tcx.def_span(opaque_type_def_id);
578-
579-
// Without a feature-gate, we only generate member-constraints for async-await.
580-
let context_name = match opaque_defn.origin {
581-
// No feature-gate required for `async fn`.
582-
hir::OpaqueTyOrigin::AsyncFn => return false,
583-
584-
// Otherwise, generate the label we'll use in the error message.
585-
hir::OpaqueTyOrigin::Binding
586-
| hir::OpaqueTyOrigin::FnReturn
587-
| hir::OpaqueTyOrigin::TyAlias
588-
| hir::OpaqueTyOrigin::Misc => "impl Trait",
589-
};
590-
let msg = format!("ambiguous lifetime bound in `{}`", context_name);
591-
let mut err = self.tcx.sess.struct_span_err(span, &msg);
592-
593-
let conflict1_name = conflict1.to_string();
594-
let conflict2_name = conflict2.to_string();
595-
let label_owned;
596-
let label = match (&*conflict1_name, &*conflict2_name) {
597-
("'_", "'_") => "the elided lifetimes here do not outlive one another",
598-
_ => {
599-
label_owned = format!(
600-
"neither `{}` nor `{}` outlives the other",
601-
conflict1_name, conflict2_name,
602-
);
603-
&label_owned
604-
}
605-
};
606-
err.span_label(span, label);
607-
608-
if self.tcx.sess.is_nightly_build() {
609-
err.help("add #![feature(member_constraints)] to the crate attributes to enable");
610-
}
611-
612-
err.emit();
613-
true
614-
}
615-
616550
/// Given the fully resolved, instantiated type for an opaque
617551
/// type, i.e., the value of an inference variable like C1 or C2
618552
/// (*), computes the "definition type" for an opaque type

src/doc/unstable-book/src/language-features/member-constraints.md

-29
This file was deleted.

src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-fg.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
// edition:2018
22
// run-pass
33

4-
// Test that a feature gate is needed to use `impl Trait` as the
5-
// return type of an async.
6-
7-
#![feature(member_constraints)]
4+
// Test member constraints that appear in the `impl Trait`
5+
// return type of an async function.
6+
// (This used to require a feature gate.)
87

98
trait Trait<'a, 'b> { }
109
impl<T> Trait<'_, '_> for T { }

src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.rs

-20
This file was deleted.

src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-no-fg.stderr

-43
This file was deleted.

src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.rs

-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
// Test that a feature gate is needed to use `impl Trait` as the
44
// return type of an async.
55

6-
#![feature(member_constraints)]
7-
86
trait Trait<'a> { }
97
impl<T> Trait<'_> for T { }
108

src/test/ui/async-await/multiple-lifetimes/ret-impl-trait-one.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0623]: lifetime mismatch
2-
--> $DIR/ret-impl-trait-one.rs:12:65
2+
--> $DIR/ret-impl-trait-one.rs:10:65
33
|
44
LL | async fn async_ret_impl_trait1<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> {
55
| ------ ^^^^^^^^^^^^^^

src/test/ui/feature-gates/feature-gate-member-constraints.rs

-10
This file was deleted.

src/test/ui/feature-gates/feature-gate-member-constraints.stderr

-18
This file was deleted.

src/test/ui/impl-trait/multiple-lifetimes/error-handling.full_tait.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
warning: the feature `type_alias_impl_trait` is incomplete and may not be safe to use and/or cause compiler crashes
2-
--> $DIR/error-handling.rs:6:32
2+
--> $DIR/error-handling.rs:5:32
33
|
44
LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))]
55
| ^^^^^^^^^^^^^^^^^^^^^
@@ -8,7 +8,7 @@ LL | #![cfg_attr(full_tait, feature(type_alias_impl_trait))]
88
= note: see issue #63063 <https://github.com/rust-lang/rust/issues/63063> for more information
99

1010
error: lifetime may not live long enough
11-
--> $DIR/error-handling.rs:26:16
11+
--> $DIR/error-handling.rs:25:16
1212
|
1313
LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> {
1414
| -- -- lifetime `'b` defined here

src/test/ui/impl-trait/multiple-lifetimes/error-handling.min_tait.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: lifetime may not live long enough
2-
--> $DIR/error-handling.rs:26:16
2+
--> $DIR/error-handling.rs:25:16
33
|
44
LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> {
55
| -- -- lifetime `'b` defined here

src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// compile-flags:-Zborrowck=mir
22

3-
#![feature(member_constraints)]
43
// revisions: min_tait full_tait
54
#![feature(min_type_alias_impl_trait)]
65
#![cfg_attr(full_tait, feature(type_alias_impl_trait))]

src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs

-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
// revisions: migrate mir
44
//[mir]compile-flags: -Z borrowck=mir
55

6-
#![feature(member_constraints)]
7-
86
trait Trait<'a, 'b> {}
97
impl<T> Trait<'_, '_> for T {}
108

src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
// revisions: migrate mir
44
//[mir]compile-flags: -Z borrowck=mir
55

6-
#![feature(member_constraints)]
7-
8-
trait Trait<'a, 'b> { }
9-
impl<T> Trait<'_, '_> for T { }
6+
trait Trait<'a, 'b> {}
7+
impl<T> Trait<'_, '_> for T {}
108

119
// Test case where we have elision in the impl trait and we have to
1210
// pick the right region.
@@ -26,4 +24,4 @@ fn upper_bounds3<'b>(a: &u8) -> impl Trait<'_, 'b> {
2624
(a, a)
2725
}
2826

29-
fn main() { }
27+
fn main() {}

src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-type-alias-impl-trait.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,9 @@
33
// revisions: migrate mir
44
//[mir]compile-flags: -Z borrowck=mir
55

6-
#![feature(member_constraints)]
76
#![feature(min_type_alias_impl_trait)]
8-
trait Trait<'a, 'b> { }
9-
impl<T> Trait<'_, '_> for T { }
7+
trait Trait<'a, 'b> {}
8+
impl<T> Trait<'_, '_> for T {}
109

1110
// Here we wind up selecting `'a` and `'b` in the hidden type because
1211
// those are the types that appear in the original values.
@@ -28,4 +27,4 @@ fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> Foo<'a, 'b> {
2827
(a, b)
2928
}
3029

31-
fn main() { }
30+
fn main() {}

src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@
33
// revisions: migrate mir
44
//[mir]compile-flags: -Z borrowck=mir
55

6-
#![feature(member_constraints)]
7-
8-
trait Trait<'a, 'b> { }
9-
impl<T> Trait<'_, '_> for T { }
6+
trait Trait<'a, 'b> {}
7+
impl<T> Trait<'_, '_> for T {}
108

119
// Here we wind up selecting `'a` and `'b` in the hidden type because
1210
// those are the types that appear in the original values.
@@ -26,4 +24,4 @@ fn upper_bounds<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a, 'b> {
2624
(a, b)
2725
}
2826

29-
fn main() { }
27+
fn main() {}

src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs

-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
// revisions: migrate mir
44
//[mir]compile-flags: -Z borrowck=mir
55

6-
#![feature(member_constraints)]
7-
86
trait Trait<'a, 'b> {}
97
impl<T> Trait<'_, '_> for T {}
108

src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
// edition:2018
22

3-
#![feature(member_constraints)]
4-
53
trait Trait<'a, 'b> {}
64
impl<T> Trait<'_, '_> for T {}
75

src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
2-
--> $DIR/ordinary-bounds-unrelated.rs:18:74
2+
--> $DIR/ordinary-bounds-unrelated.rs:16:74
33
|
44
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e>
55
| ^^^^^^^^^^^^^^^^^^
66
|
77
note: hidden type `Ordinary<'_>` captures lifetime smaller than the function body
8-
--> $DIR/ordinary-bounds-unrelated.rs:18:74
8+
--> $DIR/ordinary-bounds-unrelated.rs:16:74
99
|
1010
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e>
1111
| ^^^^^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)