about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc_passes/ast_validation.rs68
-rw-r--r--src/librustc_passes/diagnostics.rs1
-rw-r--r--src/test/ui/impl_trait_projections.rs43
-rw-r--r--src/test/ui/impl_trait_projections.stderr28
4 files changed, 139 insertions, 1 deletions
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 `<impl Iterator>::Item` or `Foo::Bar<impl Trait>`.
+struct ImplTraitProjectionVisitor<'a> {
+    session: &'a Session,
+    is_banned: bool,
+}
+
+impl<'a> ImplTraitProjectionVisitor<'a> {
+    fn with_ban<F>(&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<impl Trait>`
+                //  - `option::Option<impl Trait>`
+                //  - `option::Option<T>::Foo<impl Trait>
+                //
+                // But not these:
+                //  - `<impl Trait>::Foo`
+                //  - `option::Option<impl Trait>::Foo`.
+                //
+                // To implement this, we disallow `impl Trait` from `qself`
+                // (for cases like `<impl Trait>::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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<impl Debug> {
+    Some(5i32)
+}
+
+fn path_parametrized_type_is_allowed() -> option::Option<impl Debug> {
+    Some(5i32)
+}
+
+fn projection_is_disallowed(x: impl Iterator) -> <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)
+    -> <impl Iterator as 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<impl Debug> 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) -> <impl Iterator>::Item {
+   |                                                   ^^^^^^^^^^^^^
+
+error[E0667]: `impl Trait` is not allowed in path parameters
+  --> $DIR/impl_trait_projections.rs:30:9
+   |
+30 |     -> <impl Iterator as Iterator>::Item
+   |         ^^^^^^^^^^^^^
+
+error[E0667]: `impl Trait` is not allowed in path parameters
+  --> $DIR/impl_trait_projections.rs:37:27
+   |
+37 |     -> <::std::ops::Range<impl Debug> as Iterator>::Item
+   |                           ^^^^^^^^^^
+
+error[E0223]: ambiguous associated type
+  --> $DIR/impl_trait_projections.rs:23:50
+   |
+23 | fn projection_is_disallowed(x: impl Iterator) -> <impl Iterator>::Item {
+   |                                                  ^^^^^^^^^^^^^^^^^^^^^ ambiguous associated type
+   |
+   = note: specify the type using the syntax `<impl std::iter::Iterator as Trait>::Item`
+
+error: aborting due to 4 previous errors
+