From 829d1bfbf4bd4164b3881f42d61c0448b78ca901 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 19 Dec 2025 15:59:06 +0300 Subject: [PATCH 1/5] Support syntax for one-line trait reuse --- compiler/rustc_parse/messages.ftl | 3 + compiler/rustc_parse/src/errors.rs | 14 + compiler/rustc_parse/src/parser/item.rs | 167 +++++++--- .../one-line-trait-reuse-bad-path.rs | 31 ++ .../one-line-trait-reuse-bad-path.stderr | 52 ++++ .../one-line-trait-reuse-empty-glob.rs | 14 + .../one-line-trait-reuse-empty-glob.stderr | 8 + .../one-line-trait-reuse-illegal-places.rs | 51 +++ ...one-line-trait-reuse-illegal-places.stderr | 92 ++++++ .../one-line-trait-reuse-negative-traits.rs | 19 ++ ...ne-line-trait-reuse-negative-traits.stderr | 8 + .../one-line-trait-reuse-non-reuse-items.rs | 32 ++ ...ne-line-trait-reuse-non-reuse-items.stderr | 60 ++++ .../one-line-trait-reuse-non-trait-impl.rs | 9 + ...one-line-trait-reuse-non-trait-impl.stderr | 8 + .../delegation/one-line-trait-reuse-pass.rs | 292 ++++++++++++++++++ 16 files changed, 824 insertions(+), 36 deletions(-) create mode 100644 tests/ui/delegation/one-line-trait-reuse-bad-path.rs create mode 100644 tests/ui/delegation/one-line-trait-reuse-bad-path.stderr create mode 100644 tests/ui/delegation/one-line-trait-reuse-empty-glob.rs create mode 100644 tests/ui/delegation/one-line-trait-reuse-empty-glob.stderr create mode 100644 tests/ui/delegation/one-line-trait-reuse-illegal-places.rs create mode 100644 tests/ui/delegation/one-line-trait-reuse-illegal-places.stderr create mode 100644 tests/ui/delegation/one-line-trait-reuse-negative-traits.rs create mode 100644 tests/ui/delegation/one-line-trait-reuse-negative-traits.stderr create mode 100644 tests/ui/delegation/one-line-trait-reuse-non-reuse-items.rs create mode 100644 tests/ui/delegation/one-line-trait-reuse-non-reuse-items.stderr create mode 100644 tests/ui/delegation/one-line-trait-reuse-non-trait-impl.rs create mode 100644 tests/ui/delegation/one-line-trait-reuse-non-trait-impl.stderr create mode 100644 tests/ui/delegation/one-line-trait-reuse-pass.rs diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 87d1173c0d486..26b0e1e92df52 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -162,6 +162,9 @@ parse_default_not_followed_by_item = `default` is not followed by an item .label = the `default` qualifier .note = only `fn`, `const`, `type`, or `impl` items may be prefixed by `default` +parse_delegation_non_trait_impl_reuse = only trait impls can be reused +parse_delegation_reuse_negative_trait_impls = reuse of negative trait impls is not allowed + parse_do_catch_syntax_removed = found removed `do catch` syntax .note = following RFC #2388, the new non-placeholder syntax is `try` .suggestion = replace with the new syntax diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 698d8f76aaa64..47c355986def7 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3671,3 +3671,17 @@ pub(crate) struct VarargsWithoutPattern { #[suggestion(code = "_: ...", applicability = "machine-applicable")] pub span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_delegation_non_trait_impl_reuse)] +pub(crate) struct OneLineReuseAllowedOnlyForTraitsImpls { + #[primary_span] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_delegation_reuse_negative_trait_impls)] +pub(crate) struct OneLineReuseOfNegativeTraitImplsNotAllowed { + #[primary_span] + pub span: Span, +} diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 33913fd351a18..41d59c4f69ef1 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -117,6 +117,11 @@ impl<'a> Parser<'a> { } } +enum ReuseKind { + Path, + OneLineTraitReuse, +} + impl<'a> Parser<'a> { pub fn parse_item(&mut self, force_collect: ForceCollect) -> PResult<'a, Option>> { let fn_parse_mode = @@ -249,9 +254,9 @@ impl<'a> Parser<'a> { } else if self.check_keyword_case(exp!(Trait), case) || self.check_trait_front_matter() { // TRAIT ITEM self.parse_item_trait(attrs, lo)? - } else if self.check_impl_frontmatter() { + } else if self.check_impl_frontmatter_with_diagnostics() { // IMPL ITEM - self.parse_item_impl(attrs, def_())? + self.parse_item_impl(attrs, def_(), false)? } else if let Const::Yes(const_span) = self.parse_constness(case) { // CONST ITEM self.recover_const_mut(const_span); @@ -265,8 +270,8 @@ impl<'a> Parser<'a> { rhs, define_opaque: None, })) - } else if self.is_reuse_path_item() { - self.parse_item_delegation()? + } else if let Some(kind) = self.is_reuse_item() { + self.parse_item_delegation(attrs, def_(), kind)? } else if self.check_keyword_case(exp!(Mod), case) || self.check_keyword_case(exp!(Unsafe), case) && self.is_keyword_ahead(1, &[kw::Mod]) { @@ -367,16 +372,28 @@ impl<'a> Parser<'a> { /// When parsing a statement, would the start of a path be an item? pub(super) fn is_path_start_item(&mut self) -> bool { self.is_kw_followed_by_ident(kw::Union) // no: `union::b`, yes: `union U { .. }` - || self.is_reuse_path_item() + || self.is_reuse_item().is_some() // yes: `reuse impl Trait for Struct { self.0 }`, yes: `reuse some_path::foo;` || self.check_trait_front_matter() // no: `auto::b`, yes: `auto trait X { .. }` || self.is_async_fn() // no(2015): `async::b`, yes: `async fn` || matches!(self.is_macro_rules_item(), IsMacroRulesItem::Yes{..}) // no: `macro_rules::b`, yes: `macro_rules! mac` } - fn is_reuse_path_item(&mut self) -> bool { - // no: `reuse ::path` for compatibility reasons with macro invocations - self.token.is_keyword(kw::Reuse) - && self.look_ahead(1, |t| t.is_path_start() && *t != token::PathSep) + fn is_reuse_item(&self) -> Option { + self.token + .is_keyword(kw::Reuse) + .then(|| { + const LOOK_AHEAD_DIST: usize = 1; + + // no: `reuse ::path` for compatibility reasons with macro invocations + if self.look_ahead(LOOK_AHEAD_DIST, |t| t.is_path_start() && *t != token::PathSep) { + Some(ReuseKind::Path) + } else if self.look_ahead_check_impl_frontmatter(LOOK_AHEAD_DIST) { + Some(ReuseKind::OneLineTraitReuse) + } else { + None + } + }) + .flatten() } /// Are we sure this could not possibly be a macro invocation? @@ -560,6 +577,7 @@ impl<'a> Parser<'a> { &mut self, attrs: &mut AttrVec, defaultness: Defaultness, + is_one_line_trait_reuse: bool, ) -> PResult<'a, ItemKind> { let mut constness = self.parse_constness(Case::Sensitive); let safety = self.parse_safety(Case::Sensitive); @@ -628,7 +646,11 @@ impl<'a> Parser<'a> { generics.where_clause = self.parse_where_clause()?; - let impl_items = self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))?; + let impl_items = if is_one_line_trait_reuse { + Default::default() + } else { + self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))? + }; let (of_trait, self_ty) = match ty_second { Some(ty_second) => { @@ -699,10 +721,84 @@ impl<'a> Parser<'a> { Ok(ItemKind::Impl(Impl { generics, of_trait, self_ty, items: impl_items, constness })) } - fn parse_item_delegation(&mut self) -> PResult<'a, ItemKind> { + fn parse_item_delegation( + &mut self, + attrs: &mut AttrVec, + defaultness: Defaultness, + kind: ReuseKind, + ) -> PResult<'a, ItemKind> { let span = self.token.span; self.expect_keyword(exp!(Reuse))?; + let item_kind = match kind { + ReuseKind::Path => self.parse_path_like_delegation(), + ReuseKind::OneLineTraitReuse => { + self.parse_one_line_trait_delegation(span, attrs, defaultness) + } + }?; + + self.psess.gated_spans.gate(sym::fn_delegation, span.to(self.prev_token.span)); + + Ok(item_kind) + } + + fn parse_delegation_body(&mut self) -> PResult<'a, Option>> { + Ok(if self.check(exp!(OpenBrace)) { + Some(self.parse_block()?) + } else { + self.expect(exp!(Semi))?; + None + }) + } + + fn parse_one_line_trait_delegation( + &mut self, + span: Span, + attrs: &mut AttrVec, + defaultness: Defaultness, + ) -> PResult<'a, ItemKind> { + let mut impl_item = self.parse_item_impl(attrs, defaultness, true)?; + let ItemKind::Impl(Impl { items, of_trait, .. }) = &mut impl_item else { unreachable!() }; + + let until_expr_span = span.to(self.prev_token.span); + + let Some(of_trait) = of_trait else { + return Err(self.dcx().create_err(errors::OneLineReuseAllowedOnlyForTraitsImpls { + span: until_expr_span, + })); + }; + + if matches!(of_trait.polarity, ImplPolarity::Negative(..)) { + return Err(self.dcx().create_err( + errors::OneLineReuseOfNegativeTraitImplsNotAllowed { span: until_expr_span }, + )); + } + + let body = self.parse_delegation_body()?; + let whole_reuse_span = span.to(self.prev_token.span); + + items.push(Box::new(AssocItem { + id: DUMMY_NODE_ID, + attrs: Default::default(), + span: whole_reuse_span, + tokens: None, + vis: Visibility { + kind: VisibilityKind::Inherited, + span: whole_reuse_span, + tokens: None, + }, + kind: AssocItemKind::DelegationMac(Box::new(DelegationMac { + qself: None, + prefix: of_trait.trait_ref.path.clone(), + suffixes: None, + body, + })), + })); + + Ok(impl_item) + } + + fn parse_path_like_delegation(&mut self) -> PResult<'a, ItemKind> { let (qself, path) = if self.eat_lt() { let (qself, path) = self.parse_qpath(PathStyle::Expr)?; (Some(qself), path) @@ -713,43 +809,35 @@ impl<'a> Parser<'a> { let rename = |this: &mut Self| { Ok(if this.eat_keyword(exp!(As)) { Some(this.parse_ident()?) } else { None }) }; - let body = |this: &mut Self| { - Ok(if this.check(exp!(OpenBrace)) { - Some(this.parse_block()?) - } else { - this.expect(exp!(Semi))?; - None - }) - }; - let item_kind = if self.eat_path_sep() { + if self.eat_path_sep() { let suffixes = if self.eat(exp!(Star)) { None } else { let parse_suffix = |p: &mut Self| Ok((p.parse_path_segment_ident()?, rename(p)?)); Some(self.parse_delim_comma_seq(exp!(OpenBrace), exp!(CloseBrace), parse_suffix)?.0) }; - let deleg = DelegationMac { qself, prefix: path, suffixes, body: body(self)? }; - ItemKind::DelegationMac(Box::new(deleg)) + + Ok(ItemKind::DelegationMac(Box::new(DelegationMac { + qself, + prefix: path, + suffixes, + body: self.parse_delegation_body()?, + }))) } else { let rename = rename(self)?; let ident = rename.unwrap_or_else(|| path.segments.last().unwrap().ident); - let deleg = Delegation { + + Ok(ItemKind::Delegation(Box::new(Delegation { id: DUMMY_NODE_ID, qself, path, ident, rename, - body: body(self)?, + body: self.parse_delegation_body()?, from_glob: false, - }; - ItemKind::Delegation(Box::new(deleg)) - }; - - let span = span.to(self.prev_token.span); - self.psess.gated_spans.gate(sym::fn_delegation, span); - - Ok(item_kind) + }))) + } } fn parse_item_list( @@ -2594,16 +2682,22 @@ impl<'a> Parser<'a> { Ok(body) } - fn check_impl_frontmatter(&mut self) -> bool { - const ALL_QUALS: &[Symbol] = &[kw::Const, kw::Unsafe]; - // In contrast to the loop below, this call inserts `impl` into the + fn check_impl_frontmatter_with_diagnostics(&mut self) -> bool { + // In contrast to the method below, this call inserts `impl` into the // list of expected tokens shown in diagnostics. if self.check_keyword(exp!(Impl)) { return true; } + + self.look_ahead_check_impl_frontmatter(0) + } + + fn look_ahead_check_impl_frontmatter(&self, look_ahead_dist: usize) -> bool { + const ALL_QUALS: &[Symbol] = &[kw::Const, kw::Unsafe]; + let mut i = 0; while i < ALL_QUALS.len() { - let action = self.look_ahead(i, |token| { + let action = self.look_ahead(i + look_ahead_dist, |token| { if token.is_keyword(kw::Impl) { return Some(true); } @@ -2618,6 +2712,7 @@ impl<'a> Parser<'a> { } i += 1; } + self.is_keyword_ahead(i, &[kw::Impl]) } diff --git a/tests/ui/delegation/one-line-trait-reuse-bad-path.rs b/tests/ui/delegation/one-line-trait-reuse-bad-path.rs new file mode 100644 index 0000000000000..19eb51153468d --- /dev/null +++ b/tests/ui/delegation/one-line-trait-reuse-bad-path.rs @@ -0,0 +1,31 @@ +#![allow(incomplete_features)] +#![feature(fn_delegation)] + +mod unresolved { + struct S; + reuse impl unresolved for S { self.0 } + //~^ ERROR failed to resolve: use of unresolved module or unlinked crate `unresolved` + //~| ERROR cannot find trait `unresolved` in this scope + + trait T {} + reuse impl T for unresolved { self.0 } + //~^ ERROR empty glob delegation is not supported + //~| ERROR cannot find type `unresolved` in this scope +} + +mod wrong_entities { + trait T {} + struct Trait; + struct S; + + reuse impl Trait for S { self.0 } + //~^ ERROR expected trait, found struct `Trait` + //~| ERROR expected trait, found struct `Trait` + + mod TraitModule {} + reuse impl TraitModule for S { self.0 } + //~^ ERROR expected trait, found module `TraitModule` + //~| ERROR expected trait, found module `TraitModule` +} + +fn main() {} diff --git a/tests/ui/delegation/one-line-trait-reuse-bad-path.stderr b/tests/ui/delegation/one-line-trait-reuse-bad-path.stderr new file mode 100644 index 0000000000000..45fc26c44742c --- /dev/null +++ b/tests/ui/delegation/one-line-trait-reuse-bad-path.stderr @@ -0,0 +1,52 @@ +error: empty glob delegation is not supported + --> $DIR/one-line-trait-reuse-bad-path.rs:11:5 + | +LL | reuse impl T for unresolved { self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected trait, found struct `Trait` + --> $DIR/one-line-trait-reuse-bad-path.rs:21:16 + | +LL | reuse impl Trait for S { self.0 } + | ^^^^^ not a trait + +error: expected trait, found module `TraitModule` + --> $DIR/one-line-trait-reuse-bad-path.rs:26:16 + | +LL | reuse impl TraitModule for S { self.0 } + | ^^^^^^^^^^^ not a trait + +error[E0433]: failed to resolve: use of unresolved module or unlinked crate `unresolved` + --> $DIR/one-line-trait-reuse-bad-path.rs:6:16 + | +LL | reuse impl unresolved for S { self.0 } + | ^^^^^^^^^^ use of unresolved module or unlinked crate `unresolved` + +error[E0405]: cannot find trait `unresolved` in this scope + --> $DIR/one-line-trait-reuse-bad-path.rs:6:16 + | +LL | reuse impl unresolved for S { self.0 } + | ^^^^^^^^^^ not found in this scope + +error[E0425]: cannot find type `unresolved` in this scope + --> $DIR/one-line-trait-reuse-bad-path.rs:11:22 + | +LL | reuse impl T for unresolved { self.0 } + | ^^^^^^^^^^ not found in this scope + +error[E0404]: expected trait, found struct `Trait` + --> $DIR/one-line-trait-reuse-bad-path.rs:21:16 + | +LL | reuse impl Trait for S { self.0 } + | ^^^^^ not a trait + +error[E0404]: expected trait, found module `TraitModule` + --> $DIR/one-line-trait-reuse-bad-path.rs:26:16 + | +LL | reuse impl TraitModule for S { self.0 } + | ^^^^^^^^^^^ not a trait + +error: aborting due to 8 previous errors + +Some errors have detailed explanations: E0404, E0405, E0425, E0433. +For more information about an error, try `rustc --explain E0404`. diff --git a/tests/ui/delegation/one-line-trait-reuse-empty-glob.rs b/tests/ui/delegation/one-line-trait-reuse-empty-glob.rs new file mode 100644 index 0000000000000..3f1314c47da62 --- /dev/null +++ b/tests/ui/delegation/one-line-trait-reuse-empty-glob.rs @@ -0,0 +1,14 @@ +#![allow(incomplete_features)] +#![feature(fn_delegation)] + +mod empty_glob { + trait T {} + + struct S; + + reuse impl T for S { self.0 } + //~^ ERROR empty glob delegation is not supported +} + + +fn main() {} diff --git a/tests/ui/delegation/one-line-trait-reuse-empty-glob.stderr b/tests/ui/delegation/one-line-trait-reuse-empty-glob.stderr new file mode 100644 index 0000000000000..36272feb9a58e --- /dev/null +++ b/tests/ui/delegation/one-line-trait-reuse-empty-glob.stderr @@ -0,0 +1,8 @@ +error: empty glob delegation is not supported + --> $DIR/one-line-trait-reuse-empty-glob.rs:9:5 + | +LL | reuse impl T for S { self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/delegation/one-line-trait-reuse-illegal-places.rs b/tests/ui/delegation/one-line-trait-reuse-illegal-places.rs new file mode 100644 index 0000000000000..361331d41315d --- /dev/null +++ b/tests/ui/delegation/one-line-trait-reuse-illegal-places.rs @@ -0,0 +1,51 @@ +#![allow(incomplete_features)] +#![feature(fn_delegation)] + +trait T { + fn f(&self) {} +} + +struct S; +impl T for S {} + +struct F(S); + +struct X { + reuse impl T for F { self.0 } + //~^ ERROR expected `:`, found keyword `impl` +} + +impl X { + reuse impl T for F { self.0 } + //~^ ERROR implementation is not supported in `trait`s or `impl`s +} + +trait Trait { + reuse impl T for F { self.0 } + //~^ ERROR implementation is not supported in `trait`s or `impl`s +} + +extern "C" { + reuse impl T for F { self.0 } + //~^ ERROR implementation is not supported in `extern` blocks +} + +mod m { + mod inner { + pub fn foo() {} + } + + reuse inner::{ + reuse impl T for F { self.0 } + //~^ ERROR expected identifier, found keyword `impl` + //~| ERROR expected one of `,`, `as`, or `}`, found keyword `impl` + //~| ERROR expected one of `,`, `as`, or `}`, found `T` + //~| ERROR expected identifier, found keyword `for` + //~| ERROR expected one of `,`, `as`, or `}`, found keyword `for` + //~| ERROR expected one of `,`, `as`, or `}`, found `F` + //~| ERROR expected one of `,`, `as`, or `}`, found `{` + } +} +//~^ ERROR expected item, found `}` + +fn main() {} diff --git a/tests/ui/delegation/one-line-trait-reuse-illegal-places.stderr b/tests/ui/delegation/one-line-trait-reuse-illegal-places.stderr new file mode 100644 index 0000000000000..ceb94ac4e8fd1 --- /dev/null +++ b/tests/ui/delegation/one-line-trait-reuse-illegal-places.stderr @@ -0,0 +1,92 @@ +error: expected `:`, found keyword `impl` + --> $DIR/one-line-trait-reuse-illegal-places.rs:14:11 + | +LL | struct X { + | - while parsing this struct +LL | reuse impl T for F { self.0 } + | ^^^^ expected `:` + +error: implementation is not supported in `trait`s or `impl`s + --> $DIR/one-line-trait-reuse-illegal-places.rs:19:5 + | +LL | reuse impl T for F { self.0 } + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the implementation out to a nearby module scope + +error: implementation is not supported in `trait`s or `impl`s + --> $DIR/one-line-trait-reuse-illegal-places.rs:24:5 + | +LL | reuse impl T for F { self.0 } + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the implementation out to a nearby module scope + +error: implementation is not supported in `extern` blocks + --> $DIR/one-line-trait-reuse-illegal-places.rs:29:5 + | +LL | reuse impl T for F { self.0 } + | ^^^^^^^^^^^^^^^^^^ + | + = help: consider moving the implementation out to a nearby module scope + +error: expected identifier, found keyword `impl` + --> $DIR/one-line-trait-reuse-illegal-places.rs:39:15 + | +LL | reuse impl T for F { self.0 } + | ^^^^ expected identifier, found keyword + +error: expected one of `,`, `as`, or `}`, found keyword `impl` + --> $DIR/one-line-trait-reuse-illegal-places.rs:39:15 + | +LL | reuse impl T for F { self.0 } + | -^^^^ expected one of `,`, `as`, or `}` + | | + | help: missing `,` + +error: expected one of `,`, `as`, or `}`, found `T` + --> $DIR/one-line-trait-reuse-illegal-places.rs:39:20 + | +LL | reuse impl T for F { self.0 } + | -^ expected one of `,`, `as`, or `}` + | | + | help: missing `,` + +error: expected identifier, found keyword `for` + --> $DIR/one-line-trait-reuse-illegal-places.rs:39:22 + | +LL | reuse impl T for F { self.0 } + | ^^^ expected identifier, found keyword + +error: expected one of `,`, `as`, or `}`, found keyword `for` + --> $DIR/one-line-trait-reuse-illegal-places.rs:39:22 + | +LL | reuse impl T for F { self.0 } + | -^^^ expected one of `,`, `as`, or `}` + | | + | help: missing `,` + +error: expected one of `,`, `as`, or `}`, found `F` + --> $DIR/one-line-trait-reuse-illegal-places.rs:39:26 + | +LL | reuse impl T for F { self.0 } + | -^ expected one of `,`, `as`, or `}` + | | + | help: missing `,` + +error: expected one of `,`, `as`, or `}`, found `{` + --> $DIR/one-line-trait-reuse-illegal-places.rs:39:28 + | +LL | reuse impl T for F { self.0 } + | ^ expected one of `,`, `as`, or `}` + +error: expected item, found `}` + --> $DIR/one-line-trait-reuse-illegal-places.rs:48:1 + | +LL | } + | ^ expected item + | + = note: for a full list of items that can appear in modules, see + +error: aborting due to 12 previous errors + diff --git a/tests/ui/delegation/one-line-trait-reuse-negative-traits.rs b/tests/ui/delegation/one-line-trait-reuse-negative-traits.rs new file mode 100644 index 0000000000000..a375105e66622 --- /dev/null +++ b/tests/ui/delegation/one-line-trait-reuse-negative-traits.rs @@ -0,0 +1,19 @@ +#![allow(incomplete_features)] +#![feature(fn_delegation)] + + +trait Trait { + fn foo(&self); +} + +struct S; +impl Trait for S { + fn foo(&self) {} +} + +struct F(S); + +reuse impl !Trait for F { &self.0 } +//~^ ERROR reuse of negative trait impls is not allowed + +fn main() {} diff --git a/tests/ui/delegation/one-line-trait-reuse-negative-traits.stderr b/tests/ui/delegation/one-line-trait-reuse-negative-traits.stderr new file mode 100644 index 0000000000000..e2aedca2c5995 --- /dev/null +++ b/tests/ui/delegation/one-line-trait-reuse-negative-traits.stderr @@ -0,0 +1,8 @@ +error: reuse of negative trait impls is not allowed + --> $DIR/one-line-trait-reuse-negative-traits.rs:16:1 + | +LL | reuse impl !Trait for F { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/delegation/one-line-trait-reuse-non-reuse-items.rs b/tests/ui/delegation/one-line-trait-reuse-non-reuse-items.rs new file mode 100644 index 0000000000000..de7c92279218d --- /dev/null +++ b/tests/ui/delegation/one-line-trait-reuse-non-reuse-items.rs @@ -0,0 +1,32 @@ +#![allow(incomplete_features)] +#![feature(fn_delegation)] + +mod non_delegatable_items { + trait Trait { + fn method(&self); + const CONST: u8; + type Type; + #[allow(non_camel_case_types)] + type method; + } + + struct F; + impl Trait for F { + fn method(&self) {} + const CONST: u8 = 0; + type Type = u8; + type method = u8; + } + + struct S(F); + + reuse impl Trait for S { &self.0 } + //~^ ERROR item `CONST` is an associated method, which doesn't match its trait `Trait` + //~| ERROR item `Type` is an associated method, which doesn't match its trait `Trait` + //~| ERROR duplicate definitions with name `method` + //~| ERROR expected function, found associated constant `Trait::CONST` + //~| ERROR expected function, found associated type `Trait::Type` + //~| ERROR not all trait items implemented, missing: `CONST`, `Type`, `method` +} + +fn main() {} diff --git a/tests/ui/delegation/one-line-trait-reuse-non-reuse-items.stderr b/tests/ui/delegation/one-line-trait-reuse-non-reuse-items.stderr new file mode 100644 index 0000000000000..3e4d6a1f40815 --- /dev/null +++ b/tests/ui/delegation/one-line-trait-reuse-non-reuse-items.stderr @@ -0,0 +1,60 @@ +error[E0324]: item `CONST` is an associated method, which doesn't match its trait `Trait` + --> $DIR/one-line-trait-reuse-non-reuse-items.rs:23:5 + | +LL | const CONST: u8; + | ---------------- item in trait +... +LL | reuse impl Trait for S { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait + +error[E0324]: item `Type` is an associated method, which doesn't match its trait `Trait` + --> $DIR/one-line-trait-reuse-non-reuse-items.rs:23:5 + | +LL | type Type; + | ---------- item in trait +... +LL | reuse impl Trait for S { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait + +error[E0201]: duplicate definitions with name `method`: + --> $DIR/one-line-trait-reuse-non-reuse-items.rs:23:5 + | +LL | fn method(&self); + | ----------------- item in trait +... +LL | reuse impl Trait for S { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | + | duplicate definition + | previous definition here + +error[E0423]: expected function, found associated constant `Trait::CONST` + --> $DIR/one-line-trait-reuse-non-reuse-items.rs:23:16 + | +LL | reuse impl Trait for S { &self.0 } + | ^^^^^ not a function + +error[E0423]: expected function, found associated type `Trait::Type` + --> $DIR/one-line-trait-reuse-non-reuse-items.rs:23:16 + | +LL | reuse impl Trait for S { &self.0 } + | ^^^^^ not a function + +error[E0046]: not all trait items implemented, missing: `CONST`, `Type`, `method` + --> $DIR/one-line-trait-reuse-non-reuse-items.rs:23:5 + | +LL | const CONST: u8; + | --------------- `CONST` from trait +LL | type Type; + | --------- `Type` from trait +LL | #[allow(non_camel_case_types)] +LL | type method; + | ----------- `method` from trait +... +LL | reuse impl Trait for S { &self.0 } + | ^^^^^^^^^^^^^^^^^^^^^^ missing `CONST`, `Type`, `method` in implementation + +error: aborting due to 6 previous errors + +Some errors have detailed explanations: E0046, E0201, E0324, E0423. +For more information about an error, try `rustc --explain E0046`. diff --git a/tests/ui/delegation/one-line-trait-reuse-non-trait-impl.rs b/tests/ui/delegation/one-line-trait-reuse-non-trait-impl.rs new file mode 100644 index 0000000000000..c7a9813250db9 --- /dev/null +++ b/tests/ui/delegation/one-line-trait-reuse-non-trait-impl.rs @@ -0,0 +1,9 @@ +#![allow(incomplete_features)] +#![feature(fn_delegation)] + +struct Trait(usize); + +reuse impl Trait { self.0 } +//~^ ERROR only trait impls can be reused + +fn main() {} diff --git a/tests/ui/delegation/one-line-trait-reuse-non-trait-impl.stderr b/tests/ui/delegation/one-line-trait-reuse-non-trait-impl.stderr new file mode 100644 index 0000000000000..a4a1eba08e2eb --- /dev/null +++ b/tests/ui/delegation/one-line-trait-reuse-non-trait-impl.stderr @@ -0,0 +1,8 @@ +error: only trait impls can be reused + --> $DIR/one-line-trait-reuse-non-trait-impl.rs:6:1 + | +LL | reuse impl Trait { self.0 } + | ^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/delegation/one-line-trait-reuse-pass.rs b/tests/ui/delegation/one-line-trait-reuse-pass.rs new file mode 100644 index 0000000000000..2f1be0c4ea12e --- /dev/null +++ b/tests/ui/delegation/one-line-trait-reuse-pass.rs @@ -0,0 +1,292 @@ +//@ check-pass + +#![allow(incomplete_features)] +#![feature(fn_delegation)] +#![feature(const_trait_impl)] +#![allow(warnings)] + +mod default { + trait T { + fn foo(&self) {} + fn bar(&self) {} + fn goo(&self) {} + } + + struct S; + impl T for S {} + + struct F(S); + reuse impl T for F { self.0 } + + fn f() { + let f = F(S{}); + + f.foo(); + f.bar(); + f.goo(); + } +} + +mod dyn_traits { + trait T { + fn foo(&self) -> Box; + } + + trait SecondTrait { + fn bar(&self); + } + + reuse impl SecondTrait for dyn T { self.foo().as_ref() } +} + +mod complex_path { + pub mod first { + pub mod second { + pub trait T { + fn foo(&self, x: usize); + } + } + } + + struct S; + impl first::second::T for S { + fn foo(&self, x: usize) { } + } + + struct F(S); + reuse impl first::second::T for F { self.0 } + + fn f() { + use complex_path::first::second::T; + + let f = F(S{}); + + f.foo(1); + } +} + +mod no_body_reuse { + trait T { + fn foo(&self) {} + fn bar(&mut self) {} + } + + struct F; + + reuse impl T for F; + + fn foo() { + let mut f = F{}; + + f.foo(); + f.bar(); + } +} + +mod unsafe_trait { + unsafe trait UnsafeTrait { + fn foo(&self) {} + fn bar(&self) {} + fn goo(&self) {} + } + + struct S; + unsafe impl UnsafeTrait for S {} + + struct F(S); + reuse unsafe impl UnsafeTrait for F { self.0 } + + fn f() { + let f = F(S{}); + + f.foo(); + f.bar(); + f.goo(); + } +} + +mod const_trait { + const trait ConstTrait { + fn foo(&self) -> usize { 0 } + fn bar(&self) -> usize { 1 } + } + + struct S; + const impl ConstTrait for S {} + + struct F(S); + reuse const impl ConstTrait for F { self.0 } + + fn f() { + let f = F(S{}); + + f.foo(); + f.bar(); + } +} + +mod different_selves { + trait T: Sized { + fn foo(&self) {} + fn boo(self) {} + fn goo(&mut self) {} + } + + struct S; + impl T for S {} + + struct F(S); + reuse impl T for F { self.0 } + + struct D(S); + macro_rules! self_0 { ($self:ident) => { $self.0 } } + + reuse impl T for D { self_0!(self) } + + fn f() { + let mut f = F(S{}); + f.foo(); + f.goo(); + f.boo(); + + let mut d = D(S{}); + d.foo(); + d.goo(); + d.boo(); + } +} + +mod macros { + trait Trait { + fn foo(&self) -> u8 { 0 } + fn bar(&self) -> u8 { 1 } + } + + impl Trait for u8 {} + struct S(u8); + + macro_rules! self_0_ref { ($self:ident) => { &$self.0 } } + + reuse impl Trait for S { self_0_ref!(self) } + + struct M(u8); + macro_rules! m { () => { M } } + reuse impl Trait for m!() { self_0_ref!(self) } + + struct S1(u8); + macro_rules! one_line_reuse { ($self:ident) => { reuse impl Trait for S1 { $self.0 } } } + one_line_reuse!(self); + + struct S2(u8); + macro_rules! one_line_reuse_expr { ($x:expr) => { reuse impl Trait for S2 { $x } } } + one_line_reuse_expr!(self.0); + + struct S3(u8); + macro_rules! s3 { () => { S3 } } + macro_rules! one_line_reuse_expr2 { ($x:expr) => { reuse impl Trait for s3!() { $x } } } + one_line_reuse_expr2!(self.0); + + fn f() { + let s = S(1); + s.foo(); + s.bar(); + + let m = M(41); + m.foo(); + m.bar(); + + let s1 = S1(2); + s1.foo(); + s1.bar(); + + let s2 = S1(4); + s2.foo(); + s2.bar(); + + let s3 = S1(5); + s3.foo(); + s3.bar(); + } +} + +mod generics { + trait Trait<'a, 'b, A, B, C> { + fn foo(&self, a: &A) {} + fn bar(&self, b: &B) {} + fn goo(&self, c: &C) {} + } + + struct S; + impl<'a, 'b, A, B, C> Trait<'a, 'b, A, B, C> for S {} + + struct F(S); + reuse impl<'a, 'b, A, B, C> Trait<'a, 'b, A, B, C> for F { &self.0 } + + struct S1; + struct F1(S1); + impl<'c, B> Trait<'static, 'c, usize, B, String> for S1 {} + reuse impl<'d, B> Trait<'static, 'd, usize, B, String> for F1 { &self.0 } + + struct S2; + struct F2(S2); + impl Trait<'static, 'static, u8, u16, u32> for S2 {} + reuse impl Trait<'static, 'static, u8, u16, u32> for F2 { &self.0 } + + fn f<'a, 'b, 'c, A, B, C>(a: A, b: B, c: C) { + let f = F(S{}); + + >::foo(&f, &a); + >::bar(&f, &b); + >::goo(&f, &c); + + let f = F1(S1{}); + >::foo(&f, &123); + >::bar(&f, &b); + >::goo(&f, &"s".to_string()); + + let f = F2(S2{}); + >::foo(&f, &1); + >::bar(&f, &2); + >::goo(&f, &3); + } +} + +mod reuse_in_different_places { + trait T { + fn foo(&self, x: usize) {} + } + + struct S; + impl T for S {} + + struct F1(S); + reuse impl T for F1 { + struct F2(S, S, S); + reuse impl T for F2 { self.1 } + + let f2 = F2(S{}, S{}, S{}); + f2.foo(123); + + &self.0 + } + + fn foo() { + struct F(S); + reuse impl T for F { self.0 } + + let f = F(S{}); + f.foo(1); + } + + fn bar() { + || { + struct F(S); + reuse impl T for F { self.0 } + + let f = F(S{}); + f.foo(1); + }; + } +} + +fn main() {} From fc3f2795a1d34734990062cce938218a0153f8b1 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 19 Dec 2025 16:25:11 +0300 Subject: [PATCH 2/5] Fix using wrong structs in tests --- tests/ui/delegation/one-line-trait-reuse-pass.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/delegation/one-line-trait-reuse-pass.rs b/tests/ui/delegation/one-line-trait-reuse-pass.rs index 2f1be0c4ea12e..90060b03f9efd 100644 --- a/tests/ui/delegation/one-line-trait-reuse-pass.rs +++ b/tests/ui/delegation/one-line-trait-reuse-pass.rs @@ -199,11 +199,11 @@ mod macros { s1.foo(); s1.bar(); - let s2 = S1(4); + let s2 = S2(4); s2.foo(); s2.bar(); - let s3 = S1(5); + let s3 = S3(5); s3.foo(); s3.bar(); } From 591b2d201d94541ed10cd98bb5632c0444949a97 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 19 Dec 2025 17:44:10 +0300 Subject: [PATCH 3/5] Addressing review comments --- compiler/rustc_parse/src/parser/item.rs | 10 ++++---- ...use-bad-path.rs => impl-reuse-bad-path.rs} | 0 ...path.stderr => impl-reuse-bad-path.stderr} | 16 ++++++------- ...empty-glob.rs => impl-reuse-empty-glob.rs} | 0 ...ob.stderr => impl-reuse-empty-glob.stderr} | 2 +- ...places.rs => impl-reuse-illegal-places.rs} | 0 ...tderr => impl-reuse-illegal-places.stderr} | 24 +++++++++---------- ...raits.rs => impl-reuse-negative-traits.rs} | 0 ...derr => impl-reuse-negative-traits.stderr} | 2 +- ...items.rs => impl-reuse-non-reuse-items.rs} | 0 ...derr => impl-reuse-non-reuse-items.stderr} | 12 +++++----- ...t-impl.rs => impl-reuse-non-trait-impl.rs} | 0 ...tderr => impl-reuse-non-trait-impl.stderr} | 2 +- ...trait-reuse-pass.rs => impl-reuse-pass.rs} | 0 14 files changed, 34 insertions(+), 34 deletions(-) rename tests/ui/delegation/{one-line-trait-reuse-bad-path.rs => impl-reuse-bad-path.rs} (100%) rename tests/ui/delegation/{one-line-trait-reuse-bad-path.stderr => impl-reuse-bad-path.stderr} (78%) rename tests/ui/delegation/{one-line-trait-reuse-empty-glob.rs => impl-reuse-empty-glob.rs} (100%) rename tests/ui/delegation/{one-line-trait-reuse-empty-glob.stderr => impl-reuse-empty-glob.stderr} (77%) rename tests/ui/delegation/{one-line-trait-reuse-illegal-places.rs => impl-reuse-illegal-places.rs} (100%) rename tests/ui/delegation/{one-line-trait-reuse-illegal-places.stderr => impl-reuse-illegal-places.stderr} (78%) rename tests/ui/delegation/{one-line-trait-reuse-negative-traits.rs => impl-reuse-negative-traits.rs} (100%) rename tests/ui/delegation/{one-line-trait-reuse-negative-traits.stderr => impl-reuse-negative-traits.stderr} (75%) rename tests/ui/delegation/{one-line-trait-reuse-non-reuse-items.rs => impl-reuse-non-reuse-items.rs} (100%) rename tests/ui/delegation/{one-line-trait-reuse-non-reuse-items.stderr => impl-reuse-non-reuse-items.stderr} (84%) rename tests/ui/delegation/{one-line-trait-reuse-non-trait-impl.rs => impl-reuse-non-trait-impl.rs} (100%) rename tests/ui/delegation/{one-line-trait-reuse-non-trait-impl.stderr => impl-reuse-non-trait-impl.stderr} (72%) rename tests/ui/delegation/{one-line-trait-reuse-pass.rs => impl-reuse-pass.rs} (100%) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 41d59c4f69ef1..054c8ad087204 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -119,7 +119,7 @@ impl<'a> Parser<'a> { enum ReuseKind { Path, - OneLineTraitReuse, + Impl, } impl<'a> Parser<'a> { @@ -388,7 +388,7 @@ impl<'a> Parser<'a> { if self.look_ahead(LOOK_AHEAD_DIST, |t| t.is_path_start() && *t != token::PathSep) { Some(ReuseKind::Path) } else if self.look_ahead_check_impl_frontmatter(LOOK_AHEAD_DIST) { - Some(ReuseKind::OneLineTraitReuse) + Some(ReuseKind::Impl) } else { None } @@ -577,7 +577,7 @@ impl<'a> Parser<'a> { &mut self, attrs: &mut AttrVec, defaultness: Defaultness, - is_one_line_trait_reuse: bool, + is_reuse: bool, ) -> PResult<'a, ItemKind> { let mut constness = self.parse_constness(Case::Sensitive); let safety = self.parse_safety(Case::Sensitive); @@ -646,7 +646,7 @@ impl<'a> Parser<'a> { generics.where_clause = self.parse_where_clause()?; - let impl_items = if is_one_line_trait_reuse { + let impl_items = if is_reuse { Default::default() } else { self.parse_item_list(attrs, |p| p.parse_impl_item(ForceCollect::No))? @@ -732,7 +732,7 @@ impl<'a> Parser<'a> { let item_kind = match kind { ReuseKind::Path => self.parse_path_like_delegation(), - ReuseKind::OneLineTraitReuse => { + ReuseKind::Impl => { self.parse_one_line_trait_delegation(span, attrs, defaultness) } }?; diff --git a/tests/ui/delegation/one-line-trait-reuse-bad-path.rs b/tests/ui/delegation/impl-reuse-bad-path.rs similarity index 100% rename from tests/ui/delegation/one-line-trait-reuse-bad-path.rs rename to tests/ui/delegation/impl-reuse-bad-path.rs diff --git a/tests/ui/delegation/one-line-trait-reuse-bad-path.stderr b/tests/ui/delegation/impl-reuse-bad-path.stderr similarity index 78% rename from tests/ui/delegation/one-line-trait-reuse-bad-path.stderr rename to tests/ui/delegation/impl-reuse-bad-path.stderr index 45fc26c44742c..5fadd719ae4da 100644 --- a/tests/ui/delegation/one-line-trait-reuse-bad-path.stderr +++ b/tests/ui/delegation/impl-reuse-bad-path.stderr @@ -1,47 +1,47 @@ error: empty glob delegation is not supported - --> $DIR/one-line-trait-reuse-bad-path.rs:11:5 + --> $DIR/impl-reuse-bad-path.rs:11:5 | LL | reuse impl T for unresolved { self.0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: expected trait, found struct `Trait` - --> $DIR/one-line-trait-reuse-bad-path.rs:21:16 + --> $DIR/impl-reuse-bad-path.rs:21:16 | LL | reuse impl Trait for S { self.0 } | ^^^^^ not a trait error: expected trait, found module `TraitModule` - --> $DIR/one-line-trait-reuse-bad-path.rs:26:16 + --> $DIR/impl-reuse-bad-path.rs:26:16 | LL | reuse impl TraitModule for S { self.0 } | ^^^^^^^^^^^ not a trait error[E0433]: failed to resolve: use of unresolved module or unlinked crate `unresolved` - --> $DIR/one-line-trait-reuse-bad-path.rs:6:16 + --> $DIR/impl-reuse-bad-path.rs:6:16 | LL | reuse impl unresolved for S { self.0 } | ^^^^^^^^^^ use of unresolved module or unlinked crate `unresolved` error[E0405]: cannot find trait `unresolved` in this scope - --> $DIR/one-line-trait-reuse-bad-path.rs:6:16 + --> $DIR/impl-reuse-bad-path.rs:6:16 | LL | reuse impl unresolved for S { self.0 } | ^^^^^^^^^^ not found in this scope error[E0425]: cannot find type `unresolved` in this scope - --> $DIR/one-line-trait-reuse-bad-path.rs:11:22 + --> $DIR/impl-reuse-bad-path.rs:11:22 | LL | reuse impl T for unresolved { self.0 } | ^^^^^^^^^^ not found in this scope error[E0404]: expected trait, found struct `Trait` - --> $DIR/one-line-trait-reuse-bad-path.rs:21:16 + --> $DIR/impl-reuse-bad-path.rs:21:16 | LL | reuse impl Trait for S { self.0 } | ^^^^^ not a trait error[E0404]: expected trait, found module `TraitModule` - --> $DIR/one-line-trait-reuse-bad-path.rs:26:16 + --> $DIR/impl-reuse-bad-path.rs:26:16 | LL | reuse impl TraitModule for S { self.0 } | ^^^^^^^^^^^ not a trait diff --git a/tests/ui/delegation/one-line-trait-reuse-empty-glob.rs b/tests/ui/delegation/impl-reuse-empty-glob.rs similarity index 100% rename from tests/ui/delegation/one-line-trait-reuse-empty-glob.rs rename to tests/ui/delegation/impl-reuse-empty-glob.rs diff --git a/tests/ui/delegation/one-line-trait-reuse-empty-glob.stderr b/tests/ui/delegation/impl-reuse-empty-glob.stderr similarity index 77% rename from tests/ui/delegation/one-line-trait-reuse-empty-glob.stderr rename to tests/ui/delegation/impl-reuse-empty-glob.stderr index 36272feb9a58e..bf6bb58763519 100644 --- a/tests/ui/delegation/one-line-trait-reuse-empty-glob.stderr +++ b/tests/ui/delegation/impl-reuse-empty-glob.stderr @@ -1,5 +1,5 @@ error: empty glob delegation is not supported - --> $DIR/one-line-trait-reuse-empty-glob.rs:9:5 + --> $DIR/impl-reuse-empty-glob.rs:9:5 | LL | reuse impl T for S { self.0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/delegation/one-line-trait-reuse-illegal-places.rs b/tests/ui/delegation/impl-reuse-illegal-places.rs similarity index 100% rename from tests/ui/delegation/one-line-trait-reuse-illegal-places.rs rename to tests/ui/delegation/impl-reuse-illegal-places.rs diff --git a/tests/ui/delegation/one-line-trait-reuse-illegal-places.stderr b/tests/ui/delegation/impl-reuse-illegal-places.stderr similarity index 78% rename from tests/ui/delegation/one-line-trait-reuse-illegal-places.stderr rename to tests/ui/delegation/impl-reuse-illegal-places.stderr index ceb94ac4e8fd1..fb17ebdd6cee1 100644 --- a/tests/ui/delegation/one-line-trait-reuse-illegal-places.stderr +++ b/tests/ui/delegation/impl-reuse-illegal-places.stderr @@ -1,5 +1,5 @@ error: expected `:`, found keyword `impl` - --> $DIR/one-line-trait-reuse-illegal-places.rs:14:11 + --> $DIR/impl-reuse-illegal-places.rs:14:11 | LL | struct X { | - while parsing this struct @@ -7,7 +7,7 @@ LL | reuse impl T for F { self.0 } | ^^^^ expected `:` error: implementation is not supported in `trait`s or `impl`s - --> $DIR/one-line-trait-reuse-illegal-places.rs:19:5 + --> $DIR/impl-reuse-illegal-places.rs:19:5 | LL | reuse impl T for F { self.0 } | ^^^^^^^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | reuse impl T for F { self.0 } = help: consider moving the implementation out to a nearby module scope error: implementation is not supported in `trait`s or `impl`s - --> $DIR/one-line-trait-reuse-illegal-places.rs:24:5 + --> $DIR/impl-reuse-illegal-places.rs:24:5 | LL | reuse impl T for F { self.0 } | ^^^^^^^^^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | reuse impl T for F { self.0 } = help: consider moving the implementation out to a nearby module scope error: implementation is not supported in `extern` blocks - --> $DIR/one-line-trait-reuse-illegal-places.rs:29:5 + --> $DIR/impl-reuse-illegal-places.rs:29:5 | LL | reuse impl T for F { self.0 } | ^^^^^^^^^^^^^^^^^^ @@ -31,13 +31,13 @@ LL | reuse impl T for F { self.0 } = help: consider moving the implementation out to a nearby module scope error: expected identifier, found keyword `impl` - --> $DIR/one-line-trait-reuse-illegal-places.rs:39:15 + --> $DIR/impl-reuse-illegal-places.rs:39:15 | LL | reuse impl T for F { self.0 } | ^^^^ expected identifier, found keyword error: expected one of `,`, `as`, or `}`, found keyword `impl` - --> $DIR/one-line-trait-reuse-illegal-places.rs:39:15 + --> $DIR/impl-reuse-illegal-places.rs:39:15 | LL | reuse impl T for F { self.0 } | -^^^^ expected one of `,`, `as`, or `}` @@ -45,7 +45,7 @@ LL | reuse impl T for F { self.0 } | help: missing `,` error: expected one of `,`, `as`, or `}`, found `T` - --> $DIR/one-line-trait-reuse-illegal-places.rs:39:20 + --> $DIR/impl-reuse-illegal-places.rs:39:20 | LL | reuse impl T for F { self.0 } | -^ expected one of `,`, `as`, or `}` @@ -53,13 +53,13 @@ LL | reuse impl T for F { self.0 } | help: missing `,` error: expected identifier, found keyword `for` - --> $DIR/one-line-trait-reuse-illegal-places.rs:39:22 + --> $DIR/impl-reuse-illegal-places.rs:39:22 | LL | reuse impl T for F { self.0 } | ^^^ expected identifier, found keyword error: expected one of `,`, `as`, or `}`, found keyword `for` - --> $DIR/one-line-trait-reuse-illegal-places.rs:39:22 + --> $DIR/impl-reuse-illegal-places.rs:39:22 | LL | reuse impl T for F { self.0 } | -^^^ expected one of `,`, `as`, or `}` @@ -67,7 +67,7 @@ LL | reuse impl T for F { self.0 } | help: missing `,` error: expected one of `,`, `as`, or `}`, found `F` - --> $DIR/one-line-trait-reuse-illegal-places.rs:39:26 + --> $DIR/impl-reuse-illegal-places.rs:39:26 | LL | reuse impl T for F { self.0 } | -^ expected one of `,`, `as`, or `}` @@ -75,13 +75,13 @@ LL | reuse impl T for F { self.0 } | help: missing `,` error: expected one of `,`, `as`, or `}`, found `{` - --> $DIR/one-line-trait-reuse-illegal-places.rs:39:28 + --> $DIR/impl-reuse-illegal-places.rs:39:28 | LL | reuse impl T for F { self.0 } | ^ expected one of `,`, `as`, or `}` error: expected item, found `}` - --> $DIR/one-line-trait-reuse-illegal-places.rs:48:1 + --> $DIR/impl-reuse-illegal-places.rs:48:1 | LL | } | ^ expected item diff --git a/tests/ui/delegation/one-line-trait-reuse-negative-traits.rs b/tests/ui/delegation/impl-reuse-negative-traits.rs similarity index 100% rename from tests/ui/delegation/one-line-trait-reuse-negative-traits.rs rename to tests/ui/delegation/impl-reuse-negative-traits.rs diff --git a/tests/ui/delegation/one-line-trait-reuse-negative-traits.stderr b/tests/ui/delegation/impl-reuse-negative-traits.stderr similarity index 75% rename from tests/ui/delegation/one-line-trait-reuse-negative-traits.stderr rename to tests/ui/delegation/impl-reuse-negative-traits.stderr index e2aedca2c5995..220704066e45e 100644 --- a/tests/ui/delegation/one-line-trait-reuse-negative-traits.stderr +++ b/tests/ui/delegation/impl-reuse-negative-traits.stderr @@ -1,5 +1,5 @@ error: reuse of negative trait impls is not allowed - --> $DIR/one-line-trait-reuse-negative-traits.rs:16:1 + --> $DIR/impl-reuse-negative-traits.rs:16:1 | LL | reuse impl !Trait for F { &self.0 } | ^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/delegation/one-line-trait-reuse-non-reuse-items.rs b/tests/ui/delegation/impl-reuse-non-reuse-items.rs similarity index 100% rename from tests/ui/delegation/one-line-trait-reuse-non-reuse-items.rs rename to tests/ui/delegation/impl-reuse-non-reuse-items.rs diff --git a/tests/ui/delegation/one-line-trait-reuse-non-reuse-items.stderr b/tests/ui/delegation/impl-reuse-non-reuse-items.stderr similarity index 84% rename from tests/ui/delegation/one-line-trait-reuse-non-reuse-items.stderr rename to tests/ui/delegation/impl-reuse-non-reuse-items.stderr index 3e4d6a1f40815..9d6b0f6381367 100644 --- a/tests/ui/delegation/one-line-trait-reuse-non-reuse-items.stderr +++ b/tests/ui/delegation/impl-reuse-non-reuse-items.stderr @@ -1,5 +1,5 @@ error[E0324]: item `CONST` is an associated method, which doesn't match its trait `Trait` - --> $DIR/one-line-trait-reuse-non-reuse-items.rs:23:5 + --> $DIR/impl-reuse-non-reuse-items.rs:23:5 | LL | const CONST: u8; | ---------------- item in trait @@ -8,7 +8,7 @@ LL | reuse impl Trait for S { &self.0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait error[E0324]: item `Type` is an associated method, which doesn't match its trait `Trait` - --> $DIR/one-line-trait-reuse-non-reuse-items.rs:23:5 + --> $DIR/impl-reuse-non-reuse-items.rs:23:5 | LL | type Type; | ---------- item in trait @@ -17,7 +17,7 @@ LL | reuse impl Trait for S { &self.0 } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not match trait error[E0201]: duplicate definitions with name `method`: - --> $DIR/one-line-trait-reuse-non-reuse-items.rs:23:5 + --> $DIR/impl-reuse-non-reuse-items.rs:23:5 | LL | fn method(&self); | ----------------- item in trait @@ -29,19 +29,19 @@ LL | reuse impl Trait for S { &self.0 } | previous definition here error[E0423]: expected function, found associated constant `Trait::CONST` - --> $DIR/one-line-trait-reuse-non-reuse-items.rs:23:16 + --> $DIR/impl-reuse-non-reuse-items.rs:23:16 | LL | reuse impl Trait for S { &self.0 } | ^^^^^ not a function error[E0423]: expected function, found associated type `Trait::Type` - --> $DIR/one-line-trait-reuse-non-reuse-items.rs:23:16 + --> $DIR/impl-reuse-non-reuse-items.rs:23:16 | LL | reuse impl Trait for S { &self.0 } | ^^^^^ not a function error[E0046]: not all trait items implemented, missing: `CONST`, `Type`, `method` - --> $DIR/one-line-trait-reuse-non-reuse-items.rs:23:5 + --> $DIR/impl-reuse-non-reuse-items.rs:23:5 | LL | const CONST: u8; | --------------- `CONST` from trait diff --git a/tests/ui/delegation/one-line-trait-reuse-non-trait-impl.rs b/tests/ui/delegation/impl-reuse-non-trait-impl.rs similarity index 100% rename from tests/ui/delegation/one-line-trait-reuse-non-trait-impl.rs rename to tests/ui/delegation/impl-reuse-non-trait-impl.rs diff --git a/tests/ui/delegation/one-line-trait-reuse-non-trait-impl.stderr b/tests/ui/delegation/impl-reuse-non-trait-impl.stderr similarity index 72% rename from tests/ui/delegation/one-line-trait-reuse-non-trait-impl.stderr rename to tests/ui/delegation/impl-reuse-non-trait-impl.stderr index a4a1eba08e2eb..3987042104c38 100644 --- a/tests/ui/delegation/one-line-trait-reuse-non-trait-impl.stderr +++ b/tests/ui/delegation/impl-reuse-non-trait-impl.stderr @@ -1,5 +1,5 @@ error: only trait impls can be reused - --> $DIR/one-line-trait-reuse-non-trait-impl.rs:6:1 + --> $DIR/impl-reuse-non-trait-impl.rs:6:1 | LL | reuse impl Trait { self.0 } | ^^^^^^^^^^^^^^^^ diff --git a/tests/ui/delegation/one-line-trait-reuse-pass.rs b/tests/ui/delegation/impl-reuse-pass.rs similarity index 100% rename from tests/ui/delegation/one-line-trait-reuse-pass.rs rename to tests/ui/delegation/impl-reuse-pass.rs From d27d133329cae42da23a5b4927758f486583ab36 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 19 Dec 2025 17:53:38 +0300 Subject: [PATCH 4/5] Addressing review comments --- compiler/rustc_parse/messages.ftl | 1 - compiler/rustc_parse/src/errors.rs | 9 +-------- compiler/rustc_parse/src/parser/item.rs | 12 +++--------- tests/ui/delegation/impl-reuse-negative-traits.rs | 4 ++-- .../ui/delegation/impl-reuse-negative-traits.stderr | 9 +++++---- 5 files changed, 11 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 26b0e1e92df52..747895c804697 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -163,7 +163,6 @@ parse_default_not_followed_by_item = `default` is not followed by an item .note = only `fn`, `const`, `type`, or `impl` items may be prefixed by `default` parse_delegation_non_trait_impl_reuse = only trait impls can be reused -parse_delegation_reuse_negative_trait_impls = reuse of negative trait impls is not allowed parse_do_catch_syntax_removed = found removed `do catch` syntax .note = following RFC #2388, the new non-placeholder syntax is `try` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 47c355986def7..3b72c9802afd3 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -3674,14 +3674,7 @@ pub(crate) struct VarargsWithoutPattern { #[derive(Diagnostic)] #[diag(parse_delegation_non_trait_impl_reuse)] -pub(crate) struct OneLineReuseAllowedOnlyForTraitsImpls { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(parse_delegation_reuse_negative_trait_impls)] -pub(crate) struct OneLineReuseOfNegativeTraitImplsNotAllowed { +pub(crate) struct ImplReuseInherentImpl { #[primary_span] pub span: Span, } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 054c8ad087204..d4f3c958000ee 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -733,7 +733,7 @@ impl<'a> Parser<'a> { let item_kind = match kind { ReuseKind::Path => self.parse_path_like_delegation(), ReuseKind::Impl => { - self.parse_one_line_trait_delegation(span, attrs, defaultness) + self.parse_impl_delegation(span, attrs, defaultness) } }?; @@ -751,7 +751,7 @@ impl<'a> Parser<'a> { }) } - fn parse_one_line_trait_delegation( + fn parse_impl_delegation( &mut self, span: Span, attrs: &mut AttrVec, @@ -763,17 +763,11 @@ impl<'a> Parser<'a> { let until_expr_span = span.to(self.prev_token.span); let Some(of_trait) = of_trait else { - return Err(self.dcx().create_err(errors::OneLineReuseAllowedOnlyForTraitsImpls { + return Err(self.dcx().create_err(errors::ImplReuseInherentImpl { span: until_expr_span, })); }; - if matches!(of_trait.polarity, ImplPolarity::Negative(..)) { - return Err(self.dcx().create_err( - errors::OneLineReuseOfNegativeTraitImplsNotAllowed { span: until_expr_span }, - )); - } - let body = self.parse_delegation_body()?; let whole_reuse_span = span.to(self.prev_token.span); diff --git a/tests/ui/delegation/impl-reuse-negative-traits.rs b/tests/ui/delegation/impl-reuse-negative-traits.rs index a375105e66622..7bcbc82f03db8 100644 --- a/tests/ui/delegation/impl-reuse-negative-traits.rs +++ b/tests/ui/delegation/impl-reuse-negative-traits.rs @@ -1,9 +1,10 @@ #![allow(incomplete_features)] #![feature(fn_delegation)] - +#![feature(negative_impls)] trait Trait { fn foo(&self); + //~^ ERROR negative impls cannot have any items [E0749] } struct S; @@ -14,6 +15,5 @@ impl Trait for S { struct F(S); reuse impl !Trait for F { &self.0 } -//~^ ERROR reuse of negative trait impls is not allowed fn main() {} diff --git a/tests/ui/delegation/impl-reuse-negative-traits.stderr b/tests/ui/delegation/impl-reuse-negative-traits.stderr index 220704066e45e..1be6ef715920d 100644 --- a/tests/ui/delegation/impl-reuse-negative-traits.stderr +++ b/tests/ui/delegation/impl-reuse-negative-traits.stderr @@ -1,8 +1,9 @@ -error: reuse of negative trait impls is not allowed - --> $DIR/impl-reuse-negative-traits.rs:16:1 +error[E0749]: negative impls cannot have any items + --> $DIR/impl-reuse-negative-traits.rs:6:8 | -LL | reuse impl !Trait for F { &self.0 } - | ^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn foo(&self); + | ^^^ error: aborting due to 1 previous error +For more information about this error, try `rustc --explain E0749`. From 3f8191181e84b8474510e2ac98e51b9dbebef8f0 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 19 Dec 2025 18:00:06 +0300 Subject: [PATCH 5/5] Addressing review comments --- compiler/rustc_parse/src/parser/item.rs | 34 +++++++++---------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index d4f3c958000ee..8c560d78c4a4c 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -254,7 +254,7 @@ impl<'a> Parser<'a> { } else if self.check_keyword_case(exp!(Trait), case) || self.check_trait_front_matter() { // TRAIT ITEM self.parse_item_trait(attrs, lo)? - } else if self.check_impl_frontmatter_with_diagnostics() { + } else if self.check_impl_frontmatter(0) { // IMPL ITEM self.parse_item_impl(attrs, def_(), false)? } else if let Const::Yes(const_span) = self.parse_constness(case) { @@ -378,16 +378,14 @@ impl<'a> Parser<'a> { || matches!(self.is_macro_rules_item(), IsMacroRulesItem::Yes{..}) // no: `macro_rules::b`, yes: `macro_rules! mac` } - fn is_reuse_item(&self) -> Option { + fn is_reuse_item(&mut self) -> Option { self.token .is_keyword(kw::Reuse) .then(|| { - const LOOK_AHEAD_DIST: usize = 1; - // no: `reuse ::path` for compatibility reasons with macro invocations - if self.look_ahead(LOOK_AHEAD_DIST, |t| t.is_path_start() && *t != token::PathSep) { + if self.look_ahead(1, |t| t.is_path_start() && *t != token::PathSep) { Some(ReuseKind::Path) - } else if self.look_ahead_check_impl_frontmatter(LOOK_AHEAD_DIST) { + } else if self.check_impl_frontmatter(1) { Some(ReuseKind::Impl) } else { None @@ -732,9 +730,7 @@ impl<'a> Parser<'a> { let item_kind = match kind { ReuseKind::Path => self.parse_path_like_delegation(), - ReuseKind::Impl => { - self.parse_impl_delegation(span, attrs, defaultness) - } + ReuseKind::Impl => self.parse_impl_delegation(span, attrs, defaultness), }?; self.psess.gated_spans.gate(sym::fn_delegation, span.to(self.prev_token.span)); @@ -763,9 +759,9 @@ impl<'a> Parser<'a> { let until_expr_span = span.to(self.prev_token.span); let Some(of_trait) = of_trait else { - return Err(self.dcx().create_err(errors::ImplReuseInherentImpl { - span: until_expr_span, - })); + return Err(self + .dcx() + .create_err(errors::ImplReuseInherentImpl { span: until_expr_span })); }; let body = self.parse_delegation_body()?; @@ -2676,22 +2672,16 @@ impl<'a> Parser<'a> { Ok(body) } - fn check_impl_frontmatter_with_diagnostics(&mut self) -> bool { - // In contrast to the method below, this call inserts `impl` into the + fn check_impl_frontmatter(&mut self, look_ahead: usize) -> bool { + const ALL_QUALS: &[Symbol] = &[kw::Const, kw::Unsafe]; + // In contrast to the loop below, this call inserts `impl` into the // list of expected tokens shown in diagnostics. if self.check_keyword(exp!(Impl)) { return true; } - - self.look_ahead_check_impl_frontmatter(0) - } - - fn look_ahead_check_impl_frontmatter(&self, look_ahead_dist: usize) -> bool { - const ALL_QUALS: &[Symbol] = &[kw::Const, kw::Unsafe]; - let mut i = 0; while i < ALL_QUALS.len() { - let action = self.look_ahead(i + look_ahead_dist, |token| { + let action = self.look_ahead(i + look_ahead, |token| { if token.is_keyword(kw::Impl) { return Some(true); }