Implement dyn Trait syntax

This commit is contained in:
Vadim Petrochenkov 2017-10-10 17:33:19 +03:00
parent 3037965b5b
commit e6115af4bd
17 changed files with 149 additions and 41 deletions

View File

@ -705,7 +705,7 @@ impl<'a> LoweringContext<'a> {
let expr = self.lower_body(None, |this| this.lower_expr(expr)); let expr = self.lower_body(None, |this| this.lower_expr(expr));
hir::TyTypeof(expr) hir::TyTypeof(expr)
} }
TyKind::TraitObject(ref bounds) => { TyKind::TraitObject(ref bounds, ..) => {
let mut lifetime_bound = None; let mut lifetime_bound = None;
let bounds = bounds.iter().filter_map(|bound| { let bounds = bounds.iter().filter_map(|bound| {
match *bound { match *bound {

View File

@ -152,7 +152,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
err.emit(); err.emit();
}); });
} }
TyKind::TraitObject(ref bounds) => { TyKind::TraitObject(ref bounds, ..) => {
let mut any_lifetime_bounds = false; let mut any_lifetime_bounds = false;
for bound in bounds { for bound in bounds {
if let RegionTyParamBound(ref lifetime) = *bound { if let RegionTyParamBound(ref lifetime) = *bound {

View File

@ -288,7 +288,7 @@ impl Sig for ast::Ty {
}) })
} }
} }
ast::TyKind::TraitObject(ref bounds) => { ast::TyKind::TraitObject(ref bounds, ..) => {
// FIXME recurse into bounds // FIXME recurse into bounds
let nested = pprust::bounds_to_string(bounds); let nested = pprust::bounds_to_string(bounds);
Ok(text_sig(nested)) Ok(text_sig(nested))

View File

@ -1419,7 +1419,7 @@ pub enum TyKind {
Path(Option<QSelf>, Path), Path(Option<QSelf>, Path),
/// A trait object type `Bound1 + Bound2 + Bound3` /// A trait object type `Bound1 + Bound2 + Bound3`
/// where `Bound` is a trait or a lifetime. /// where `Bound` is a trait or a lifetime.
TraitObject(TyParamBounds), TraitObject(TyParamBounds, TraitObjectSyntax),
/// An `impl Bound1 + Bound2 + Bound3` type /// An `impl Bound1 + Bound2 + Bound3` type
/// where `Bound` is a trait or a lifetime. /// where `Bound` is a trait or a lifetime.
ImplTrait(TyParamBounds), ImplTrait(TyParamBounds),
@ -1438,6 +1438,13 @@ pub enum TyKind {
Err, 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. /// Inline assembly dialect.
/// ///
/// E.g. `"intel"` as in `asm!("mov eax, 2" : "={eax}"(result) : : : "intel")`` /// E.g. `"intel"` as in `asm!("mov eax, 2" : "={eax}"(result) : : : "intel")``

View File

@ -398,6 +398,9 @@ declare_features! (
// Default match binding modes (RFC 2005) // Default match binding modes (RFC 2005)
(active, match_default_bindings, "1.22.0", Some(42640)), (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! ( declare_features! (
@ -1417,6 +1420,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
gate_feature_post!(&self, never_type, ty.span, gate_feature_post!(&self, never_type, ty.span,
"The `!` type is experimental"); "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) visit::walk_ty(self, ty)

View File

@ -400,8 +400,8 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> {
TyKind::Typeof(expr) => { TyKind::Typeof(expr) => {
TyKind::Typeof(fld.fold_expr(expr)) TyKind::Typeof(fld.fold_expr(expr))
} }
TyKind::TraitObject(bounds) => { TyKind::TraitObject(bounds, syntax) => {
TyKind::TraitObject(bounds.move_map(|b| fld.fold_ty_param_bound(b))) TyKind::TraitObject(bounds.move_map(|b| fld.fold_ty_param_bound(b)), syntax)
} }
TyKind::ImplTrait(bounds) => { TyKind::ImplTrait(bounds) => {
TyKind::ImplTrait(bounds.move_map(|b| fld.fold_ty_param_bound(b))) TyKind::ImplTrait(bounds.move_map(|b| fld.fold_ty_param_bound(b)))

View File

@ -33,7 +33,7 @@ use ast::{Stmt, StmtKind};
use ast::{VariantData, StructField}; use ast::{VariantData, StructField};
use ast::StrStyle; use ast::StrStyle;
use ast::SelfKind; use ast::SelfKind;
use ast::{TraitItem, TraitRef}; use ast::{TraitItem, TraitRef, TraitObjectSyntax};
use ast::{Ty, TyKind, TypeBinding, TyParam, TyParamBounds}; use ast::{Ty, TyKind, TypeBinding, TyParam, TyParamBounds};
use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple}; use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
use ast::{Visibility, WhereClause}; use ast::{Visibility, WhereClause};
@ -364,6 +364,13 @@ fn is_ident_or_underscore(t: &token::Token) -> bool {
t.is_ident() || *t == token::Underscore 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. /// Information about the path to a module.
pub struct ModulePath { pub struct ModulePath {
pub name: String, pub name: String,
@ -1428,7 +1435,7 @@ impl<'a> Parser<'a> {
TyKind::Path(None, ref path) if maybe_bounds => { TyKind::Path(None, ref path) if maybe_bounds => {
self.parse_remaining_bounds(Vec::new(), path.clone(), lo, true)? 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 => { if maybe_bounds && bounds.len() == 1 && !trailing_plus => {
let path = match bounds[0] { let path = match bounds[0] {
TraitTyParamBound(ref pt, ..) => pt.trait_ref.path.clone(), TraitTyParamBound(ref pt, ..) => pt.trait_ref.path.clone(),
@ -1472,6 +1479,35 @@ impl<'a> Parser<'a> {
} else if self.eat(&token::Underscore) { } else if self.eat(&token::Underscore) {
// A type to be inferred `_` // A type to be inferred `_`
TyKind::Infer TyKind::Infer
} else if self.token_is_bare_fn_keyword() {
// Function pointer type
self.parse_ty_bare_fn(Vec::new())?
} else if self.check_keyword(keywords::For) {
// Function pointer type or bound list (trait object type) starting with a poly-trait.
// `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
// `for<'lt> Trait1<'lt> + Trait2 + 'a`
let lo = self.span;
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
if self.token_is_bare_fn_keyword() {
self.parse_ty_bare_fn(lifetime_defs)?
} else {
let path = self.parse_path(PathStyle::Type)?;
let parse_plus = allow_plus && self.check(&token::BinOp(token::Plus));
self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)?
}
} 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)) {
// Bound list (trait object type)
TyKind::TraitObject(self.parse_ty_param_bounds_common(allow_plus)?,
TraitObjectSyntax::None)
} else if self.eat_lt() { } else if self.eat_lt() {
// Qualified path // Qualified path
let (qself, path) = self.parse_qpath(PathStyle::Type)?; let (qself, path) = self.parse_qpath(PathStyle::Type)?;
@ -1493,29 +1529,6 @@ impl<'a> Parser<'a> {
TyKind::Path(None, path) TyKind::Path(None, path)
} }
} }
} else if self.token_is_bare_fn_keyword() {
// Function pointer type
self.parse_ty_bare_fn(Vec::new())?
} else if self.check_keyword(keywords::For) {
// Function pointer type or bound list (trait object type) starting with a poly-trait.
// `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
// `for<'lt> Trait1<'lt> + Trait2 + 'a`
let lo = self.span;
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
if self.token_is_bare_fn_keyword() {
self.parse_ty_bare_fn(lifetime_defs)?
} else {
let path = self.parse_path(PathStyle::Type)?;
let parse_plus = allow_plus && self.check(&token::BinOp(token::Plus));
self.parse_remaining_bounds(lifetime_defs, path, lo, parse_plus)?
}
} 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(&token::Question) ||
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)?)
} else { } else {
let msg = format!("expected type, found {}", self.this_token_descr()); let msg = format!("expected type, found {}", self.this_token_descr());
return Err(self.fatal(&msg)); return Err(self.fatal(&msg));
@ -1538,7 +1551,7 @@ impl<'a> Parser<'a> {
self.bump(); // `+` self.bump(); // `+`
bounds.append(&mut self.parse_ty_param_bounds()?); 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, ()> { fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> {
@ -4256,6 +4269,7 @@ impl<'a> Parser<'a> {
fn parse_ty_param_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, TyParamBounds> { fn parse_ty_param_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, TyParamBounds> {
let mut bounds = Vec::new(); let mut bounds = Vec::new();
loop { loop {
// This needs to be syncronized with `Token::can_begin_bound`.
let is_bound_start = self.check_path() || self.check_lifetime() || let is_bound_start = self.check_path() || self.check_lifetime() ||
self.check(&token::Question) || self.check(&token::Question) ||
self.check_keyword(keywords::For) || self.check_keyword(keywords::For) ||

View File

@ -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 /// Returns `true` if the token is any literal
pub fn is_lit(&self) -> bool { pub fn is_lit(&self) -> bool {
match *self { match *self {

View File

@ -1049,8 +1049,9 @@ impl<'a> State<'a> {
ast::TyKind::Path(Some(ref qself), ref path) => { ast::TyKind::Path(Some(ref qself), ref path) => {
self.print_qpath(path, qself, false)? self.print_qpath(path, qself, false)?
} }
ast::TyKind::TraitObject(ref bounds) => { ast::TyKind::TraitObject(ref bounds, syntax) => {
self.print_bounds("", &bounds[..])?; let prefix = if syntax == ast::TraitObjectSyntax::Dyn { "dyn " } else { "" };
self.print_bounds(prefix, &bounds[..])?;
} }
ast::TyKind::ImplTrait(ref bounds) => { ast::TyKind::ImplTrait(ref bounds) => {
self.print_bounds("impl ", &bounds[..])?; self.print_bounds("impl ", &bounds[..])?;

View File

@ -348,7 +348,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
visitor.visit_ty(ty); visitor.visit_ty(ty);
visitor.visit_expr(expression) visitor.visit_expr(expression)
} }
TyKind::TraitObject(ref bounds) | TyKind::TraitObject(ref bounds, ..) |
TyKind::ImplTrait(ref bounds) => { TyKind::ImplTrait(ref bounds) => {
walk_list!(visitor, visit_ty_param_bound, bounds); walk_list!(visitor, visit_ty_param_bound, bounds);
} }

View File

@ -309,10 +309,11 @@ declare_keywords! {
(54, Yield, "yield") (54, Yield, "yield")
// Weak keywords, have special meaning only in specific contexts. // Weak keywords, have special meaning only in specific contexts.
(55, Default, "default") (55, Catch, "catch")
(56, StaticLifetime, "'static") (56, Default, "default")
(57, Union, "union") (57, Dyn, "dyn")
(58, Catch, "catch") (58, StaticLifetime, "'static")
(59, Union, "union")
} }
// If an interner exists in TLS, return it. Otherwise, prepare a fresh one. // If an interner exists in TLS, return it. Otherwise, prepare a fresh one.

View File

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

View File

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

View File

@ -8,9 +8,12 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![feature(dyn_trait)]
struct Foo; struct Foo;
fn foo(_x: Box<Foo + Send>) { } //~ ERROR expected trait, found 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() { } fn main() { }

View File

@ -17,4 +17,6 @@ fn main() {
//~^ ERROR expected a path on the left-hand side of `+`, not `( Copy + Copy)` //~^ ERROR expected a path on the left-hand side of `+`, not `( Copy + Copy)`
let _: Box<(Copy +) + Copy>; let _: Box<(Copy +) + Copy>;
//~^ ERROR expected a path on the left-hand side of `+`, not `( 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)`
} }

View File

@ -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");
}

View File

@ -13,11 +13,11 @@ error: expected type, found keyword `true`
18 | foo!(true); 18 | foo!(true);
| ^^^^ expecting a type here because of type ascription | ^^^^ 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 --> $DIR/issue-44406.rs:18:10
| |
13 | bar(baz: $rest) 13 | bar(baz: $rest)
| - expected one of 19 possible tokens here | - expected one of 20 possible tokens here
... ...
18 | foo!(true); 18 | foo!(true);
| ^^^^ unexpected token | ^^^^ unexpected token