Skip to content

Commit 70e1f4f

Browse files
committed
Disallow projections from impl Trait types
1 parent 75f72c0 commit 70e1f4f

File tree

4 files changed

+139
-1
lines changed

4 files changed

+139
-1
lines changed

src/librustc_passes/ast_validation.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> {
447447
struct_span_err!(self.session, t.span, E0666,
448448
"nested `impl Trait` is not allowed")
449449
.span_label(outer_impl_trait, "outer `impl Trait`")
450-
.span_label(t.span, "devilishly nested `impl Trait` here")
450+
.span_label(t.span, "nested `impl Trait` here")
451451
.emit();
452452

453453
}
@@ -482,6 +482,66 @@ impl<'a> Visitor<'a> for NestedImplTraitVisitor<'a> {
482482
}
483483
}
484484

485+
// Bans `impl Trait` in path projections like `<impl Iterator>::Item` or `Foo::Bar<impl Trait>`.
486+
struct ImplTraitProjectionVisitor<'a> {
487+
session: &'a Session,
488+
is_banned: bool,
489+
}
490+
491+
impl<'a> ImplTraitProjectionVisitor<'a> {
492+
fn with_ban<F>(&mut self, f: F)
493+
where F: FnOnce(&mut ImplTraitProjectionVisitor<'a>)
494+
{
495+
let old_is_banned = self.is_banned;
496+
self.is_banned = true;
497+
f(self);
498+
self.is_banned = old_is_banned;
499+
}
500+
}
501+
502+
impl<'a> Visitor<'a> for ImplTraitProjectionVisitor<'a> {
503+
fn visit_ty(&mut self, t: &'a Ty) {
504+
match t.node {
505+
TyKind::ImplTrait(_) => {
506+
if self.is_banned {
507+
struct_span_err!(self.session, t.span, E0667,
508+
"`impl Trait` is not allowed in path parameters")
509+
.emit();
510+
}
511+
}
512+
TyKind::Path(ref qself, ref path) => {
513+
// We allow these:
514+
// - `Option<impl Trait>`
515+
// - `option::Option<impl Trait>`
516+
// - `option::Option<T>::Foo<impl Trait>
517+
//
518+
// But not these:
519+
// - `<impl Trait>::Foo`
520+
// - `option::Option<impl Trait>::Foo`.
521+
//
522+
// To implement this, we disallow `impl Trait` from `qself`
523+
// (for cases like `<impl Trait>::Foo>`)
524+
// but we allow `impl Trait` in `PathParameters`
525+
// iff there are no more PathSegments.
526+
if let Some(ref qself) = *qself {
527+
// `impl Trait` in `qself` is always illegal
528+
self.with_ban(|this| this.visit_ty(&qself.ty));
529+
}
530+
531+
for (i, segment) in path.segments.iter().enumerate() {
532+
// Allow `impl Trait` iff we're on the final path segment
533+
if i == (path.segments.len() - 1) {
534+
visit::walk_path_segment(self, path.span, segment);
535+
} else {
536+
self.with_ban(|this|
537+
visit::walk_path_segment(this, path.span, segment));
538+
}
539+
}
540+
}
541+
_ => visit::walk_ty(self, t),
542+
}
543+
}
544+
}
485545

486546
pub fn check_crate(session: &Session, krate: &Crate) {
487547
visit::walk_crate(
@@ -490,5 +550,11 @@ pub fn check_crate(session: &Session, krate: &Crate) {
490550
outer_impl_trait: None,
491551
}, krate);
492552

553+
visit::walk_crate(
554+
&mut ImplTraitProjectionVisitor {
555+
session,
556+
is_banned: false,
557+
}, krate);
558+
493559
visit::walk_crate(&mut AstValidator { session: session }, krate)
494560
}

src/librustc_passes/diagnostics.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -321,4 +321,5 @@ register_diagnostics! {
321321
E0568, // auto traits can not have super traits
322322
E0642, // patterns aren't allowed in methods without bodies
323323
E0666, // nested `impl Trait` is illegal
324+
E0667, // `impl Trait` in projections
324325
}

src/test/ui/impl_trait_projections.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
#![feature(conservative_impl_trait, universal_impl_trait)]
11+
12+
use std::fmt::Debug;
13+
use std::option;
14+
15+
fn parametrized_type_is_allowed() -> Option<impl Debug> {
16+
Some(5i32)
17+
}
18+
19+
fn path_parametrized_type_is_allowed() -> option::Option<impl Debug> {
20+
Some(5i32)
21+
}
22+
23+
fn projection_is_disallowed(x: impl Iterator) -> <impl Iterator>::Item {
24+
//~^ ERROR `impl Trait` is not allowed in path parameters
25+
//~^^ ERROR ambiguous associated type
26+
x.next().unwrap()
27+
}
28+
29+
fn projection_with_named_trait_is_disallowed(x: impl Iterator)
30+
-> <impl Iterator as Iterator>::Item
31+
//~^ ERROR `impl Trait` is not allowed in path parameters
32+
{
33+
x.next().unwrap()
34+
}
35+
36+
fn projection_with_named_trait_inside_path_is_disallowed()
37+
-> <::std::ops::Range<impl Debug> as Iterator>::Item
38+
//~^ ERROR `impl Trait` is not allowed in path parameters
39+
{
40+
(1i32..100).next().unwrap()
41+
}
42+
43+
fn main() {}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
error[E0667]: `impl Trait` is not allowed in path parameters
2+
--> $DIR/impl_trait_projections.rs:23:51
3+
|
4+
23 | fn projection_is_disallowed(x: impl Iterator) -> <impl Iterator>::Item {
5+
| ^^^^^^^^^^^^^
6+
7+
error[E0667]: `impl Trait` is not allowed in path parameters
8+
--> $DIR/impl_trait_projections.rs:30:9
9+
|
10+
30 | -> <impl Iterator as Iterator>::Item
11+
| ^^^^^^^^^^^^^
12+
13+
error[E0667]: `impl Trait` is not allowed in path parameters
14+
--> $DIR/impl_trait_projections.rs:37:27
15+
|
16+
37 | -> <::std::ops::Range<impl Debug> as Iterator>::Item
17+
| ^^^^^^^^^^
18+
19+
error[E0223]: ambiguous associated type
20+
--> $DIR/impl_trait_projections.rs:23:50
21+
|
22+
23 | fn projection_is_disallowed(x: impl Iterator) -> <impl Iterator>::Item {
23+
| ^^^^^^^^^^^^^^^^^^^^^ ambiguous associated type
24+
|
25+
= note: specify the type using the syntax `<impl std::iter::Iterator as Trait>::Item`
26+
27+
error: aborting due to 4 previous errors
28+

0 commit comments

Comments
 (0)