From 70e1f4fc6d951dfc0547ff5acb3d8780d16635e6 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Thu, 8 Feb 2018 16:50:14 -0800 Subject: [PATCH] Disallow projections from impl Trait types --- src/librustc_passes/ast_validation.rs | 68 ++++++++++++++++++++++- src/librustc_passes/diagnostics.rs | 1 + src/test/ui/impl_trait_projections.rs | 43 ++++++++++++++ src/test/ui/impl_trait_projections.stderr | 28 ++++++++++ 4 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/impl_trait_projections.rs create mode 100644 src/test/ui/impl_trait_projections.stderr diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index 826f27c2ddb..14ea5c0ce79 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -447,7 +447,7 @@ impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> { 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") + .span_label(t.span, "nested `impl Trait` here") .emit(); } @@ -482,6 +482,66 @@ impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> { } } +// Bans `impl Trait` in path projections like `::Item` or `Foo::Bar`. +struct ImplTraitProjectionVisitor<'a> { + session: &'a Session, + is_banned: bool, +} + +impl<'a> ImplTraitProjectionVisitor<'a> { + fn with_ban(&mut self, f: F) + where F: FnOnce(&mut ImplTraitProjectionVisitor<'a>) + { + let old_is_banned = self.is_banned; + self.is_banned = true; + f(self); + self.is_banned = old_is_banned; + } +} + +impl<'a> Visitor<'a> for ImplTraitProjectionVisitor<'a> { + fn visit_ty(&mut self, t: &'a Ty) { + match t.node { + TyKind::ImplTrait(_) => { + if self.is_banned { + struct_span_err!(self.session, t.span, E0667, + "`impl Trait` is not allowed in path parameters") + .emit(); + } + } + TyKind::Path(ref qself, ref path) => { + // We allow these: + // - `Option` + // - `option::Option` + // - `option::Option::Foo + // + // But not these: + // - `::Foo` + // - `option::Option::Foo`. + // + // To implement this, we disallow `impl Trait` from `qself` + // (for cases like `::Foo>`) + // but we allow `impl Trait` in `PathParameters` + // iff there are no more PathSegments. + if let Some(ref qself) = *qself { + // `impl Trait` in `qself` is always illegal + self.with_ban(|this| this.visit_ty(&qself.ty)); + } + + for (i, segment) in path.segments.iter().enumerate() { + // Allow `impl Trait` iff we're on the final path segment + if i == (path.segments.len() - 1) { + visit::walk_path_segment(self, path.span, segment); + } else { + self.with_ban(|this| + visit::walk_path_segment(this, path.span, segment)); + } + } + } + _ => visit::walk_ty(self, t), + } + } +} pub fn check_crate(session: &Session, krate: &Crate) { visit::walk_crate( @@ -490,5 +550,11 @@ pub fn check_crate(session: &Session, krate: &Crate) { outer_impl_trait: None, }, krate); + visit::walk_crate( + &mut ImplTraitProjectionVisitor { + session, + is_banned: false, + }, krate); + visit::walk_crate(&mut AstValidator { session: session }, krate) } diff --git a/src/librustc_passes/diagnostics.rs b/src/librustc_passes/diagnostics.rs index 6dfc52f842e..980808a6905 100644 --- a/src/librustc_passes/diagnostics.rs +++ b/src/librustc_passes/diagnostics.rs @@ -321,4 +321,5 @@ register_diagnostics! { E0568, // auto traits can not have super traits E0642, // patterns aren't allowed in methods without bodies E0666, // nested `impl Trait` is illegal + E0667, // `impl Trait` in projections } diff --git a/src/test/ui/impl_trait_projections.rs b/src/test/ui/impl_trait_projections.rs new file mode 100644 index 00000000000..e34b7799fb7 --- /dev/null +++ b/src/test/ui/impl_trait_projections.rs @@ -0,0 +1,43 @@ +// Copyright 2018 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +#![feature(conservative_impl_trait, universal_impl_trait)] + +use std::fmt::Debug; +use std::option; + +fn parametrized_type_is_allowed() -> Option { + Some(5i32) +} + +fn path_parametrized_type_is_allowed() -> option::Option { + Some(5i32) +} + +fn projection_is_disallowed(x: impl Iterator) -> ::Item { +//~^ ERROR `impl Trait` is not allowed in path parameters +//~^^ ERROR ambiguous associated type + x.next().unwrap() +} + +fn projection_with_named_trait_is_disallowed(x: impl Iterator) + -> ::Item +//~^ ERROR `impl Trait` is not allowed in path parameters +{ + x.next().unwrap() +} + +fn projection_with_named_trait_inside_path_is_disallowed() + -> <::std::ops::Range as Iterator>::Item +//~^ ERROR `impl Trait` is not allowed in path parameters +{ + (1i32..100).next().unwrap() +} + +fn main() {} diff --git a/src/test/ui/impl_trait_projections.stderr b/src/test/ui/impl_trait_projections.stderr new file mode 100644 index 00000000000..2e8bfc931f8 --- /dev/null +++ b/src/test/ui/impl_trait_projections.stderr @@ -0,0 +1,28 @@ +error[E0667]: `impl Trait` is not allowed in path parameters + --> $DIR/impl_trait_projections.rs:23:51 + | +23 | fn projection_is_disallowed(x: impl Iterator) -> ::Item { + | ^^^^^^^^^^^^^ + +error[E0667]: `impl Trait` is not allowed in path parameters + --> $DIR/impl_trait_projections.rs:30:9 + | +30 | -> ::Item + | ^^^^^^^^^^^^^ + +error[E0667]: `impl Trait` is not allowed in path parameters + --> $DIR/impl_trait_projections.rs:37:27 + | +37 | -> <::std::ops::Range as Iterator>::Item + | ^^^^^^^^^^ + +error[E0223]: ambiguous associated type + --> $DIR/impl_trait_projections.rs:23:50 + | +23 | fn projection_is_disallowed(x: impl Iterator) -> ::Item { + | ^^^^^^^^^^^^^^^^^^^^^ ambiguous associated type + | + = note: specify the type using the syntax `::Item` + +error: aborting due to 4 previous errors +