Implement dyn Trait
syntax
This commit is contained in:
parent
3037965b5b
commit
e6115af4bd
@ -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 {
|
||||
|
@ -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 {
|
||||
|
@ -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))
|
||||
|
@ -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")``
|
||||
|
@ -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)
|
||||
|
@ -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)))
|
||||
|
@ -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<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 @@ 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) ||
|
||||
|
@ -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 {
|
||||
|
@ -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[..])?;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
29
src/test/compile-fail/dyn-trait-compatibility.rs
Normal file
29
src/test/compile-fail/dyn-trait-compatibility.rs
Normal 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() {}
|
14
src/test/compile-fail/feature-gate-dyn-trait.rs
Normal file
14
src/test/compile-fail/feature-gate-dyn-trait.rs
Normal 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() {}
|
@ -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() { }
|
||||
|
@ -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
src/test/run-pass/dyn-trait.rs
Normal file
24
src/test/run-pass/dyn-trait.rs
Normal 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");
|
||||
}
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user