]> git.lizzy.rs Git - rust.git/commitdiff
Implement `dyn Trait` syntax
authorVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Tue, 10 Oct 2017 14:33:19 +0000 (17:33 +0300)
committerVadim Petrochenkov <vadim.petrochenkov@gmail.com>
Sat, 14 Oct 2017 09:51:13 +0000 (12:51 +0300)
17 files changed:
src/librustc/hir/lowering.rs
src/librustc_passes/ast_validation.rs
src/librustc_save_analysis/sig.rs
src/libsyntax/ast.rs
src/libsyntax/feature_gate.rs
src/libsyntax/fold.rs
src/libsyntax/parse/parser.rs
src/libsyntax/parse/token.rs
src/libsyntax/print/pprust.rs
src/libsyntax/visit.rs
src/libsyntax_pos/symbol.rs
src/test/compile-fail/dyn-trait-compatibility.rs [new file with mode: 0644]
src/test/compile-fail/feature-gate-dyn-trait.rs [new file with mode: 0644]
src/test/compile-fail/trait-bounds-not-on-struct.rs
src/test/parse-fail/trait-object-bad-parens.rs
src/test/run-pass/dyn-trait.rs [new file with mode: 0644]
src/test/ui/issue-44406.stderr

index 64a2ba1fa6f246fc951fb429b4d8188c8834d777..54aecb4b00f826580f8d0e683ec2d1e80c7d4650 100644 (file)
@@ -705,7 +705,7 @@ fn lower_ty(&mut self, t: &Ty) -> P<hir::Ty> {
                 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 {
index efb5b0318099822877662560b6fe457ecbd971f6..14e33378969aaa8cd25d1f091917fe7e1740acb2 100644 (file)
@@ -152,7 +152,7 @@ fn visit_ty(&mut self, ty: &'a Ty) {
                     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 {
index c7e00245d63502b6b883ac079f79ff9cbfe1c636..47e5ad6c0108834a427f7d8e6331c4cd73229c2f 100644 (file)
@@ -288,7 +288,7 @@ fn make(&self, offset: usize, _parent_id: Option<NodeId>, scx: &SaveContext) ->
                     })
                 }
             }
-            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))
index 021f96aff8caf94ec84038a8dc999f02df86f61f..f445def9e0335182dbed2e0118e0a8f288098706 100644 (file)
@@ -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),
@@ -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")``
index a2f29181a20d3ec5f423ddfb7f2efe173f16bd52..9060a613bc132d87f03f2300483046420bc62511 100644 (file)
@@ -398,6 +398,9 @@ pub fn new() -> 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 @@ fn visit_ty(&mut self, ty: &'a ast::Ty) {
                 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)
index 03c47b71d02d717c223ec953233e6b8a309e6a8c..d7d491db71f05bfb449c7d5d21436a2da42de7be 100644 (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(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)))
index 04e6cdcfe5820fe0197f73b0c46d8c5dbe8b8996..bd400ef6dd6dab9ecdbb0fd960171bc9c13ec94a 100644 (file)
@@ -33,7 +33,7 @@
 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<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,
@@ -1428,7 +1435,7 @@ fn parse_ty_common(&mut self, allow_plus: bool) -> PResult<'a, P<Ty>> {
                     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,27 +1479,6 @@ fn parse_ty_common(&mut self, allow_plus: bool) -> PResult<'a, P<Ty>> {
         } 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())?
@@ -1512,10 +1498,37 @@ fn parse_ty_common(&mut self, allow_plus: bool) -> PResult<'a, P<Ty>> {
         } 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));
@@ -1538,7 +1551,7 @@ fn parse_remaining_bounds(&mut self, lifetime_defs: Vec<LifetimeDef>, path: ast:
             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 @@ fn warn_dotdoteq(&self, span: Span) {
     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) ||
index 4888654fac9d0ed09c9454ee6f23b92731757b40..20db87cfc82d9849367418a197ed876691136aa0 100644 (file)
@@ -258,6 +258,12 @@ pub fn can_begin_type(&self) -> bool {
         }
     }
 
+    /// 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 {
index 959dd4ef30f29853506d4288199b5a14c0e44b0d..02f871c58c73cfb7065d05f8f34c98d16b656fbf 100644 (file)
@@ -1049,8 +1049,9 @@ pub fn print_type(&mut self, ty: &ast::Ty) -> io::Result<()> {
             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[..])?;
index 05077d42a0bedb6886ee60069cc03490f6513c26..521c6030eba2cc06d064e78ec08a475631638e69 100644 (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_expr(expression)
         }
-        TyKind::TraitObject(ref bounds) |
+        TyKind::TraitObject(ref bounds, ..) |
         TyKind::ImplTrait(ref bounds) => {
             walk_list!(visitor, visit_ty_param_bound, bounds);
         }
index 4d3db15ef29dbd8fead00742db3303c032284af2..872fc031cfb1a8a0fe980b86a5f6e34857884de9 100644 (file)
@@ -309,10 +309,11 @@ fn fresh() -> Self {
     (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 (file)
index 0000000..a7cfda5
--- /dev/null
@@ -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() {}
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 (file)
index 0000000..4b3803d
--- /dev/null
@@ -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() {}
index cabe0fd48edf99d0124ef40e19b45060f8ec4cf0..6cd439167314bd9ba4f684f4538b26b1bb8b45b5 100644 (file)
@@ -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() { }
index a44c0c3f32fef0b46b8d3f4e8b6f697cb9abd6ca..3e8c140eb197d154c9f7af6f02ef97d908e7d7fa 100644 (file)
@@ -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 (file)
index 0000000..9193085
--- /dev/null
@@ -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");
+}
index e07face24bab7b0c4acfd1dfd4e8693e64e5a208..9beae91540abfd09a1ebdd8e6d68c2b72e8b2c69 100644 (file)
@@ -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