Skip to content

Commit

Permalink
Auto merge of rust-lang#45175 - petrochenkov:dyn, r=nikomatsakis
Browse files Browse the repository at this point in the history
Implement `dyn Trait` syntax (RFC 2113)

cc rust-lang#44662
r? @nikomatsakis
  • Loading branch information
bors committed Oct 14, 2017
2 parents 9c5e9a5 + 9d37320 commit 7920a7c
Show file tree
Hide file tree
Showing 18 changed files with 150 additions and 42 deletions.
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);
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

0 comments on commit 7920a7c

Please sign in to comment.