diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 6971033c899..826f27c2ddb 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -420,6 +420,75 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } } +// Bans nested `impl Trait`, e.g. `impl Into`. +// Nested `impl Trait` _is_ allowed in associated type position, +// e.g `impl Iterator` +struct NestedImplTraitVisitor<'a> { + session: &'a Session, + outer_impl_trait: Option, +} + +impl<'a> NestedImplTraitVisitor<'a> { + fn with_impl_trait(&mut self, outer_impl_trait: Option, f: F) + where F: FnOnce(&mut NestedImplTraitVisitor<'a>) + { + let old_outer_impl_trait = self.outer_impl_trait; + self.outer_impl_trait = outer_impl_trait; + f(self); + self.outer_impl_trait = old_outer_impl_trait; + } +} + + +impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> { + fn visit_ty(&mut self, t: &'a Ty) { + if let TyKind::ImplTrait(_) = t.node { + if let Some(outer_impl_trait) = self.outer_impl_trait { + struct_span_err!(self.session, t.span, E0666, + "nested `impl Trait` is not allowed") + .span_label(outer_impl_trait, "outer `impl Trait`") + .span_label(t.span, "devilishly nested `impl Trait` here") + .emit(); + + } + self.with_impl_trait(Some(t.span), |this| visit::walk_ty(this, t)); + } else { + visit::walk_ty(self, t); + } + } + fn visit_path_parameters(&mut self, _: Span, path_parameters: &'a PathParameters) { + match *path_parameters { + PathParameters::AngleBracketed(ref params) => { + for type_ in ¶ms.types { + self.visit_ty(type_); + } + for type_binding in ¶ms.bindings { + // Type bindings such as `Item=impl Debug` in `Iterator` + // are allowed to contain nested `impl Trait`. + self.with_impl_trait(None, |this| visit::walk_ty(this, &type_binding.ty)); + } + } + PathParameters::Parenthesized(ref params) => { + for type_ in ¶ms.inputs { + self.visit_ty(type_); + } + if let Some(ref type_) = params.output { + // `-> Foo` syntax is essentially an associated type binding, + // so it is also allowed to contain nested `impl Trait`. + self.with_impl_trait(None, |this| visit::walk_ty(this, type_)); + } + } + } + } +} + + pub fn check_crate(session: &Session, krate: &Crate) { + visit::walk_crate( + &mut NestedImplTraitVisitor { + session, + outer_impl_trait: None, + }, krate); + visit::walk_crate(&mut AstValidator { session: session }, krate) } diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index 743f7b7326e..6dfc52f842e 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -320,4 +320,5 @@ register_diagnostics! { E0567, // auto traits can not have generic parameters E0568, // auto traits can not have super traits E0642, // patterns aren't allowed in methods without bodies + E0666, // nested `impl Trait` is illegal } diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ea916d5168c..a3eba63706c 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -432,9 +432,6 @@ declare_features! ( // `foo.rs` as an alternative to `foo/mod.rs` (active, non_modrs_mods, "1.24.0", Some(44660)), - // Nested `impl Trait` - (active, nested_impl_trait, "1.24.0", Some(34511)), - // Termination trait in main (RFC 1937) (active, termination_trait, "1.24.0", Some(43301)), @@ -1352,73 +1349,8 @@ fn contains_novel_literal(item: &ast::MetaItem) -> bool { } } -// Bans nested `impl Trait`, e.g. `impl Into`. -// Nested `impl Trait` _is_ allowed in associated type position, -// e.g `impl Iterator` -struct NestedImplTraitVisitor<'a> { - context: &'a Context<'a>, - is_in_impl_trait: bool, -} - -impl<'a> NestedImplTraitVisitor<'a> { - fn with_impl_trait(&mut self, is_in_impl_trait: bool, f: F) - where F: FnOnce(&mut NestedImplTraitVisitor<'a>) - { - let old_is_in_impl_trait = self.is_in_impl_trait; - self.is_in_impl_trait = is_in_impl_trait; - f(self); - self.is_in_impl_trait = old_is_in_impl_trait; - } -} - - -impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> { - fn visit_ty(&mut self, t: &'a ast::Ty) { - if let ast::TyKind::ImplTrait(_) = t.node { - if self.is_in_impl_trait { - gate_feature_post!(&self, nested_impl_trait, t.span, - "nested `impl Trait` is experimental" - ); - } - self.with_impl_trait(true, |this| visit::walk_ty(this, t)); - } else { - visit::walk_ty(self, t); - } - } - fn visit_path_parameters(&mut self, _: Span, path_parameters: &'a ast::PathParameters) { - match *path_parameters { - ast::PathParameters::AngleBracketed(ref params) => { - for type_ in ¶ms.types { - self.visit_ty(type_); - } - for type_binding in ¶ms.bindings { - // Type bindings such as `Item=impl Debug` in `Iterator` - // are allowed to contain nested `impl Trait`. - self.with_impl_trait(false, |this| visit::walk_ty(this, &type_binding.ty)); - } - } - ast::PathParameters::Parenthesized(ref params) => { - for type_ in ¶ms.inputs { - self.visit_ty(type_); - } - if let Some(ref type_) = params.output { - // `-> Foo` syntax is essentially an associated type binding, - // so it is also allowed to contain nested `impl Trait`. - self.with_impl_trait(false, |this| visit::walk_ty(this, type_)); - } - } - } - } -} - impl<'a> PostExpansionVisitor<'a> { - fn whole_crate_feature_gates(&mut self, krate: &ast::Crate) { - visit::walk_crate( - &mut NestedImplTraitVisitor { - context: self.context, - is_in_impl_trait: false, - }, krate); - + fn whole_crate_feature_gates(&mut self, _krate: &ast::Crate) { for &(ident, span) in &*self.context.parse_sess.non_modrs_mods.borrow() { if !span.allows_unstable() { let cx = &self.context; diff --git a/src/test/compile-fail/impl-trait/where-allowed.rs b/src/test/compile-fail/impl-trait/where-allowed.rs index a9fe1e04664..52c5471681d 100644 --- a/src/test/compile-fail/impl-trait/where-allowed.rs +++ b/src/test/compile-fail/impl-trait/where-allowed.rs @@ -10,7 +10,7 @@ //! A simple test for testing many permutations of allowedness of //! impl Trait -#![feature(conservative_impl_trait, nested_impl_trait, universal_impl_trait, dyn_trait)] +#![feature(conservative_impl_trait, universal_impl_trait, dyn_trait)] use std::fmt::Debug; // Allowed @@ -60,6 +60,7 @@ fn in_dyn_Fn_return_in_return() -> &'static dyn Fn() -> impl Debug { panic!() } // Disallowed fn in_impl_Fn_parameter_in_parameters(_: &impl Fn(impl Debug)) { panic!() } //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +//~^^ ERROR nested `impl Trait` is not allowed // Disallowed fn in_impl_Fn_return_in_parameters(_: &impl Fn() -> impl Debug) { panic!() } @@ -68,6 +69,7 @@ fn in_impl_Fn_return_in_parameters(_: &impl Fn() -> impl Debug) { panic!() } // Disallowed fn in_impl_Fn_parameter_in_return() -> &'static impl Fn(impl Debug) { panic!() } //~^ ERROR `impl Trait` not allowed outside of function and inherent method return types +//~^^ ERROR nested `impl Trait` is not allowed // Disallowed fn in_impl_Fn_return_in_return() -> &'static impl Fn() -> impl Debug { panic!() } diff --git a/src/test/run-pass/impl-trait/lifetimes.rs b/src/test/run-pass/impl-trait/lifetimes.rs index 1f2d76f2894..4d40e707ddc 100644 --- a/src/test/run-pass/impl-trait/lifetimes.rs +++ b/src/test/run-pass/impl-trait/lifetimes.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(conservative_impl_trait, underscore_lifetimes, universal_impl_trait, nested_impl_trait)] +#![feature(conservative_impl_trait, underscore_lifetimes, universal_impl_trait)] #![allow(warnings)] use std::fmt::Debug; @@ -55,12 +55,11 @@ fn pass_through_elision_with_fn_ptr(x: &fn(&u32) -> &u32) -> impl Into<&fn(&u32) fn pass_through_elision_with_fn_path &u32>( x: &T -) -> impl Into<&impl Fn(&u32) -> &u32> { x } +) -> &impl Fn(&u32) -> &u32 { x } -fn foo(x: &impl Debug) -> impl Into<&impl Debug> { x } -fn foo_explicit_lifetime<'a>(x: &'a impl Debug) -> impl Into<&'a impl Debug> { x } -fn foo_no_outer_impl(x: &impl Debug) -> &impl Debug { x } -fn foo_explicit_arg(x: &T) -> impl Into<&impl Debug> { x } +fn foo(x: &impl Debug) -> &impl Debug { x } +fn foo_explicit_lifetime<'a>(x: &'a impl Debug) -> &'a impl Debug { x } +fn foo_explicit_arg(x: &T) -> &impl Debug { x } fn mixed_lifetimes<'a>() -> impl for<'b: 'a> Fn(&'b u32) { |_| () } fn mixed_as_static() -> impl Fn(&'static u32) { mixed_lifetimes() } diff --git a/src/test/ui/error-codes/E0657.rs b/src/test/ui/error-codes/E0657.rs index 4595e413081..31b3acd86ef 100644 --- a/src/test/ui/error-codes/E0657.rs +++ b/src/test/ui/error-codes/E0657.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. #![allow(warnings)] -#![feature(conservative_impl_trait, nested_impl_trait)] +#![feature(conservative_impl_trait)] trait Id {} trait Lt<'a> {} @@ -17,7 +17,7 @@ impl<'a> Lt<'a> for () {} impl Id for T {} fn free_fn_capture_hrtb_in_impl_trait() - -> impl for<'a> Id> + -> Box Id>> //~^ ERROR `impl Trait` can only capture lifetimes bound at the fn or impl level [E0657] { () @@ -26,7 +26,7 @@ fn free_fn_capture_hrtb_in_impl_trait() struct Foo; impl Foo { fn impl_fn_capture_hrtb_in_impl_trait() - -> impl for<'a> Id> + -> Box Id>> //~^ ERROR `impl Trait` can only capture lifetimes bound at the fn or impl level { () diff --git a/src/test/compile-fail/feature-gate-nested_impl_trait.rs b/src/test/ui/nested_impl_trait.rs similarity index 80% rename from src/test/compile-fail/feature-gate-nested_impl_trait.rs rename to src/test/ui/nested_impl_trait.rs index 7c35263d05d..f6302c0f3b3 100644 --- a/src/test/compile-fail/feature-gate-nested_impl_trait.rs +++ b/src/test/ui/nested_impl_trait.rs @@ -14,18 +14,19 @@ use std::fmt::Debug; fn fine(x: impl Into) -> impl Into { x } fn bad_in_ret_position(x: impl Into) -> impl Into { x } -//~^ ERROR nested `impl Trait` is experimental +//~^ ERROR nested `impl Trait` is not allowed fn bad_in_fn_syntax(x: fn() -> impl Into) {} -//~^ ERROR nested `impl Trait` is experimental +//~^ ERROR nested `impl Trait` is not allowed +//~^^ `impl Trait` not allowed fn bad_in_arg_position(_: impl Into) { } -//~^ ERROR nested `impl Trait` is experimental +//~^ ERROR nested `impl Trait` is not allowed struct X; impl X { fn bad(x: impl Into) -> impl Into { x } - //~^ ERROR nested `impl Trait` is experimental + //~^ ERROR nested `impl Trait` is not allowed } fn allowed_in_assoc_type() -> impl Iterator { @@ -33,6 +34,7 @@ fn allowed_in_assoc_type() -> impl Iterator { } fn allowed_in_ret_type() -> impl Fn() -> impl Into { +//~^ `impl Trait` not allowed || 5 } diff --git a/src/test/ui/nested_impl_trait.stderr b/src/test/ui/nested_impl_trait.stderr new file mode 100644 index 00000000000..6649f9ab99c --- /dev/null +++ b/src/test/ui/nested_impl_trait.stderr @@ -0,0 +1,50 @@ +error[E0666]: nested `impl Trait` is not allowed + --> $DIR/nested_impl_trait.rs:16:56 + | +16 | fn bad_in_ret_position(x: impl Into) -> impl Into { x } + | ----------^^^^^^^^^^- + | | | + | | devilishly nested `impl Trait` here + | outer `impl Trait` + +error[E0666]: nested `impl Trait` is not allowed + --> $DIR/nested_impl_trait.rs:19:42 + | +19 | fn bad_in_fn_syntax(x: fn() -> impl Into) {} + | ----------^^^^^^^^^^- + | | | + | | devilishly nested `impl Trait` here + | outer `impl Trait` + +error[E0666]: nested `impl Trait` is not allowed + --> $DIR/nested_impl_trait.rs:23:37 + | +23 | fn bad_in_arg_position(_: impl Into) { } + | ----------^^^^^^^^^^- + | | | + | | devilishly nested `impl Trait` here + | outer `impl Trait` + +error[E0666]: nested `impl Trait` is not allowed + --> $DIR/nested_impl_trait.rs:28:44 + | +28 | fn bad(x: impl Into) -> impl Into { x } + | ----------^^^^^^^^^^- + | | | + | | devilishly nested `impl Trait` here + | outer `impl Trait` + +error[E0562]: `impl Trait` not allowed outside of function and inherent method return types + --> $DIR/nested_impl_trait.rs:19:32 + | +19 | fn bad_in_fn_syntax(x: fn() -> impl Into) {} + | ^^^^^^^^^^^^^^^^^^^^^ + +error[E0562]: `impl Trait` not allowed outside of function and inherent method return types + --> $DIR/nested_impl_trait.rs:36:42 + | +36 | fn allowed_in_ret_type() -> impl Fn() -> impl Into { + | ^^^^^^^^^^^^^^ + +error: aborting due to 6 previous errors +