Skip to content

Implement dyn Trait syntax (RFC 2113) #45175

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 14, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/librustc/hir/lowering.rs
Original file line number Diff line number Diff line change
Expand Up @@ -705,7 +705,7 @@ impl<'a> LoweringContext<'a> {
let expr = self.lower_body(None, |this| this.lower_expr(expr));
hir::TyTypeof(expr)
}
TyKind::TraitObject(ref bounds) => {
TyKind::TraitObject(ref bounds, ..) => {
let mut lifetime_bound = None;
let bounds = bounds.iter().filter_map(|bound| {
match *bound {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_passes/ast_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
err.emit();
});
}
TyKind::TraitObject(ref bounds) => {
TyKind::TraitObject(ref bounds, ..) => {
let mut any_lifetime_bounds = false;
for bound in bounds {
if let RegionTyParamBound(ref lifetime) = *bound {
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_save_analysis/sig.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,7 +288,7 @@ impl Sig for ast::Ty {
})
}
}
ast::TyKind::TraitObject(ref bounds) => {
ast::TyKind::TraitObject(ref bounds, ..) => {
// FIXME recurse into bounds
let nested = pprust::bounds_to_string(bounds);
Ok(text_sig(nested))
Expand Down
9 changes: 8 additions & 1 deletion src/libsyntax/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1419,7 +1419,7 @@ pub enum TyKind {
Path(Option<QSelf>, Path),
/// A trait object type `Bound1 + Bound2 + Bound3`
/// where `Bound` is a trait or a lifetime.
TraitObject(TyParamBounds),
TraitObject(TyParamBounds, TraitObjectSyntax),
/// An `impl Bound1 + Bound2 + Bound3` type
/// where `Bound` is a trait or a lifetime.
ImplTrait(TyParamBounds),
Expand All @@ -1438,6 +1438,13 @@ pub enum TyKind {
Err,
}

/// Syntax used to declare a trait object.
#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
pub enum TraitObjectSyntax {
Dyn,
None,
}

/// Inline assembly dialect.
///
/// E.g. `"intel"` as in `asm!("mov eax, 2" : "={eax}"(result) : : : "intel")``
Expand Down
7 changes: 7 additions & 0 deletions src/libsyntax/feature_gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,9 @@ declare_features! (

// Default match binding modes (RFC 2005)
(active, match_default_bindings, "1.22.0", Some(42640)),

// Trait object syntax with `dyn` prefix
(active, dyn_trait, "1.22.0", Some(44662)),
);

declare_features! (
Expand Down Expand Up @@ -1417,6 +1420,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
gate_feature_post!(&self, never_type, ty.span,
"The `!` type is experimental");
},
ast::TyKind::TraitObject(_, ast::TraitObjectSyntax::Dyn) => {
gate_feature_post!(&self, dyn_trait, ty.span,
"`dyn Trait` syntax is unstable");
}
_ => {}
}
visit::walk_ty(self, ty)
Expand Down
4 changes: 2 additions & 2 deletions src/libsyntax/fold.rs
Original file line number Diff line number Diff line change
Expand Up @@ -400,8 +400,8 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> {
TyKind::Typeof(expr) => {
TyKind::Typeof(fld.fold_expr(expr))
}
TyKind::TraitObject(bounds) => {
TyKind::TraitObject(bounds.move_map(|b| fld.fold_ty_param_bound(b)))
TyKind::TraitObject(bounds, syntax) => {
TyKind::TraitObject(bounds.move_map(|b| fld.fold_ty_param_bound(b)), syntax)
}
TyKind::ImplTrait(bounds) => {
TyKind::ImplTrait(bounds.move_map(|b| fld.fold_ty_param_bound(b)))
Expand Down
66 changes: 40 additions & 26 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ use ast::{Stmt, StmtKind};
use ast::{VariantData, StructField};
use ast::StrStyle;
use ast::SelfKind;
use ast::{TraitItem, TraitRef};
use ast::{TraitItem, TraitRef, TraitObjectSyntax};
use ast::{Ty, TyKind, TypeBinding, TyParam, TyParamBounds};
use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
use ast::{Visibility, WhereClause};
Expand Down Expand Up @@ -364,6 +364,13 @@ fn is_ident_or_underscore(t: &token::Token) -> bool {
t.is_ident() || *t == token::Underscore
}

// Returns true if `IDENT t` can start a type - `IDENT::a::b`, `IDENT<u8, u8>`,
// `IDENT<<u8 as Trait>::AssocTy>`, `IDENT(u8, u8) -> u8`.
fn can_continue_type_after_ident(t: &token::Token) -> bool {
t == &token::ModSep || t == &token::Lt ||
t == &token::BinOp(token::Shl) || t == &token::OpenDelim(token::Paren)
}

/// Information about the path to a module.
pub struct ModulePath {
pub name: String,
Expand Down Expand Up @@ -1428,7 +1435,7 @@ impl<'a> Parser<'a> {
TyKind::Path(None, ref path) if maybe_bounds => {
self.parse_remaining_bounds(Vec::new(), path.clone(), lo, true)?
}
TyKind::TraitObject(ref bounds)
TyKind::TraitObject(ref bounds, TraitObjectSyntax::None)
if maybe_bounds && bounds.len() == 1 && !trailing_plus => {
let path = match bounds[0] {
TraitTyParamBound(ref pt, ..) => pt.trait_ref.path.clone(),
Expand Down Expand Up @@ -1472,27 +1479,6 @@ impl<'a> Parser<'a> {
} else if self.eat(&token::Underscore) {
// A type to be inferred `_`
TyKind::Infer
} else if self.eat_lt() {
// Qualified path
let (qself, path) = self.parse_qpath(PathStyle::Type)?;
TyKind::Path(Some(qself), path)
} else if self.token.is_path_start() {
// Simple path
let path = self.parse_path(PathStyle::Type)?;
if self.eat(&token::Not) {
// Macro invocation in type position
let (_, tts) = self.expect_delimited_token_tree()?;
TyKind::Mac(respan(lo.to(self.span), Mac_ { path: path, tts: tts }))
} else {
// Just a type path or bound list (trait object type) starting with a trait.
// `Type`
// `Trait1 + Trait2 + 'a`
if allow_plus && self.check(&token::BinOp(token::Plus)) {
self.parse_remaining_bounds(Vec::new(), path, lo, true)?
} else {
TyKind::Path(None, path)
}
}
} else if self.token_is_bare_fn_keyword() {
// Function pointer type
self.parse_ty_bare_fn(Vec::new())?
Expand All @@ -1512,10 +1498,37 @@ impl<'a> Parser<'a> {
} else if self.eat_keyword(keywords::Impl) {
// FIXME: figure out priority of `+` in `impl Trait1 + Trait2` (#34511).
TyKind::ImplTrait(self.parse_ty_param_bounds()?)
} else if self.check_keyword(keywords::Dyn) &&
self.look_ahead(1, |t| t.can_begin_bound() && !can_continue_type_after_ident(t)) {
// FIXME: figure out priority of `+` in `dyn Trait1 + Trait2` (#34511).
self.bump(); // `dyn`
TyKind::TraitObject(self.parse_ty_param_bounds()?, TraitObjectSyntax::Dyn)
} else if self.check(&token::Question) ||
self.check_lifetime() && self.look_ahead(1, |t| t == &token::BinOp(token::Plus)){
self.check_lifetime() && self.look_ahead(1, |t| t == &token::BinOp(token::Plus)) {
// Bound list (trait object type)
TyKind::TraitObject(self.parse_ty_param_bounds_common(allow_plus)?)
TyKind::TraitObject(self.parse_ty_param_bounds_common(allow_plus)?,
TraitObjectSyntax::None)
} else if self.eat_lt() {
// Qualified path
let (qself, path) = self.parse_qpath(PathStyle::Type)?;
TyKind::Path(Some(qself), path)
} else if self.token.is_path_start() {
// Simple path
let path = self.parse_path(PathStyle::Type)?;
if self.eat(&token::Not) {
// Macro invocation in type position
let (_, tts) = self.expect_delimited_token_tree()?;
TyKind::Mac(respan(lo.to(self.span), Mac_ { path: path, tts: tts }))
} else {
// Just a type path or bound list (trait object type) starting with a trait.
// `Type`
// `Trait1 + Trait2 + 'a`
if allow_plus && self.check(&token::BinOp(token::Plus)) {
self.parse_remaining_bounds(Vec::new(), path, lo, true)?
} else {
TyKind::Path(None, path)
}
}
} else {
let msg = format!("expected type, found {}", self.this_token_descr());
return Err(self.fatal(&msg));
Expand All @@ -1538,7 +1551,7 @@ impl<'a> Parser<'a> {
self.bump(); // `+`
bounds.append(&mut self.parse_ty_param_bounds()?);
}
Ok(TyKind::TraitObject(bounds))
Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None))
}

fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> {
Expand Down Expand Up @@ -4256,6 +4269,7 @@ impl<'a> Parser<'a> {
fn parse_ty_param_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, TyParamBounds> {
let mut bounds = Vec::new();
loop {
// This needs to be syncronized with `Token::can_begin_bound`.
let is_bound_start = self.check_path() || self.check_lifetime() ||
self.check(&token::Question) ||
self.check_keyword(keywords::For) ||
Expand Down
6 changes: 6 additions & 0 deletions src/libsyntax/parse/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,12 @@ impl Token {
}
}

/// Returns `true` if the token can appear at the start of a generic bound.
pub fn can_begin_bound(&self) -> bool {
self.is_path_start() || self.is_lifetime() || self.is_keyword(keywords::For) ||
self == &Question || self == &OpenDelim(Paren)
}

/// Returns `true` if the token is any literal
pub fn is_lit(&self) -> bool {
match *self {
Expand Down
5 changes: 3 additions & 2 deletions src/libsyntax/print/pprust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1049,8 +1049,9 @@ impl<'a> State<'a> {
ast::TyKind::Path(Some(ref qself), ref path) => {
self.print_qpath(path, qself, false)?
}
ast::TyKind::TraitObject(ref bounds) => {
self.print_bounds("", &bounds[..])?;
ast::TyKind::TraitObject(ref bounds, syntax) => {
let prefix = if syntax == ast::TraitObjectSyntax::Dyn { "dyn " } else { "" };
self.print_bounds(prefix, &bounds[..])?;
}
ast::TyKind::ImplTrait(ref bounds) => {
self.print_bounds("impl ", &bounds[..])?;
Expand Down
2 changes: 1 addition & 1 deletion src/libsyntax/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
visitor.visit_ty(ty);
visitor.visit_expr(expression)
}
TyKind::TraitObject(ref bounds) |
TyKind::TraitObject(ref bounds, ..) |
TyKind::ImplTrait(ref bounds) => {
walk_list!(visitor, visit_ty_param_bound, bounds);
}
Expand Down
9 changes: 5 additions & 4 deletions src/libsyntax_pos/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,10 +309,11 @@ declare_keywords! {
(54, Yield, "yield")

// Weak keywords, have special meaning only in specific contexts.
(55, Default, "default")
(56, StaticLifetime, "'static")
(57, Union, "union")
(58, Catch, "catch")
(55, Catch, "catch")
(56, Default, "default")
(57, Dyn, "dyn")
(58, StaticLifetime, "'static")
(59, Union, "union")
}

// If an interner exists in TLS, return it. Otherwise, prepare a fresh one.
Expand Down
29 changes: 29 additions & 0 deletions src/test/compile-fail/dyn-trait-compatibility.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2017 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.

type A0 = dyn;
//~^ ERROR cannot find type `dyn` in this scope
type A1 = dyn::dyn;
//~^ ERROR Use of undeclared type or module `dyn`
type A2 = dyn<dyn, dyn>;
//~^ ERROR cannot find type `dyn` in this scope
//~| ERROR cannot find type `dyn` in this scope
//~| ERROR cannot find type `dyn` in this scope
type A3 = dyn<<dyn as dyn>::dyn>;
//~^ ERROR cannot find type `dyn` in this scope
//~| ERROR cannot find type `dyn` in this scope
//~| ERROR Use of undeclared type or module `dyn`
type A4 = dyn(dyn, dyn) -> dyn;
//~^ ERROR cannot find type `dyn` in this scope
//~| ERROR cannot find type `dyn` in this scope
//~| ERROR cannot find type `dyn` in this scope
//~| ERROR cannot find type `dyn` in this scope

fn main() {}
14 changes: 14 additions & 0 deletions src/test/compile-fail/feature-gate-dyn-trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Copyright 2016 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.

trait Trait {}
type A = Box<dyn Trait>; //~ ERROR `dyn Trait` syntax is unstable

fn main() {}
3 changes: 3 additions & 0 deletions src/test/compile-fail/trait-bounds-not-on-struct.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,12 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![feature(dyn_trait)]

struct Foo;

fn foo(_x: Box<Foo + Send>) { } //~ ERROR expected trait, found struct `Foo`

type A<T> = Box<dyn Vec<T>>; //~ ERROR expected trait, found struct `Vec`

fn main() { }
2 changes: 2 additions & 0 deletions src/test/parse-fail/trait-object-bad-parens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ fn main() {
//~^ ERROR expected a path on the left-hand side of `+`, not `( Copy + Copy)`
let _: Box<(Copy +) + Copy>;
//~^ ERROR expected a path on the left-hand side of `+`, not `( Copy)`
let _: Box<(dyn Copy) + Copy>;
//~^ ERROR expected a path on the left-hand side of `+`, not `(dyn Copy)`
}
24 changes: 24 additions & 0 deletions src/test/run-pass/dyn-trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright 2017 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(dyn_trait)]

use std::fmt::Display;

static BYTE: u8 = 33;

fn main() {
let x: &(dyn 'static + Display) = &BYTE;
let y: Box<dyn Display + 'static> = Box::new(BYTE);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we get a test like dyn Vec<T> -- i.e., something not a trait?

let xstr = format!("{}", x);
let ystr = format!("{}", y);
assert_eq!(xstr, "33");
assert_eq!(ystr, "33");
}
4 changes: 2 additions & 2 deletions src/test/ui/issue-44406.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ error: expected type, found keyword `true`
18 | foo!(true);
| ^^^^ expecting a type here because of type ascription

error: expected one of `!`, `&&`, `&`, `(`, `*`, `.`, `;`, `<`, `?`, `[`, `_`, `extern`, `fn`, `for`, `impl`, `unsafe`, `}`, an operator, or lifetime, found `true`
error: expected one of `!`, `&&`, `&`, `(`, `*`, `.`, `;`, `<`, `?`, `[`, `_`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, `}`, an operator, or lifetime, found `true`
--> $DIR/issue-44406.rs:18:10
|
13 | bar(baz: $rest)
| - expected one of 19 possible tokens here
| - expected one of 20 possible tokens here
...
18 | foo!(true);
| ^^^^ unexpected token
Expand Down
2 changes: 1 addition & 1 deletion src/tools/rustfmt