Skip to content

Commit 6572ba1

Browse files
authored
Unrolled build for #150130
Rollup merge of #150130 - aerooneqq:delegation-one-line-trait-impl, r=petrochenkov Support syntax for one-line trait reuse This PR adds support for reusing the whole trait with a one-line reuse syntax and is part of the delegation feature #118212: ```rust trait T { fn foo(&self); } struct S; impl T for S { ... } struct Wrapper(S); reuse impl T for Wrapper { self.0 } ``` The core idea is that we already have support for glob reuse, so in this scenario we want to transform one-line reuse into a trait impl block with a glob reuse in the following way: ```rust //Before reuse impl T for Wrapper { self.0 } //After impl T for Wrapper { reuse T::* { self.0 } } ``` It seems like this task can be solved during parsing stage, when we encountered a one-line trait reuse, we can expand into this impl block right away, and the code which was already written to expand glob delegations will take care about the rest. We will copy trait path into glob reuse path. The implementation of the transformation reuses already existing methods for `impl` parsing, however, we do not parse inner `impl` items, instead we parse "inner items" as delegation body. Thus, we do not have to deal with generics, consts, unsafe and other `impl` related features. Other syntax possibility is trying to shorten one-line reuse by replacing `impl` keyword with `reuse` keyword: ```rust reuse T for Wrapper { self.0 } ``` In this case implementation may become more complicated, and the syntax more confusing, as keywords such as `const` or `unsafe` will precede `reuse`, and there are also generics: ```rust unsafe reuse<T1, T2> T for Wrapper { self.0 } ``` In the first (currently implemented) version reuse is placed in the beginning of the item, and it is clear that we will reuse trait implementation, while in the second, shorter version, the `reuse` keyword may be lost in generics and keywords that may precede `impl`. r? ``@petrochenkov``
2 parents 0ac9e59 + 1de1885 commit 6572ba1

20 files changed

Lines changed: 879 additions & 33 deletions

compiler/rustc_parse/messages.ftl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,8 @@ parse_default_not_followed_by_item = `default` is not followed by an item
162162
.label = the `default` qualifier
163163
.note = only `fn`, `const`, `type`, or `impl` items may be prefixed by `default`
164164
165+
parse_delegation_non_trait_impl_reuse = only trait impls can be reused
166+
165167
parse_do_catch_syntax_removed = found removed `do catch` syntax
166168
.note = following RFC #2388, the new non-placeholder syntax is `try`
167169
.suggestion = replace with the new syntax

compiler/rustc_parse/src/errors.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3671,3 +3671,10 @@ pub(crate) struct VarargsWithoutPattern {
36713671
#[suggestion(code = "_: ...", applicability = "machine-applicable")]
36723672
pub span: Span,
36733673
}
3674+
3675+
#[derive(Diagnostic)]
3676+
#[diag(parse_delegation_non_trait_impl_reuse)]
3677+
pub(crate) struct ImplReuseInherentImpl {
3678+
#[primary_span]
3679+
pub span: Span,
3680+
}

compiler/rustc_parse/src/parser/item.rs

Lines changed: 111 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,11 @@ impl<'a> Parser<'a> {
117117
}
118118
}
119119

