Skip to content

Commit 2aebebf

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 00f75b6 + b3054d2 commit 2aebebf

30 files changed

+31
-238
lines changed

compiler/rustc_feature/src/accepted.rs

Lines changed: 2 additions & 0 deletions
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

Lines changed: 0 additions & 3 deletions
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

Lines changed: 0 additions & 66 deletions
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

Lines changed: 0 additions & 29 deletions
This file was deleted.

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

Lines changed: 3 additions & 4 deletions
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

Lines changed: 0 additions & 20 deletions
This file was deleted.

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

Lines changed: 0 additions & 43 deletions
This file was deleted.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: lifetime may not live long enough
2-
--> $DIR/ret-impl-trait-one.rs:12:80
2+
--> $DIR/ret-impl-trait-one.rs:10:80
33
|
44
LL | async fn async_ret_impl_trait1<'a, 'b>(a: &'a u8, b: &'b u8) -> impl Trait<'a> {
55
| ________________________________--__--__________________________________________^

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

Lines changed: 0 additions & 2 deletions
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

Lines changed: 1 addition & 1 deletion
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
| ------ ^^^^^^^^^^^^^^

0 commit comments

Comments
 (0)