Disallow projections from impl Trait types

This commit is contained in:
Taylor Cramer 2018-02-08 16:50:14 -08:00
parent 75f72c0de1
commit 70e1f4fc6d
4 changed files with 139 additions and 1 deletions

View file

@ -447,7 +447,7 @@ impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> {
struct_span_err!(self.session, t.span, E0666, struct_span_err!(self.session, t.span, E0666,
"nested `impl Trait` is not allowed") "nested `impl Trait` is not allowed")
.span_label(outer_impl_trait, "outer `impl Trait`") .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(); .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) { pub fn check_crate(session: &Session, krate: &Crate) {
visit::walk_crate( visit::walk_crate(
@ -490,5 +550,11 @@ pub fn check_crate(session: &Session, krate: &Crate) {
outer_impl_trait: None, outer_impl_trait: None,
}, krate); }, krate);
visit::walk_crate(
&mut ImplTraitProjectionVisitor {
session,
is_banned: false,
}, krate);
visit::walk_crate(&mut AstValidator { session: session }, krate) visit::walk_crate(&mut AstValidator { session: session }, krate)
} }

View file

@ -321,4 +321,5 @@ register_diagnostics! {
E0568, // auto traits can not have super traits E0568, // auto traits can not have super traits
E0642, // patterns aren't allowed in methods without bodies E0642, // patterns aren't allowed in methods without bodies
E0666, // nested `impl Trait` is illegal E0666, // nested `impl Trait` is illegal
E0667, // `impl Trait` in projections
} }

View file

@ -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() {}

View file

@ -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