Skip to content

Commit 7920a7c

Browse files
committed
Auto merge of #45175 - petrochenkov:dyn, r=nikomatsakis
Implement `dyn Trait` syntax (RFC 2113) cc #44662 r? @nikomatsakis
2 parents 9c5e9a5 + 9d37320 commit 7920a7c

File tree

18 files changed

+150
-42
lines changed

18 files changed

+150
-42
lines changed

src/librustc/hir/lowering.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -705,7 +705,7 @@ impl<'a> LoweringContext<'a> {
705705
let expr = self.lower_body(None, |this| this.lower_expr(expr));
706706
hir::TyTypeof(expr)
707707
}
708-
TyKind::TraitObject(ref bounds) => {
708+
TyKind::TraitObject(ref bounds, ..) => {
709709
let mut lifetime_bound = None;
710710
let bounds = bounds.iter().filter_map(|bound| {
711711
match *bound {

src/librustc_passes/ast_validation.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
152152
err.emit();
153153
});
154154
}
155-
TyKind::TraitObject(ref bounds) => {
155+
TyKind::TraitObject(ref bounds, ..) => {
156156
let mut any_lifetime_bounds = false;
157157
for bound in bounds {
158158
if let RegionTyParamBound(ref lifetime) = *bound {

src/librustc_save_analysis/sig.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -288,7 +288,7 @@ impl Sig for ast::Ty {
288288
})
289289
}
290290
}
291-
ast::TyKind::TraitObject(ref bounds) => {
291+
ast::TyKind::TraitObject(ref bounds, ..) => {
292292
// FIXME recurse into bounds
293293
let nested = pprust::bounds_to_string(bounds);
294294
Ok(text_sig(nested))

src/libsyntax/ast.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -1419,7 +1419,7 @@ pub enum TyKind {
14191419
Path(Option<QSelf>, Path),
14201420
/// A trait object type `Bound1 + Bound2 + Bound3`
14211421
/// where `Bound` is a trait or a lifetime.
1422-
TraitObject(TyParamBounds),
1422+
TraitObject(TyParamBounds, TraitObjectSyntax),
14231423
/// An `impl Bound1 + Bound2 + Bound3` type
14241424
/// where `Bound` is a trait or a lifetime.
14251425
ImplTrait(TyParamBounds),
@@ -1438,6 +1438,13 @@ pub enum TyKind {
14381438
Err,
14391439
}
14401440

1441+
/// Syntax used to declare a trait object.
1442+
#[derive(Clone, Copy, PartialEq, Eq, RustcEncodable, RustcDecodable, Hash, Debug)]
1443+
pub enum TraitObjectSyntax {
1444+
Dyn,
1445+
None,
1446+
}
1447+
14411448
/// Inline assembly dialect.
14421449
///
14431450
/// E.g. `"intel"` as in `asm!("mov eax, 2" : "={eax}"(result) : : : "intel")``

src/libsyntax/feature_gate.rs

+7
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,9 @@ declare_features! (
398398

399399
// Default match binding modes (RFC 2005)
400400
(active, match_default_bindings, "1.22.0", Some(42640)),
401+
402+
// Trait object syntax with `dyn` prefix
403+
(active, dyn_trait, "1.22.0", Some(44662)),
401404
);
402405

403406
declare_features! (
@@ -1417,6 +1420,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
14171420
gate_feature_post!(&self, never_type, ty.span,
14181421
"The `!` type is experimental");
14191422
},
1423+
ast::TyKind::TraitObject(_, ast::TraitObjectSyntax::Dyn) => {
1424+
gate_feature_post!(&self, dyn_trait, ty.span,
1425+
"`dyn Trait` syntax is unstable");
1426+
}
14201427
_ => {}
14211428
}
14221429
visit::walk_ty(self, ty)

src/libsyntax/fold.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -400,8 +400,8 @@ pub fn noop_fold_ty<T: Folder>(t: P<Ty>, fld: &mut T) -> P<Ty> {
400400
TyKind::Typeof(expr) => {
401401
TyKind::Typeof(fld.fold_expr(expr))
402402
}
403-
TyKind::TraitObject(bounds) => {
404-
TyKind::TraitObject(bounds.move_map(|b| fld.fold_ty_param_bound(b)))
403+
TyKind::TraitObject(bounds, syntax) => {
404+
TyKind::TraitObject(bounds.move_map(|b| fld.fold_ty_param_bound(b)), syntax)
405405
}
406406
TyKind::ImplTrait(bounds) => {
407407
TyKind::ImplTrait(bounds.move_map(|b| fld.fold_ty_param_bound(b)))

src/libsyntax/parse/parser.rs

+40-26
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ use ast::{Stmt, StmtKind};
3333
use ast::{VariantData, StructField};
3434
use ast::StrStyle;
3535
use ast::SelfKind;
36-
use ast::{TraitItem, TraitRef};
36+
use ast::{TraitItem, TraitRef, TraitObjectSyntax};
3737
use ast::{Ty, TyKind, TypeBinding, TyParam, TyParamBounds};
3838
use ast::{ViewPath, ViewPathGlob, ViewPathList, ViewPathSimple};
3939
use ast::{Visibility, WhereClause};
@@ -364,6 +364,13 @@ fn is_ident_or_underscore(t: &token::Token) -> bool {
364364
t.is_ident() || *t == token::Underscore
365365
}
366366

367+
// Returns true if `IDENT t` can start a type - `IDENT::a::b`, `IDENT<u8, u8>`,
368+
// `IDENT<<u8 as Trait>::AssocTy>`, `IDENT(u8, u8) -> u8`.
369+
fn can_continue_type_after_ident(t: &token::Token) -> bool {
370+
t == &token::ModSep || t == &token::Lt ||
371+
t == &token::BinOp(token::Shl) || t == &token::OpenDelim(token::Paren)
372+
}
373+
367374
/// Information about the path to a module.
368375
pub struct ModulePath {
369376
pub name: String,
@@ -1428,7 +1435,7 @@ impl<'a> Parser<'a> {
14281435
TyKind::Path(None, ref path) if maybe_bounds => {
14291436
self.parse_remaining_bounds(Vec::new(), path.clone(), lo, true)?
14301437
}
1431-
TyKind::TraitObject(ref bounds)
1438+
TyKind::TraitObject(ref bounds, TraitObjectSyntax::None)
14321439
if maybe_bounds && bounds.len() == 1 && !trailing_plus => {
14331440
let path = match bounds[0] {
14341441
TraitTyParamBound(ref pt, ..) => pt.trait_ref.path.clone(),
@@ -1472,27 +1479,6 @@ impl<'a> Parser<'a> {
14721479
} else if self.eat(&token::Underscore) {
14731480
// A type to be inferred `_`
14741481
TyKind::Infer
1475-
} else if self.eat_lt() {
1476-
// Qualified path
1477-
let (qself, path) = self.parse_qpath(PathStyle::Type)?;
1478-
TyKind::Path(Some(qself), path)
1479-
} else if self.token.is_path_start() {
1480-
// Simple path
1481-
let path = self.parse_path(PathStyle::Type)?;
1482-
if self.eat(&token::Not) {
1483-
// Macro invocation in type position
1484-
let (_, tts) = self.expect_delimited_token_tree()?;
1485-
TyKind::Mac(respan(lo.to(self.span), Mac_ { path: path, tts: tts }))
1486-
} else {
1487-
// Just a type path or bound list (trait object type) starting with a trait.
1488-
// `Type`
1489-
// `Trait1 + Trait2 + 'a`
1490-
if allow_plus && self.check(&token::BinOp(token::Plus)) {
1491-
self.parse_remaining_bounds(Vec::new(), path, lo, true)?
1492-
} else {
1493-
TyKind::Path(None, path)
1494-
}
1495-
}
14961482
} else if self.token_is_bare_fn_keyword() {
14971483
// Function pointer type
14981484
self.parse_ty_bare_fn(Vec::new())?
@@ -1512,10 +1498,37 @@ impl<'a> Parser<'a> {
15121498
} else if self.eat_keyword(keywords::Impl) {
15131499
// FIXME: figure out priority of `+` in `impl Trait1 + Trait2` (#34511).
15141500
TyKind::ImplTrait(self.parse_ty_param_bounds()?)
1501+
} else if self.check_keyword(keywords::Dyn) &&
1502+
self.look_ahead(1, |t| t.can_begin_bound() && !can_continue_type_after_ident(t)) {
1503+
// FIXME: figure out priority of `+` in `dyn Trait1 + Trait2` (#34511).
1504+
self.bump(); // `dyn`
1505+
TyKind::TraitObject(self.parse_ty_param_bounds()?, TraitObjectSyntax::Dyn)
15151506
} else if self.check(&token::Question) ||
1516-
self.check_lifetime() && self.look_ahead(1, |t| t == &token::BinOp(token::Plus)){
1507+
self.check_lifetime() && self.look_ahead(1, |t| t == &token::BinOp(token::Plus)) {
15171508
// Bound list (trait object type)
1518-
TyKind::TraitObject(self.parse_ty_param_bounds_common(allow_plus)?)
1509+
TyKind::TraitObject(self.parse_ty_param_bounds_common(allow_plus)?,
1510+
TraitObjectSyntax::None)
1511+
} else if self.eat_lt() {
1512+
// Qualified path
1513+
let (qself, path) = self.parse_qpath(PathStyle::Type)?;
1514+
TyKind::Path(Some(qself), path)
1515+
} else if self.token.is_path_start() {
1516+
// Simple path
1517+
let path = self.parse_path(PathStyle::Type)?;
1518+
if self.eat(&token::Not) {
1519+
// Macro invocation in type position
1520+
let (_, tts) = self.expect_delimited_token_tree()?;
1521+
TyKind::Mac(respan(lo.to(self.span), Mac_ { path: path, tts: tts }))
1522+
} else {
1523+
// Just a type path or bound list (trait object type) starting with a trait.
1524+
// `Type`
1525+
// `Trait1 + Trait2 + 'a`
1526+
if allow_plus && self.check(&token::BinOp(token::Plus)) {
1527+
self.parse_remaining_bounds(Vec::new(), path, lo, true)?
1528+
} else {
1529+
TyKind::Path(None, path)
1530+
}
1531+
}
15191532
} else {
15201533
let msg = format!("expected type, found {}", self.this_token_descr());
15211534
return Err(self.fatal(&msg));
@@ -1538,7 +1551,7 @@ impl<'a> Parser<'a> {
15381551
self.bump(); // `+`
15391552
bounds.append(&mut self.parse_ty_param_bounds()?);
15401553
}
1541-
Ok(TyKind::TraitObject(bounds))
1554+
Ok(TyKind::TraitObject(bounds, TraitObjectSyntax::None))
15421555
}
15431556

15441557
fn maybe_recover_from_bad_type_plus(&mut self, allow_plus: bool, ty: &Ty) -> PResult<'a, ()> {
@@ -4256,6 +4269,7 @@ impl<'a> Parser<'a> {
42564269
fn parse_ty_param_bounds_common(&mut self, allow_plus: bool) -> PResult<'a, TyParamBounds> {
42574270
let mut bounds = Vec::new();
42584271
loop {
4272+
// This needs to be syncronized with `Token::can_begin_bound`.
42594273
let is_bound_start = self.check_path() || self.check_lifetime() ||
42604274
self.check(&token::Question) ||
42614275
self.check_keyword(keywords::For) ||

src/libsyntax/parse/token.rs

+6
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,12 @@ impl Token {
258258
}
259259
}
260260

261+
/// Returns `true` if the token can appear at the start of a generic bound.
262+
pub fn can_begin_bound(&self) -> bool {
263+
self.is_path_start() || self.is_lifetime() || self.is_keyword(keywords::For) ||
264+
self == &Question || self == &OpenDelim(Paren)
265+
}
266+
261267
/// Returns `true` if the token is any literal
262268
pub fn is_lit(&self) -> bool {
263269
match *self {

src/libsyntax/print/pprust.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -1049,8 +1049,9 @@ impl<'a> State<'a> {
10491049
ast::TyKind::Path(Some(ref qself), ref path) => {
10501050
self.print_qpath(path, qself, false)?
10511051
}
1052-
ast::TyKind::TraitObject(ref bounds) => {
1053-
self.print_bounds("", &bounds[..])?;
1052+
ast::TyKind::TraitObject(ref bounds, syntax) => {
1053+
let prefix = if syntax == ast::TraitObjectSyntax::Dyn { "dyn " } else { "" };
1054+
self.print_bounds(prefix, &bounds[..])?;
10541055
}
10551056
ast::TyKind::ImplTrait(ref bounds) => {
10561057
self.print_bounds("impl ", &bounds[..])?;

src/libsyntax/visit.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ pub fn walk_ty<'a, V: Visitor<'a>>(visitor: &mut V, typ: &'a Ty) {
348348
visitor.visit_ty(ty);
349349
visitor.visit_expr(expression)
350350
}
351-
TyKind::TraitObject(ref bounds) |
351+
TyKind::TraitObject(ref bounds, ..) |
352352
TyKind::ImplTrait(ref bounds) => {
353353
walk_list!(visitor, visit_ty_param_bound, bounds);
354354
}

src/libsyntax_pos/symbol.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -309,10 +309,11 @@ declare_keywords! {
309309
(54, Yield, "yield")
310310

311311
// Weak keywords, have special meaning only in specific contexts.
312-
(55, Default, "default")
313-
(56, StaticLifetime, "'static")
314-
(57, Union, "union")
315-
(58, Catch, "catch")
312+
(55, Catch, "catch")
313+
(56, Default, "default")
314+
(57, Dyn, "dyn")
315+
(58, StaticLifetime, "'static")
316+
(59, Union, "union")
316317
}
317318

318319
// If an interner exists in TLS, return it. Otherwise, prepare a fresh one.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
type A0 = dyn;
12+
//~^ ERROR cannot find type `dyn` in this scope
13+
type A1 = dyn::dyn;
14+
//~^ ERROR Use of undeclared type or module `dyn`
15+
type A2 = dyn<dyn, dyn>;
16+
//~^ ERROR cannot find type `dyn` in this scope
17+
//~| ERROR cannot find type `dyn` in this scope
18+
//~| ERROR cannot find type `dyn` in this scope
19+
type A3 = dyn<<dyn as dyn>::dyn>;
20+
//~^ ERROR cannot find type `dyn` in this scope
21+
//~| ERROR cannot find type `dyn` in this scope
22+
//~| ERROR Use of undeclared type or module `dyn`
23+
type A4 = dyn(dyn, dyn) -> dyn;
24+
//~^ ERROR cannot find type `dyn` in this scope
25+
//~| ERROR cannot find type `dyn` in this scope
26+
//~| ERROR cannot find type `dyn` in this scope
27+
//~| ERROR cannot find type `dyn` in this scope
28+
29+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
trait Trait {}
12+
type A = Box<dyn Trait>; //~ ERROR `dyn Trait` syntax is unstable
13+
14+
fn main() {}

src/test/compile-fail/trait-bounds-not-on-struct.rs

+3
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,12 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
#![feature(dyn_trait)]
1112

1213
struct Foo;
1314

1415
fn foo(_x: Box<Foo + Send>) { } //~ ERROR expected trait, found struct `Foo`
1516

17+
type A<T> = Box<dyn Vec<T>>; //~ ERROR expected trait, found struct `Vec`
18+
1619
fn main() { }

src/test/parse-fail/trait-object-bad-parens.rs

+2
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,6 @@ fn main() {
1717
//~^ ERROR expected a path on the left-hand side of `+`, not `( Copy + Copy)`
1818
let _: Box<(Copy +) + Copy>;
1919
//~^ ERROR expected a path on the left-hand side of `+`, not `( Copy)`
20+
let _: Box<(dyn Copy) + Copy>;
21+
//~^ ERROR expected a path on the left-hand side of `+`, not `(dyn Copy)`
2022
}

src/test/run-pass/dyn-trait.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
#![feature(dyn_trait)]
12+
13+
use std::fmt::Display;
14+
15+
static BYTE: u8 = 33;
16+
17+
fn main() {
18+
let x: &(dyn 'static + Display) = &BYTE;
19+
let y: Box<dyn Display + 'static> = Box::new(BYTE);
20+
let xstr = format!("{}", x);
21+
let ystr = format!("{}", y);
22+
assert_eq!(xstr, "33");
23+
assert_eq!(ystr, "33");
24+
}

src/test/ui/issue-44406.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@ error: expected type, found keyword `true`
1313
18 | foo!(true);
1414
| ^^^^ expecting a type here because of type ascription
1515

16-
error: expected one of `!`, `&&`, `&`, `(`, `*`, `.`, `;`, `<`, `?`, `[`, `_`, `extern`, `fn`, `for`, `impl`, `unsafe`, `}`, an operator, or lifetime, found `true`
16+
error: expected one of `!`, `&&`, `&`, `(`, `*`, `.`, `;`, `<`, `?`, `[`, `_`, `dyn`, `extern`, `fn`, `for`, `impl`, `unsafe`, `}`, an operator, or lifetime, found `true`
1717
--> $DIR/issue-44406.rs:18:10
1818
|
1919
13 | bar(baz: $rest)
20-
| - expected one of 19 possible tokens here
20+
| - expected one of 20 possible tokens here
2121
...
2222
18 | foo!(true);
2323
| ^^^^ unexpected token

src/tools/rustfmt

0 commit comments

Comments
 (0)