From 74a6efbf00e2fff08afe623a0a01155b4bebd0a3 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Thu, 20 Jun 2019 10:44:20 -0400 Subject: feature-gate member constraints outside of async-await Minimizes risk. --- .../src/language-features/member-constraints.md | 29 +++++++++ src/librustc/infer/opaque_types/mod.rs | 72 ++++++++++++++++++++++ src/libsyntax/feature_gate.rs | 3 + src/libsyntax_pos/symbol.rs | 1 + .../feature-gate-member-constraints.rs | 9 +++ .../feature-gate-member-constraints.stderr | 10 +++ .../multiple-lifetimes/error-handling.rs | 1 + .../multiple-lifetimes/error-handling.stderr | 2 +- .../multiple-lifetimes/inverse-bounds.rs | 2 + .../ordinary-bounds-pick-original-elided.rs | 2 + .../ordinary-bounds-pick-original-existential.rs | 1 + .../ordinary-bounds-pick-original.rs | 2 + .../ordinary-bounds-pick-other.rs | 2 + .../ordinary-bounds-unrelated.rs | 2 + .../ordinary-bounds-unrelated.stderr | 6 +- .../multiple-lifetimes/ordinary-bounds-unsuited.rs | 2 + .../ordinary-bounds-unsuited.stderr | 6 +- .../ui/impl-trait/needs_least_region_or_bound.rs | 2 + 18 files changed, 147 insertions(+), 7 deletions(-) create mode 100644 src/doc/unstable-book/src/language-features/member-constraints.md create mode 100644 src/test/ui/feature-gates/feature-gate-member-constraints.rs create mode 100644 src/test/ui/feature-gates/feature-gate-member-constraints.stderr (limited to 'src') diff --git a/src/doc/unstable-book/src/language-features/member-constraints.md b/src/doc/unstable-book/src/language-features/member-constraints.md new file mode 100644 index 00000000000..71f2a10d092 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/member-constraints.md @@ -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 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. diff --git a/src/librustc/infer/opaque_types/mod.rs b/src/librustc/infer/opaque_types/mod.rs index 2e35aefa88a..9c2cc5815de 100644 --- a/src/librustc/infer/opaque_types/mod.rs +++ b/src/librustc/infer/opaque_types/mod.rs @@ -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 diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index f97e9d43854..1223c069d65 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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 // ------------------------------------------------------------------------- diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 266bd2a04a2..aee988d5148 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -389,6 +389,7 @@ symbols! { match_beginning_vert, match_default_bindings, may_dangle, + member_constraints, message, meta, min_const_fn, diff --git a/src/test/ui/feature-gates/feature-gate-member-constraints.rs b/src/test/ui/feature-gates/feature-gate-member-constraints.rs new file mode 100644 index 00000000000..293a93352e6 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-member-constraints.rs @@ -0,0 +1,9 @@ +trait Trait<'a, 'b> { } +impl 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() { } diff --git a/src/test/ui/feature-gates/feature-gate-member-constraints.stderr b/src/test/ui/feature-gates/feature-gate-member-constraints.stderr new file mode 100644 index 00000000000..3745d5e1c59 --- /dev/null +++ b/src/test/ui/feature-gates/feature-gate-member-constraints.stderr @@ -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 + diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs index 06122184634..61e858ee02d 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.rs @@ -1,5 +1,6 @@ // compile-flags:-Zborrowck=mir +#![feature(member_constraints)] #![feature(existential_type)] #[derive(Clone)] diff --git a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr index 8355399506d..b59dfbe9f2a 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/error-handling.stderr @@ -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` diff --git a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs index 53d2bc19089..2da3886bb55 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/inverse-bounds.rs @@ -3,6 +3,8 @@ // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir +#![feature(member_constraints)] + trait Trait<'a, 'b> {} impl Trait<'_, '_> for T {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs index 23981d92562..e2cb574fac0 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-elided.rs @@ -3,6 +3,8 @@ // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir +#![feature(member_constraints)] + trait Trait<'a, 'b> { } impl Trait<'_, '_> for T { } diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs index be249eeb9ea..a1ec89e8fbd 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original-existential.rs @@ -3,6 +3,7 @@ // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir +#![feature(member_constraints)] #![feature(existential_type)] trait Trait<'a, 'b> { } diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs index 0d04980d37a..21979b00179 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-original.rs @@ -3,6 +3,8 @@ // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir +#![feature(member_constraints)] + trait Trait<'a, 'b> { } impl Trait<'_, '_> for T { } diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs index 6555bacba67..0dfc118d78c 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-pick-other.rs @@ -3,6 +3,8 @@ // revisions: migrate mir //[mir]compile-flags: -Z borrowck=mir +#![feature(member_constraints)] + trait Trait<'a, 'b> {} impl Trait<'_, '_> for T {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs index 3a97624647e..db1641b0140 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.rs @@ -1,5 +1,7 @@ // edition:2018 +#![feature(member_constraints)] + trait Trait<'a, 'b> {} impl Trait<'_, '_> for T {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr index c807048ce54..cd2d46ac182 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unrelated.stderr @@ -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: diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs index 85c478d8d31..8f85b444d08 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.rs @@ -1,5 +1,7 @@ // edition:2018 +#![feature(member_constraints)] + trait Trait<'a, 'b> {} impl Trait<'_, '_> for T {} diff --git a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr index 6687c40f957..59ce93fa78b 100644 --- a/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr +++ b/src/test/ui/impl-trait/multiple-lifetimes/ordinary-bounds-unsuited.stderr @@ -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: diff --git a/src/test/ui/impl-trait/needs_least_region_or_bound.rs b/src/test/ui/impl-trait/needs_least_region_or_bound.rs index 8475122cfae..52475f65a83 100644 --- a/src/test/ui/impl-trait/needs_least_region_or_bound.rs +++ b/src/test/ui/impl-trait/needs_least_region_or_bound.rs @@ -1,5 +1,7 @@ // run-pass +#![feature(member_constraints)] + use std::fmt::Debug; trait MultiRegionTrait<'a, 'b> {} -- cgit 1.4.1-3-g733a5