120+
enum ReuseKind {
121+
Path,
122+
Impl,
123+
}
124+
120125
impl<'a> Parser<'a> {
121126
pub fn parse_item(&mut self, force_collect: ForceCollect) -> PResult<'a, Option<Box<Item>>> {
122127
let fn_parse_mode =
@@ -249,9 +254,9 @@ impl<'a> Parser<'a> {
249254
} else if self.check_keyword_case(exp!(Trait), case) || self.check_trait_front_matter() {
250255
// TRAIT ITEM
251256
self.parse_item_trait(attrs, lo)?
252-
} else if self.check_impl_frontmatter() {
257+
} else if self.check_impl_frontmatter(0) {
253258
// IMPL ITEM
254-
self.parse_item_impl(attrs, def_())?
259+
self.parse_item_impl(attrs, def_(), false)?
255260
} else if let Const::Yes(const_span) = self.parse_constness(case) {
256261
// CONST ITEM
257262
self.recover_const_mut(const_span);
@@ -265,8 +270,8 @@ impl<'a> Parser<'a> {
265270
rhs,
266271
define_opaque: None,
267272
}))
268-
} else if self.is_reuse_path_item() {
269-
self.parse_item_delegation()?
273+
} else if let Some(kind) = self.is_reuse_item() {
274+
self.parse_item_delegation(attrs, def_(), kind)?
270275
} else if self.check_keyword_case(exp!(Mod), case)
271276
|| self.check_keyword_case(exp!(Unsafe), case) && self.is_keyword_ahead(1, &[kw::Mod])
272277
{
@@ -367,16 +372,25 @@ impl<'a> Parser<'a> {
367372
/// When parsing a statement, would the start of a path be an item?
368373
pub(super) fn is_path_start_item(&mut self) -> bool {
369374
self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }`
370-
|| self.is_reuse_path_item()
375+
|| self.is_reuse_item().is_some() // yes: `reuse impl Trait for Struct { self.0 }`, yes: `reuse some_path::foo;`
371376
|| self.check_trait_front_matter() // no: `auto::b`, yes: `auto trait X { .. }`
372377
|| self.is_async_fn() // no(2015): `async::b`, yes: `async fn`
373378
|| matches!(self.is_macro_rules_item(), IsMacroRulesItem::Yes{..}) // no: `macro_rules::b`, yes: `macro_rules! mac`
374379
}
375380

376-
fn is_reuse_path_item(&mut self) -> bool {
381+
fn is_reuse_item(&mut self) -> Option<ReuseKind> {
382+
if !self.token.is_keyword(kw::Reuse) {
383+
return None;
384+
}
385+
377386
// no: `reuse ::path` for compatibility reasons with macro invocations
378-
self.token.is_keyword(kw::Reuse)
379-
&& self.look_ahead(1, |t| t.is_path_start() && *t != token::PathSep)
387+
if self.look_ahead(1, |t| t.is_path_start() && *t != token::PathSep) {
388+
Some(ReuseKind::Path)
389+
} else if self.check_impl_frontmatter(1) {
390+
Some(ReuseKind::Impl)
391+
} else {
392+
None
393+
}
380394
}
381395

382396
/// Are we sure this could not possibly be a macro invocation?
@@ -560,6 +574,7 @@ impl<'a> Parser<'a> {
560574
&mut self,
561575
attrs: &mut AttrVec,
562576
defaultness: Defaultness,
577+
is_reuse: bool,
563578
) -> PResult<'a, ItemKind> {
564579
let mut constness = self.parse_constness(Case::Sensitive);
565580
let safety = self.parse_safety(Case::Sensitive);
@@ -628,7 +643,11 @@ impl<'a> Parser<'a> {
628643

629644
generics.where_clause = self.parse_where_clause()?;
630645

631-
let impl_items = self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))?;
646+
let impl_items = if is_reuse {
647+
Default::default()
648+
} else {
649+
self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))?
650+
};
632651

633652
let (of_trait, self_ty) = match ty_second {
634653
Some(ty_second) => {
@@ -699,10 +718,76 @@ impl<'a> Parser<'a> {
699718
Ok(ItemKind::Impl(Impl { generics, of_trait, self_ty, items: impl_items, constness }))
700719
}
701720

702-
fn parse_item_delegation(&mut self) -> PResult<'a, ItemKind> {
721+
fn parse_item_delegation(
722+
&mut self,
723+
attrs: &mut AttrVec,
724+
defaultness: Defaultness,
725+
kind: ReuseKind,
726+
) -> PResult<'a, ItemKind> {
703727
let span = self.token.span;
704728
self.expect_keyword(exp!(Reuse))?;
705729

730+
let item_kind = match kind {
731+
ReuseKind::Path => self.parse_path_like_delegation(),
732+
ReuseKind::Impl => self.parse_impl_delegation(span, attrs, defaultness),
733+
}?;
734+
735+
self.psess.gated_spans.gate(sym::fn_delegation, span.to(self.prev_token.span));
736+
737+
Ok(item_kind)
738+
}
739+
740+
fn parse_delegation_body(&mut self) -> PResult<'a, Option<Box<Block>>> {
741+
Ok(if self.check(exp!(OpenBrace)) {
742+
Some(self.parse_block()?)
743+
} else {
744+
self.expect(exp!(Semi))?;
745+
None
746+
})
747+
}
748+
749+
fn parse_impl_delegation(
750+
&mut self,
751+
span: Span,
752+
attrs: &mut AttrVec,
753+
defaultness: Defaultness,
754+
) -> PResult<'a, ItemKind> {
755+
let mut impl_item = self.parse_item_impl(attrs, defaultness, true)?;
756+
let ItemKind::Impl(Impl { items, of_trait, .. }) = &mut impl_item else { unreachable!() };
757+
758+
let until_expr_span = span.to(self.prev_token.span);
759+
760+
let Some(of_trait) = of_trait else {
761+
return Err(self
762+
.dcx()
763+
.create_err(errors::ImplReuseInherentImpl { span: until_expr_span }));
764+
};
765+
766+
let body = self.parse_delegation_body()?;
767+
let whole_reuse_span = span.to(self.prev_token.span);
768+
769+
items.push(Box::new(AssocItem {
770+
id: DUMMY_NODE_ID,
771+
attrs: Default::default(),
772+
span: whole_reuse_span,
773+
tokens: None,
774+
vis: Visibility {
775+
kind: VisibilityKind::Inherited,
776+
span: whole_reuse_span,
777+
tokens: None,
778+
},
779+
kind: AssocItemKind::DelegationMac(Box::new(DelegationMac {
780+
qself: None,
781+
prefix: of_trait.trait_ref.path.clone(),
782+
suffixes: None,
783+
body,
784+
})),
785+
}));
786+
787+
Ok(impl_item)
788+
}
789+
790+
fn parse_path_like_delegation(&mut self) -> PResult<'a, ItemKind> {
706791
let (qself, path) = if self.eat_lt() {
707792
let (qself, path) = self.parse_qpath(PathStyle::Expr)?;
708793
(Some(qself), path)
@@ -713,43 +798,35 @@ impl<'a> Parser<'a> {
713798
let rename = |this: &mut Self| {
714799
Ok(if this.eat_keyword(exp!(As)) { Some(this.parse_ident()?) } else { None })
715800
};
716-
let body = |this: &mut Self| {
717-
Ok(if this.check(exp!(OpenBrace)) {
718-
Some(this.parse_block()?)
719-
} else {
720-
this.expect(exp!(Semi))?;
721-
None
722-
})
723-
};
724801

725-
let item_kind = if self.eat_path_sep() {
802+
Ok(if self.eat_path_sep() {
726803
let suffixes = if self.eat(exp!(Star)) {
727804
None
728805
} else {
729806
let parse_suffix = |p: &mut Self| Ok((p.parse_path_segment_ident()?, rename(p)?));
730807
Some(self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), parse_suffix)?.0)
731808
};
732-
let deleg = DelegationMac { qself, prefix: path, suffixes, body: body(self)? };
733-
ItemKind::DelegationMac(Box::new(deleg))
809+
810+
ItemKind::DelegationMac(Box::new(DelegationMac {
811+
qself,
812+
prefix: path,
813+
suffixes,
814+
body: self.parse_delegation_body()?,
815+
}))
734816
} else {
735817
let rename = rename(self)?;
736818
let ident = rename.unwrap_or_else(|| path.segments.last().unwrap().ident);
737-
let deleg = Delegation {
819+
820+
ItemKind::Delegation(Box::new(Delegation {
738821
id: DUMMY_NODE_ID,
739822
qself,
740823
path,
741824
ident,
742825
rename,
743-
body: body(self)?,
826+
body: self.parse_delegation_body()?,
744827
from_glob: false,
745-
};
746-
ItemKind::Delegation(Box::new(deleg))
747-
};
748-
749-
let span = span.to(self.prev_token.span);
750-
self.psess.gated_spans.gate(sym::fn_delegation, span);
751-
752-
Ok(item_kind)
828+
}))
829+
})
753830
}
754831

755832
fn parse_item_list<T>(
@@ -2594,7 +2671,7 @@ impl<'a> Parser<'a> {
25942671
Ok(body)
25952672
}
25962673

2597-
fn check_impl_frontmatter(&mut self) -> bool {
2674+
fn check_impl_frontmatter(&mut self, look_ahead: usize) -> bool {
25982675
const ALL_QUALS: &[Symbol] = &[kw::Const, kw::Unsafe];
25992676
// In contrast to the loop below, this call inserts `impl` into the
26002677
// list of expected tokens shown in diagnostics.
@@ -2603,7 +2680,7 @@ impl<'a> Parser<'a> {
26032680
}
26042681
let mut i = 0;
26052682
while i < ALL_QUALS.len() {
2606-
let action = self.look_ahead(i, |token| {
2683+
let action = self.look_ahead(i + look_ahead, |token| {
26072684
if token.is_keyword(kw::Impl) {
26082685
return Some(true);
26092686
}
@@ -2618,6 +2695,7 @@ impl<'a> Parser<'a> {
26182695
}
26192696
i += 1;
26202697
}
2698+
26212699
self.is_keyword_ahead(i, &[kw::Impl])
26222700
}
26232701

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#![feature(prelude_import)]
2+
#![no_std]
3+
//@ pretty-compare-only
4+
//@ pretty-mode:expanded
5+
//@ pp-exact:delegation-impl-reuse.pp
6+
7+
#![allow(incomplete_features)]
8+
#![feature(fn_delegation)]
9+
#[macro_use]
10+
extern crate std;
11+
#[prelude_import]
12+
use ::std::prelude::rust_2015::*;
13+
14+
trait Trait<T> {
15+
fn foo(&self) {}
16+
fn bar(&self) {}
17+
fn baz(&self) {}
18+
}
19+
20+
struct S;
21+
22+
impl Trait<{
23+
struct S;
24+
0
25+
}> for S {
26+
reuse Trait<{
27+
struct S;
28+
0
29+
}>::foo {
30+
self.0
31+
}
32+
reuse Trait<{
33+
struct S;
34+
0
35+
}>::bar {
36+
self.0
37+
}
38+
reuse Trait<{
39+
struct S;
40+
0
41+
}>::baz {
42+
self.0
43+
}
44+
}
45+
46+
fn main() {}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//@ pretty-compare-only
2+
//@ pretty-mode:expanded
3+
//@ pp-exact:delegation-impl-reuse.pp
4+
5+
#![allow(incomplete_features)]
6+
#![feature(fn_delegation)]
7+
8+
trait Trait<T> {
9+
fn foo(&self) {}
10+
fn bar(&self) {}
11+
fn baz(&self) {}
12+
}
13+
14+
struct S;
15+
16+
reuse impl Trait<{ struct S; 0 }> for S { self.0 }
17+
18+
fn main() {}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#![allow(incomplete_features)]
2+
#![feature(fn_delegation)]
3+
4+
mod unresolved {
5+
struct S;
6+
reuse impl unresolved for S { self.0 }
7+
//~^ ERROR failed to resolve: use of unresolved module or unlinked crate `unresolved`
8+
//~| ERROR cannot find trait `unresolved` in this scope
9+
10+
trait T {}
11+
reuse impl T for unresolved { self.0 }
12+
//~^ ERROR empty glob delegation is not supported
13+
//~| ERROR cannot find type `unresolved` in this scope
14+
}
15+
16+
mod wrong_entities {
17+
trait T {}
18+
struct Trait;
19+
struct S;
20+
21+
reuse impl Trait for S { self.0 }
22+
//~^ ERROR expected trait, found struct `Trait`
23+
//~| ERROR expected trait, found struct `Trait`
24+
25+
mod TraitModule {}
26+
reuse impl TraitModule for S { self.0 }
27+
//~^ ERROR expected trait, found module `TraitModule`
28+
//~| ERROR expected trait, found module `TraitModule`
29+
}
30+
31+
fn main() {}

0 commit comments

Comments
 (0)