Rollup merge of #155635 - aerooneqq:delegation-generics-Self-rename, r=petrochenkov

delegation: rename `Self` generic param to `This` in recursive delegations

This PR supports renaming of `Self` generic parameter to `This` in recursive delegations scenario, this allows propagation of `This` as we rely on `Self` naming to check whether it is implicit Self of a trait. Comment with a bit deeper explanation is in `uplift_delegation_generic_params`. Part of rust-lang/rust#118212.

r? @petrochenkov
This commit is contained in:
Jonathan Brouwer
2026-04-24 18:19:15 +02:00
committed by GitHub
6 changed files with 237 additions and 54 deletions
+5 -10
View File
@@ -508,7 +508,7 @@ fn finalize_body_lowering(
// 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 @@ fn finalize_body_lowering(
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 @@ fn finalize_body_lowering(
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 @@ fn process_segment(
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) };
@@ -6,7 +6,7 @@
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<T> {
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 @@ pub(super) fn into_hir_generics(
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 @@ fn hir_generics_or_empty(&self) -> &'hir hir::Generics<'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 @@ pub(super) fn into_generic_args(
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 @@ pub(super) fn uplift_delegation_generics(
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 @@ pub(super) fn uplift_delegation_generics(
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 @@ fn uplift_delegation_generic_params(
&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 @@ fn uplift_delegation_generic_params(
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<Self>() {}` with generic
// param "Self" then it will not be generated in HIR, the same thing
// applies to traits, `trait Trait<Self> {}` 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();
+55
View File
@@ -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::<B2, T, U>(arg1) }
#[attr = Inline(Hint)]
fn bar<Self, impl FnOnce() -> usize>(self: _, arg1: _)
-> _ { Trait::<'static, (), true>::foo::<true, (), ()>(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::<This, A, B, B2, T, U>(arg0, arg1) }
#[attr = Inline(Hint)]
fn bar2<This, impl FnOnce() -> usize>(arg0: _, arg1: _)
-> _ { bar::<This>(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::<This, A, B, B2, T, U>(arg0, arg1) }
#[attr = Inline(Hint)]
fn bar3<This, impl FnOnce() -> usize>(arg0: _, arg1: _)
-> _ { bar2::<This>(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::<This, A, B, B2, T, U>(arg0, arg1) }
#[attr = Inline(Hint)]
fn bar4<This, impl FnOnce() -> usize>(arg0: _, arg1: _)
-> _ { <() as Trait2>::bar3::<This>(arg0, arg1) }
fn main() { }
+32
View File
@@ -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::<true, (), ()> 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() {}
@@ -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::<true, (), ()> 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>(&X, || 123), 124);
assert_eq!(bar2::<X>(&X, || 123), 124);
assert_eq!(<()>::bar3::<X>(&X, || 123), 124);
assert_eq!(bar4::<X>(&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>(&X, || 123), 124);
}
@@ -62,40 +62,49 @@ pub fn check() {
}
}
// 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()
// }
// }
// 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 {}
struct X;
impl<'a, A, const B: bool> Trait<'a, A, B> for X {}
// reuse Trait::foo;
// reuse Trait::<'static, (), true>::foo::<true, (), ()> as bar;
reuse Trait::foo;
reuse Trait::<'static, (), true>::foo::<true, (), ()> as bar;
// trait Trait2 {
// reuse foo;
// reuse 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::<X> as bar3;
impl Trait2 for () {}
// pub fn check() {
// assert_eq!(foo::<'static, X, (), true, false, (), ()>(&X, || 123), 123);
// assert_eq!(bar::<X>(&X, || 123), 123);
// assert_eq!(bar(&X, || 123), 123);
// }
// }
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::<X> as bar3;
pub fn check() {
assert_eq!(foo::<'static, X, (), true, false, (), ()>(&X, || 123), 123);
assert_eq!(bar::<X>(&X, || 123), 123);
assert_eq!(bar(&X, || 123), 123);
assert_eq!(foo2::<'static, X, (), true, false, (), ()>(&X, || 123), 123);
assert_eq!(bar2::<X>(&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();
}