feature-gate member constraints outside of async-await
Minimizes risk.
This commit is contained in:
parent
cbc75c699c
commit
74a6efbf00
18 changed files with 147 additions and 7 deletions
|
@ -0,0 +1,29 @@
|
|||
# `member_constraints`
|
||||
|
||||
The tracking issue for this feature is: [#61977]
|
||||
|
||||
[#61977]: https://github.com/rust-lang/rust/issues/61977
|
||||
|
||||
------------------------
|
||||
|
||||
The `member_constraints` feature gate lets you use `impl Trait` syntax with
|
||||
multiple unrelated lifetime parameters.
|
||||
|
||||
A simple example is:
|
||||
|
||||
```rust
|
||||
#![feature(member_constraints)]
|
||||
|
||||
trait Trait { }
|
||||
impl<T> Trait<'_, '_> for T {}
|
||||
|
||||
fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> {
|
||||
(x, y)
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
```
|
||||
|
||||
Without the `member_constraints` feature gate, the above example is an
|
||||
error because both `'a` and `'b` appear in the impl Trait bounds, but
|
||||
neither outlives the other.
|
|
@ -10,6 +10,7 @@ use crate::ty::subst::{InternalSubsts, Kind, SubstsRef, UnpackedKind};
|
|||
use crate::ty::{self, GenericParamDefKind, Ty, TyCtxt};
|
||||
use crate::util::nodemap::DefIdMap;
|
||||
use errors::DiagnosticBuilder;
|
||||
use rustc::session::config::nightly_options;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use syntax_pos::Span;
|
||||
|
@ -398,6 +399,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
abstract_type_generics,
|
||||
opaque_defn,
|
||||
def_id,
|
||||
lr,
|
||||
subst_arg,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -418,13 +421,28 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
/// related, we would generate a constraint `'r in ['a, 'b,
|
||||
/// 'static]` for each region `'r` that appears in the hidden type
|
||||
/// (i.e., it must be equal to `'a`, `'b`, or `'static`).
|
||||
///
|
||||
/// `conflict1` and `conflict2` are the two region bounds that we
|
||||
/// detected which were unrelated. They are used for diagnostics.
|
||||
fn generate_member_constraint(
|
||||
&self,
|
||||
concrete_ty: Ty<'tcx>,
|
||||
abstract_type_generics: &ty::Generics,
|
||||
opaque_defn: &OpaqueTypeDecl<'tcx>,
|
||||
opaque_type_def_id: DefId,
|
||||
conflict1: ty::Region<'tcx>,
|
||||
conflict2: ty::Region<'tcx>,
|
||||
) {
|
||||
// For now, enforce a feature gate outside of async functions.
|
||||
if self.member_constraint_feature_gate(
|
||||
opaque_defn,
|
||||
opaque_type_def_id,
|
||||
conflict1,
|
||||
conflict2,
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Create the set of choice regions: each region in the hidden
|
||||
// type can be equal to any of the region parameters of the
|
||||
// opaque type definition.
|
||||
|
@ -453,6 +471,60 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
});
|
||||
}
|
||||
|
||||
/// Member constraints are presently feature-gated except for
|
||||
/// async-await. We expect to lift this once we've had a bit more
|
||||
/// time.
|
||||
fn member_constraint_feature_gate(
|
||||
&self,
|
||||
opaque_defn: &OpaqueTypeDecl<'tcx>,
|
||||
opaque_type_def_id: DefId,
|
||||
conflict1: ty::Region<'tcx>,
|
||||
conflict2: ty::Region<'tcx>,
|
||||
) -> bool {
|
||||
// If we have `#![feature(member_constraints)]`, no problems.
|
||||
if self.tcx.features().member_constraints {
|
||||
return false;
|
||||
}
|
||||
|
||||
let span = self.tcx.def_span(opaque_type_def_id);
|
||||
|
||||
// Otherwise, we allow for async-await but not otherwise.
|
||||
let context_name = match opaque_defn.origin {
|
||||
hir::ExistTyOrigin::ExistentialType => "existential type",
|
||||
hir::ExistTyOrigin::ReturnImplTrait => "impl Trait",
|
||||
hir::ExistTyOrigin::AsyncFn => {
|
||||
// we permit
|
||||
return false;
|
||||
}
|
||||
};
|
||||
let msg = format!("ambiguous lifetime bound in `{}`", context_name);
|
||||
let mut err = self.tcx.sess.struct_span_err(span, &msg);
|
||||
|
||||
let conflict1_name = conflict1.to_string();
|
||||
let conflict2_name = conflict2.to_string();
|
||||
let label_owned;
|
||||
let label = match (&*conflict1_name, &*conflict2_name) {
|
||||
("'_", "'_") => "the elided lifetimes here do not outlive one another",
|
||||
_ => {
|
||||
label_owned = format!(
|
||||
"neither `{}` nor `{}` outlives the other",
|
||||
conflict1_name, conflict2_name,
|
||||
);
|
||||
&label_owned
|
||||
}
|
||||
};
|
||||
err.span_label(span, label);
|
||||
|
||||
if nightly_options::is_nightly_build() {
|
||||
help!(err,
|
||||
"add #![feature(member_constraints)] to the crate attributes \
|
||||
to enable");
|
||||
}
|
||||
|
||||
err.emit();
|
||||
true
|
||||
}
|
||||
|
||||
/// Given the fully resolved, instantiated type for an opaque
|
||||
/// type, i.e., the value of an inference variable like C1 or C2
|
||||
/// (*), computes the "definition type" for an abstract type
|
||||
|
|
|
@ -570,6 +570,9 @@ declare_features! (
|
|||
// Allows explicit discriminants on non-unit enum variants.
|
||||
(active, arbitrary_enum_discriminant, "1.37.0", Some(60553), None),
|
||||
|
||||
// Allows impl trait with multiple unrelated lifetimes
|
||||
(active, member_constraints, "1.37.0", Some(61977), None),
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// feature-group-end: actual feature gates
|
||||
// -------------------------------------------------------------------------
|
||||
|
|
|
@ -389,6 +389,7 @@ symbols! {
|
|||
match_beginning_vert,
|
||||
match_default_bindings,
|
||||
may_dangle,
|
||||
member_constraints,
|
||||
message,
|
||||
meta,
|
||||
min_const_fn,
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
trait Trait<'a, 'b> { }
|
||||
impl<T> Trait<'_, '_> for T {}
|
||||
|
||||
fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> {
|
||||
//~^ ERROR ambiguous lifetime bound
|
||||
(x, y)
|
||||
}
|
||||
|
||||
fn main() { }
|
|
@ -0,0 +1,10 @@
|
|||
error: ambiguous lifetime bound in `impl Trait`
|
||||
--> $DIR/feature-gate-member-constraints.rs:4:43
|
||||
|
|
||||
LL | fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> impl Trait<'a, 'b> {
|
||||
| ^^^^^^^^^^^^^^^^^^ neither `'a` nor `'b` outlives the other
|
||||
|
|
||||
= help: add #![feature(member_constraints)] to the crate attributes to enable
|
||||
|
||||
error: aborting due to previous error
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
// compile-flags:-Zborrowck=mir
|
||||
|
||||
#![feature(member_constraints)]
|
||||
#![feature(existential_type)]
|
||||
|
||||
#[derive(Clone)]
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
error: lifetime may not live long enough
|
||||
--> $DIR/error-handling.rs:12:56
|
||||
--> $DIR/error-handling.rs:13:56
|
||||
|
|
||||
LL | fn foo<'a, 'b, 'c>(x: &'static i32, mut y: &'a i32) -> E<'b, 'c> {
|
||||
| -- lifetime `'a` defined here ^^^^^^^^^ opaque type requires that `'a` must outlive `'static`
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
// revisions: migrate mir
|
||||
//[mir]compile-flags: -Z borrowck=mir
|
||||
|
||||
#![feature(member_constraints)]
|
||||
|
||||
trait Trait<'a, 'b> {}
|
||||
impl<T> Trait<'_, '_> for T {}
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
// revisions: migrate mir
|
||||
//[mir]compile-flags: -Z borrowck=mir
|
||||
|
||||
#![feature(member_constraints)]
|
||||
|
||||
trait Trait<'a, 'b> { }
|
||||
impl<T> Trait<'_, '_> for T { }
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// revisions: migrate mir
|
||||
//[mir]compile-flags: -Z borrowck=mir
|
||||
|
||||
#![feature(member_constraints)]
|
||||
#![feature(existential_type)]
|
||||
|
||||
trait Trait<'a, 'b> { }
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
// revisions: migrate mir
|
||||
//[mir]compile-flags: -Z borrowck=mir
|
||||
|
||||
#![feature(member_constraints)]
|
||||
|
||||
trait Trait<'a, 'b> { }
|
||||
impl<T> Trait<'_, '_> for T { }
|
||||
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
// revisions: migrate mir
|
||||
//[mir]compile-flags: -Z borrowck=mir
|
||||
|
||||
#![feature(member_constraints)]
|
||||
|
||||
trait Trait<'a, 'b> {}
|
||||
impl<T> Trait<'_, '_> for T {}
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// edition:2018
|
||||
|
||||
#![feature(member_constraints)]
|
||||
|
||||
trait Trait<'a, 'b> {}
|
||||
impl<T> Trait<'_, '_> for T {}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
|
||||
--> $DIR/ordinary-bounds-unrelated.rs:16:74
|
||||
--> $DIR/ordinary-bounds-unrelated.rs:18:74
|
||||
|
|
||||
LL | fn upper_bounds<'a, 'b, 'c, 'd, 'e>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'d, 'e>
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 21:1
|
||||
--> $DIR/ordinary-bounds-unrelated.rs:21:1
|
||||
note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 23:1
|
||||
--> $DIR/ordinary-bounds-unrelated.rs:23:1
|
||||
|
|
||||
LL | / {
|
||||
LL | | // Hidden type `Ordinary<'0>` with constraints:
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// edition:2018
|
||||
|
||||
#![feature(member_constraints)]
|
||||
|
||||
trait Trait<'a, 'b> {}
|
||||
impl<T> Trait<'_, '_> for T {}
|
||||
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
|
||||
--> $DIR/ordinary-bounds-unsuited.rs:18:62
|
||||
--> $DIR/ordinary-bounds-unsuited.rs:20:62
|
||||
|
|
||||
LL | fn upper_bounds<'a, 'b>(a: Ordinary<'a>, b: Ordinary<'b>) -> impl Trait<'a, 'b>
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 20:1
|
||||
--> $DIR/ordinary-bounds-unsuited.rs:20:1
|
||||
note: hidden type `Ordinary<'_>` captures the scope of call-site for function at 22:1
|
||||
--> $DIR/ordinary-bounds-unsuited.rs:22:1
|
||||
|
|
||||
LL | / {
|
||||
LL | | // We return a value:
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
// run-pass
|
||||
|
||||
#![feature(member_constraints)]
|
||||
|
||||
use std::fmt::Debug;
|
||||
|
||||
trait MultiRegionTrait<'a, 'b> {}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue