diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index 593eac2df4893..3a5b6ad608aa2 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -508,7 +508,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { // FIXME(fn_delegation): proper support for parent generics propagation // in method call scenario. - let segment = self.process_segment(span, &segment, &mut generics.child, false); + let segment = self.process_segment(span, &segment, &mut generics.child); let segment = self.arena.alloc(segment); self.arena.alloc(hir::Expr { @@ -534,14 +534,10 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { new_path.segments = self.arena.alloc_from_iter( new_path.segments.iter().enumerate().map(|(idx, segment)| { - let mut process_segment = |result, add_lifetimes| { - self.process_segment(span, segment, result, add_lifetimes) - }; - if idx + 2 == len { - process_segment(&mut generics.parent, true) + self.process_segment(span, segment, &mut generics.parent) } else if idx + 1 == len { - process_segment(&mut generics.child, false) + self.process_segment(span, segment, &mut generics.child) } else { segment.clone() } @@ -551,7 +547,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { hir::QPath::Resolved(ty, self.arena.alloc(new_path)) } hir::QPath::TypeRelative(ty, segment) => { - let segment = self.process_segment(span, segment, &mut generics.child, false); + let segment = self.process_segment(span, segment, &mut generics.child); hir::QPath::TypeRelative(ty, self.arena.alloc(segment)) } @@ -584,13 +580,12 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { span: Span, segment: &hir::PathSegment<'hir>, result: &mut GenericsGenerationResult<'hir>, - add_lifetimes: bool, ) -> hir::PathSegment<'hir> { let details = result.generics.args_propagation_details(); let segment = if details.should_propagate { let generics = result.generics.into_hir_generics(self, span); - let args = generics.into_generic_args(self, add_lifetimes, span); + let args = generics.into_generic_args(self, span); // Needed for better error messages (`trait-impl-wrong-args-count.rs` test). let args = if args.is_empty() { None } else { Some(args) }; diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index 201f6bfb4bd63..503877cff9785 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -6,7 +6,7 @@ use rustc_hir::def_id::DefId; use rustc_middle::ty::GenericParamDefKind; use rustc_middle::{bug, ty}; use rustc_span::symbol::kw; -use rustc_span::{Ident, Span}; +use rustc_span::{Ident, Span, sym}; use crate::{LoweringContext, ResolverAstLoweringExt}; @@ -25,22 +25,37 @@ pub(super) enum DelegationGenericsKind { TraitImpl(bool /* Has user-specified args */), } +#[derive(Debug, Clone, Copy)] +pub(super) enum GenericsPosition { + Parent, + Child, +} + pub(super) struct DelegationGenerics { generics: T, kind: DelegationGenericsKind, + pos: GenericsPosition, } impl<'hir> DelegationGenerics<&'hir [ty::GenericParamDef]> { - fn default(generics: &'hir [ty::GenericParamDef]) -> Self { - DelegationGenerics { generics, kind: DelegationGenericsKind::Default } + fn default(generics: &'hir [ty::GenericParamDef], pos: GenericsPosition) -> Self { + DelegationGenerics { generics, pos, kind: DelegationGenericsKind::Default } } - fn user_specified(generics: &'hir [ty::GenericParamDef]) -> Self { - DelegationGenerics { generics, kind: DelegationGenericsKind::UserSpecified } + fn user_specified(generics: &'hir [ty::GenericParamDef], pos: GenericsPosition) -> Self { + DelegationGenerics { generics, pos, kind: DelegationGenericsKind::UserSpecified } } - fn trait_impl(generics: &'hir [ty::GenericParamDef], user_specified: bool) -> Self { - DelegationGenerics { generics, kind: DelegationGenericsKind::TraitImpl(user_specified) } + fn trait_impl( + generics: &'hir [ty::GenericParamDef], + user_specified: bool, + pos: GenericsPosition, + ) -> Self { + DelegationGenerics { + generics, + pos, + kind: DelegationGenericsKind::TraitImpl(user_specified), + } } } @@ -103,8 +118,14 @@ impl<'hir> HirOrTyGenerics<'hir> { span: Span, ) -> &mut HirOrTyGenerics<'hir> { if let HirOrTyGenerics::Ty(ty) = self { - let params = ctx.uplift_delegation_generic_params(span, ty.generics); - *self = HirOrTyGenerics::Hir(DelegationGenerics { generics: params, kind: ty.kind }); + let rename_self = matches!(ty.pos, GenericsPosition::Child); + let params = ctx.uplift_delegation_generic_params(span, ty.generics, rename_self); + + *self = HirOrTyGenerics::Hir(DelegationGenerics { + generics: params, + kind: ty.kind, + pos: ty.pos, + }); } self @@ -120,7 +141,6 @@ impl<'hir> HirOrTyGenerics<'hir> { pub(super) fn into_generic_args( &self, ctx: &mut LoweringContext<'_, 'hir, impl ResolverAstLoweringExt<'hir>>, - add_lifetimes: bool, span: Span, ) -> &'hir hir::GenericArgs<'hir> { match self { @@ -128,6 +148,7 @@ impl<'hir> HirOrTyGenerics<'hir> { bug!("Attempting to get generic args before uplifting to HIR") } HirOrTyGenerics::Hir(hir) => { + let add_lifetimes = matches!(hir.pos, GenericsPosition::Parent); ctx.create_generics_args_from_params(hir.generics.params, add_lifetimes, span) } } @@ -227,10 +248,15 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { if matches!(delegation_parent_kind, DefKind::Impl { of_trait: true }) { // Considering parent generics, during signature inheritance // we will take those args that are in trait impl header trait ref. - let parent = DelegationGenerics::trait_impl(&[], true); + let parent = DelegationGenerics::trait_impl(&[], true, GenericsPosition::Parent); let parent = GenericsGenerationResult::new(parent); - let child = DelegationGenerics::trait_impl(sig_params, child_user_specified); + let child = DelegationGenerics::trait_impl( + sig_params, + child_user_specified, + GenericsPosition::Child, + ); + let child = GenericsGenerationResult::new(child); return GenericsGenerationResults { @@ -263,25 +289,32 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { DelegationGenerics { kind: DelegationGenericsKind::SelfAndUserSpecified, generics: &sig_parent_params[..1], + pos: GenericsPosition::Parent, } } else { - DelegationGenerics::user_specified(&[]) + DelegationGenerics::user_specified(&[], GenericsPosition::Parent) } } else { let skip_self = usize::from(!generate_self); - DelegationGenerics::default(&sig_parent_params[skip_self..]) + DelegationGenerics::default( + &sig_parent_params[skip_self..], + GenericsPosition::Parent, + ) } } else { - DelegationGenerics::default(&[]) + DelegationGenerics::default(&[], GenericsPosition::Parent) }; let child_generics = if child_user_specified { let synth_params_index = sig_params.iter().position(|p| p.kind.is_synthetic()).unwrap_or(sig_params.len()); - DelegationGenerics::user_specified(&sig_params[synth_params_index..]) + DelegationGenerics::user_specified( + &sig_params[synth_params_index..], + GenericsPosition::Child, + ) } else { - DelegationGenerics::default(sig_params) + DelegationGenerics::default(sig_params, GenericsPosition::Child) }; GenericsGenerationResults { @@ -296,6 +329,7 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { &mut self, span: Span, params: &'hir [ty::GenericParamDef], + rename_self: bool, ) -> &'hir hir::Generics<'hir> { let params = self.arena.alloc_from_iter(params.iter().map(|p| { let def_kind = match p.kind { @@ -304,7 +338,20 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> { GenericParamDefKind::Const { .. } => DefKind::ConstParam, }; - let param_ident = Ident::new(p.name, span); + // Rename Self generic param to This so it is properly propagated. + // If the user will create a function `fn foo() {}` with generic + // param "Self" then it will not be generated in HIR, the same thing + // applies to traits, `trait Trait {}` will be represented as + // `trait Trait {}` in HIR and "unexpected keyword `Self` in generic parameters" + // error will be emitted. + // Note that we do not rename `Self` to `This` after non-recursive reuse + // from Trait, in this case the `Self` should not be propagated + // (we rely that implicit `Self` generic param of a trait is named "Self") + // and it is OK to have Self generic param generated during lowering. + let param_name = + if rename_self && p.name == kw::SelfUpper { sym::This } else { p.name }; + + let param_ident = Ident::new(param_name, span); let def_name = Some(param_ident.name); let node_id = self.next_node_id(); diff --git a/tests/pretty/delegation-self-rename.pp b/tests/pretty/delegation-self-rename.pp new file mode 100644 index 0000000000000..59a07315185c7 --- /dev/null +++ b/tests/pretty/delegation-self-rename.pp @@ -0,0 +1,55 @@ +#![attr = Feature([fn_delegation#0])] +extern crate std; +#[attr = PreludeImport] +use ::std::prelude::rust_2015::*; +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:delegation-self-rename.pp + + +trait Trait<'a, A, const B: bool> { + fn foo<'b, const B2: bool, T, U, + impl FnOnce() -> usize>(&self, f: impl FnOnce() -> usize) -> usize + where impl FnOnce() -> usize: FnOnce() -> usize { f() + 1 } +} + +struct X; +impl <'a, A, const B: bool> Trait<'a, A, B> for X { } + +#[attr = Inline(Hint)] +fn foo<'a, Self, A, const B: _, const B2: _, T, U, + impl FnOnce() -> usize>(self: _, arg1: _) -> _ where + 'a:'a { self.foo::(arg1) } +#[attr = Inline(Hint)] +fn bar usize>(self: _, arg1: _) + -> _ { Trait::<'static, (), true>::foo::(self, arg1) } + +#[attr = Inline(Hint)] +fn foo2<'a, This, A, const B: _, const B2: _, T, U, + impl FnOnce() -> usize>(arg0: _, arg1: _) -> _ where + 'a:'a { foo::(arg0, arg1) } +#[attr = Inline(Hint)] +fn bar2 usize>(arg0: _, arg1: _) + -> _ { bar::(arg0, arg1) } + +trait Trait2 { + #[attr = Inline(Hint)] + fn foo3<'a, This, A, const B: _, const B2: _, T, U, + impl FnOnce() -> usize>(arg0: _, arg1: _) -> _ where + 'a:'a { foo2::(arg0, arg1) } + #[attr = Inline(Hint)] + fn bar3 usize>(arg0: _, arg1: _) + -> _ { bar2::(arg0, arg1) } +} + +impl Trait2 for () { } + +#[attr = Inline(Hint)] +fn foo4<'a, This, A, const B: _, const B2: _, T, U, + impl FnOnce() -> usize>(arg0: _, arg1: _) -> _ where + 'a:'a { <() as Trait2>::foo3::(arg0, arg1) } +#[attr = Inline(Hint)] +fn bar4 usize>(arg0: _, arg1: _) + -> _ { <() as Trait2>::bar3::(arg0, arg1) } + +fn main() { } diff --git a/tests/pretty/delegation-self-rename.rs b/tests/pretty/delegation-self-rename.rs new file mode 100644 index 0000000000000..9054bb2b89571 --- /dev/null +++ b/tests/pretty/delegation-self-rename.rs @@ -0,0 +1,32 @@ +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:delegation-self-rename.pp + +#![feature(fn_delegation)] + +trait Trait<'a, A, const B: bool> { + fn foo<'b, const B2: bool, T, U>(&self, f: impl FnOnce() -> usize) -> usize { + f() + 1 + } +} + +struct X; +impl<'a, A, const B: bool> Trait<'a, A, B> for X {} + +reuse Trait::foo; +reuse Trait::<'static, (), true>::foo:: as bar; + +reuse foo as foo2; +reuse bar as bar2; + +trait Trait2 { + reuse foo2 as foo3; + reuse bar2 as bar3; +} + +impl Trait2 for () {} + +reuse <() as Trait2>::foo3 as foo4; +reuse <() as Trait2>::bar3 as bar4; + +fn main() {} diff --git a/tests/ui/delegation/generics/self-rename.rs b/tests/ui/delegation/generics/self-rename.rs new file mode 100644 index 0000000000000..a4c92cecb5c6b --- /dev/null +++ b/tests/ui/delegation/generics/self-rename.rs @@ -0,0 +1,45 @@ +//@ run-pass + +#![feature(fn_delegation)] + +trait Trait<'a, A, const B: bool> { + fn foo<'b, const B2: bool, T, U>(&self, f: impl FnOnce() -> usize) -> usize { + f() + 1 + } +} + +struct X; +impl<'a, A, const B: bool> Trait<'a, A, B> for X {} + +reuse Trait::foo; +reuse Trait::<'static, (), true>::foo:: as bar; + +reuse foo as foo2; +reuse bar as bar2; + +trait Trait2 { + reuse foo2 as foo3; + reuse bar2 as bar3; +} + +impl Trait2 for () {} + +reuse <() as Trait2>::foo3 as foo4; +reuse <() as Trait2>::bar3 as bar4; + +fn main() { + assert_eq!(foo::<'static, X, (), true, false, (), ()>(&X, || 123), 124); + assert_eq!(foo2::<'static, X, (), true, false, (), ()>(&X, || 123), 124); + assert_eq!(<()>::foo3::<'static, X, (), true, false, (), ()>(&X, || 123), 124); + assert_eq!(foo4::<'static, X, (), true, false, (), ()>(&X, || 123), 124); + + assert_eq!(bar::(&X, || 123), 124); + assert_eq!(bar2::(&X, || 123), 124); + assert_eq!(<()>::bar3::(&X, || 123), 124); + assert_eq!(bar4::(&X, || 123), 124); + + assert_eq!(bar(&X, || 123), 124); + assert_eq!(bar2(&X, || 123), 124); + assert_eq!(<()>::bar3(&X, || 123), 124); + assert_eq!(bar4::(&X, || 123), 124); +} diff --git a/tests/ui/delegation/generics/synth-params-ice-154780.rs b/tests/ui/delegation/generics/synth-params-ice-154780.rs index 3ea52818a8f08..ef8684698612f 100644 --- a/tests/ui/delegation/generics/synth-params-ice-154780.rs +++ b/tests/ui/delegation/generics/synth-params-ice-154780.rs @@ -62,40 +62,49 @@ mod test_3 { } } -// FIXME(fn_delegation): rename Self generic param in recursive delegations -// mod test_4 { -// trait Trait<'a, A, const B: bool> { -// fn foo<'b, const B2: bool, T, U>(&self, f: impl FnOnce() -> usize) -> usize { -// f() -// } -// } - -// struct X; -// impl<'a, A, const B: bool> Trait<'a, A, B> for X {} - -// reuse Trait::foo; -// reuse Trait::<'static, (), true>::foo:: as bar; - -// trait Trait2 { -// reuse foo; -// reuse bar; -// } - -// reuse Trait2::foo as foo2; -// reuse Trait2::foo::<'static, X, (), true, false, (), ()> as foo3; -// reuse Trait2::bar as bar2; -// reuse Trait2::bar:: as bar3; - -// pub fn check() { -// assert_eq!(foo::<'static, X, (), true, false, (), ()>(&X, || 123), 123); -// assert_eq!(bar::(&X, || 123), 123); -// assert_eq!(bar(&X, || 123), 123); -// } -// } +// Test recursive delegation through trait +mod test_4 { + trait Trait<'a, A, const B: bool> { + fn foo<'b, const B2: bool, T, U>(&self, f: impl FnOnce() -> usize) -> usize { + f() + } + } + + struct X; + impl<'a, A, const B: bool> Trait<'a, A, B> for X {} + + reuse Trait::foo; + reuse Trait::<'static, (), true>::foo:: as bar; + + trait Trait2 { + reuse foo; + reuse bar; + } + + impl Trait2 for () {} + + reuse <() as Trait2>::foo as foo2; + reuse <() as Trait2>::foo::<'static, X, (), true, false, (), ()> as foo3; + reuse <() as Trait2>::bar as bar2; + reuse <() as Trait2>::bar:: as bar3; + + pub fn check() { + assert_eq!(foo::<'static, X, (), true, false, (), ()>(&X, || 123), 123); + assert_eq!(bar::(&X, || 123), 123); + assert_eq!(bar(&X, || 123), 123); + + assert_eq!(foo2::<'static, X, (), true, false, (), ()>(&X, || 123), 123); + assert_eq!(bar2::(&X, || 123), 123); + assert_eq!(bar2(&X, || 123), 123); + + assert_eq!(foo3(&X, || 123), 123); + assert_eq!(bar3(&X, || 123), 123); + } +} fn main() { test_1::check(); test_2::check(); test_3::check(); - // test_4::check(); + test_4::check(); }