From e6115af4bd5f07024e0b73139e1dbcd68c96d9f7 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Tue, 10 Oct 2017 17:33:19 +0300 Subject: [PATCH] Implement `dyn Trait` syntax --- src/librustc/hir/lowering.rs | 2 +- src/librustc_passes/ast_validation.rs | 2 +- src/librustc_save_analysis/sig.rs | 2 +- src/libsyntax/ast.rs | 9 ++- src/libsyntax/feature_gate.rs | 7 ++ src/libsyntax/fold.rs | 4 +- src/libsyntax/parse/parser.rs | 66 +++++++++++-------- src/libsyntax/parse/token.rs | 6 ++ src/libsyntax/print/pprust.rs | 5 +- src/libsyntax/visit.rs | 2 +- src/libsyntax_pos/symbol.rs | 9 +-- .../compile-fail/dyn-trait-compatibility.rs | 29 ++++++++ .../compile-fail/feature-gate-dyn-trait.rs | 14 ++++ .../trait-bounds-not-on-struct.rs | 3 + .../parse-fail/trait-object-bad-parens.rs | 2 + src/test/run-pass/dyn-trait.rs | 24 +++++++ src/test/ui/issue-44406.stderr | 4 +- 17 files changed, 149 insertions(+), 41 deletions(-) create mode 100644 src/test/compile-fail/dyn-trait-compatibility.rs create mode 100644 src/test/compile-fail/feature-gate-dyn-trait.rs create mode 100644 src/test/run-pass/dyn-trait.rs diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 64a2ba1fa6f..54aecb4b00f 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -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 { diff --git a/src/librustc_passes/ast_validation.rs b/src/librustc_passes/ast_validation.rs index efb5b031809..14e33378969 100644 --- a/src/librustc_passes/ast_validation.rs +++ b/src/librustc_passes/ast_validation.rs @@ -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 { diff --git a/src/librustc_save_analysis/sig.rs b/src/librustc_save_analysis/sig.rs index c7e00245d63..47e5ad6c010 100644 --- a/src/librustc_save_analysis/sig.rs +++ b/src/librustc_save_analysis/sig.rs @@ -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)) diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index 021f96aff8c..f445def9e03 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1419,7 +1419,7 @@ pub enum TyKind { Path(Option, 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), @@ -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")`` diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index a2f29181a20..9060a613bc1 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -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! ( @@ -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) diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 03c47b71d02..d7d491db71f 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -400,8 +400,8 @@ pub fn noop_fold_ty(t: P, fld: &mut T) -> P { 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))) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 04e6cdcfe58..bd400ef6dd6 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -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}; @@ -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`, +// `IDENT<::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, @@ -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(), @@ -1472,6 +1479,35 @@ impl<'a> Parser<'a> { } else if self.eat(&token::Underscore) { // A type to be inferred `_` 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() { // Qualified path let (qself, path) = self.parse_qpath(PathStyle::Type)?; @@ -1493,29 +1529,6 @@ impl<'a> Parser<'a> { 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 { let msg = format!("expected type, found {}", self.this_token_descr()); return Err(self.fatal(&msg)); @@ -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, ()> { @@ -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) || diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 4888654fac9..20db87cfc82 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -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 { diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 959dd4ef30f..02f871c58c7 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -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[..])?; diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index 05077d42a0b..521c6030eba 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -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); } diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index 4d3db15ef29..872fc031cfb 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -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. diff --git a/src/test/compile-fail/dyn-trait-compatibility.rs b/src/test/compile-fail/dyn-trait-compatibility.rs new file mode 100644 index 00000000000..a7cfda504c7 --- /dev/null +++ b/src/test/compile-fail/dyn-trait-compatibility.rs @@ -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 or the MIT license +// , 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; +//~^ 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>; +//~^ 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() {} diff --git a/src/test/compile-fail/feature-gate-dyn-trait.rs b/src/test/compile-fail/feature-gate-dyn-trait.rs new file mode 100644 index 00000000000..4b3803d019b --- /dev/null +++ b/src/test/compile-fail/feature-gate-dyn-trait.rs @@ -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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +trait Trait {} +type A = Box; //~ ERROR `dyn Trait` syntax is unstable + +fn main() {} diff --git a/src/test/compile-fail/trait-bounds-not-on-struct.rs b/src/test/compile-fail/trait-bounds-not-on-struct.rs index cabe0fd48ed..6cd43916731 100644 --- a/src/test/compile-fail/trait-bounds-not-on-struct.rs +++ b/src/test/compile-fail/trait-bounds-not-on-struct.rs @@ -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) { } //~ ERROR expected trait, found struct `Foo` +type A = Box>; //~ ERROR expected trait, found struct `Vec` + fn main() { } diff --git a/src/test/parse-fail/trait-object-bad-parens.rs b/src/test/parse-fail/trait-object-bad-parens.rs index a44c0c3f32f..3e8c140eb19 100644 --- a/src/test/parse-fail/trait-object-bad-parens.rs +++ b/src/test/parse-fail/trait-object-bad-parens.rs @@ -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)` } diff --git a/src/test/run-pass/dyn-trait.rs b/src/test/run-pass/dyn-trait.rs new file mode 100644 index 00000000000..91930852a57 --- /dev/null +++ b/src/test/run-pass/dyn-trait.rs @@ -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 or the MIT license +// , 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 = Box::new(BYTE); + let xstr = format!("{}", x); + let ystr = format!("{}", y); + assert_eq!(xstr, "33"); + assert_eq!(ystr, "33"); +} diff --git a/src/test/ui/issue-44406.stderr b/src/test/ui/issue-44406.stderr index e07face24ba..9beae91540a 100644 --- a/src/test/ui/issue-44406.stderr +++ b/src/test/ui/issue-44406.stderr @@ -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