Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Support proper interaction of user-specified args and impl Traits
  • Loading branch information
aerooneqq committed Apr 13, 2026
commit 51888a15fb80a98ee2966ac59bb6b05585762dc6
116 changes: 58 additions & 58 deletions compiler/rustc_ast_lowering/src/delegation/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,38 @@ use rustc_span::{Ident, Span};

use crate::{LoweringContext, ResolverAstLoweringExt};

pub(super) enum DelegationGenerics<T> {
#[derive(Clone, Copy)]
pub(super) enum DelegationGenericsKind {
/// User-specified args are present: `reuse foo::<String>;`.
UserSpecified,
/// The default case when no user-specified args are present: `reuse Trait::foo;`.
Default(T),
Default,
/// In free-to-trait reuse, when user specified args for trait `reuse Trait::<i32>::foo;`
/// in this case we need to both generate `Self` and process user args.
SelfAndUserSpecified(T),
SelfAndUserSpecified,
/// In delegations from trait impl to other entities like free functions or trait functions,
/// we want to generate a function whose generics matches generics of signature function
/// in trait.
TraitImpl(T, bool /* Has user-specified args */),
TraitImpl(bool /* Has user-specified args */),
}

pub(super) struct DelegationGenerics<T> {
generics: T,
kind: DelegationGenericsKind,
}

impl<'hir> DelegationGenerics<&'hir [ty::GenericParamDef]> {
fn default(generics: &'hir [ty::GenericParamDef]) -> Self {
DelegationGenerics { generics, kind: DelegationGenericsKind::Default }
}

fn user_specified(generics: &'hir [ty::GenericParamDef]) -> Self {
DelegationGenerics { generics, kind: DelegationGenericsKind::UserSpecified }
}

fn trait_impl(generics: &'hir [ty::GenericParamDef], user_specified: bool) -> Self {
DelegationGenerics { generics, kind: DelegationGenericsKind::TraitImpl(user_specified) }
}
}

/// Used for storing either ty generics or their uplifted HIR version. First we obtain
Expand Down Expand Up @@ -54,20 +74,19 @@ pub(super) struct GenericArgsPropagationDetails {
pub(super) use_args_in_sig_inheritance: bool,
}

impl<T> DelegationGenerics<T> {
fn args_propagation_details(&self) -> GenericArgsPropagationDetails {
impl DelegationGenericsKind {
fn args_propagation_details(self) -> GenericArgsPropagationDetails {
match self {
DelegationGenerics::UserSpecified | DelegationGenerics::SelfAndUserSpecified { .. } => {
GenericArgsPropagationDetails {
should_propagate: false,
use_args_in_sig_inheritance: true,
}
}
DelegationGenerics::TraitImpl(_, user_specified) => GenericArgsPropagationDetails {
should_propagate: !*user_specified,
DelegationGenericsKind::UserSpecified
| DelegationGenericsKind::SelfAndUserSpecified => GenericArgsPropagationDetails {
should_propagate: false,
use_args_in_sig_inheritance: true,
},
DelegationGenericsKind::TraitImpl(user_specified) => GenericArgsPropagationDetails {
should_propagate: !user_specified,
use_args_in_sig_inheritance: false,
},
DelegationGenerics::Default(_) => GenericArgsPropagationDetails {
DelegationGenericsKind::Default => GenericArgsPropagationDetails {
should_propagate: true,
use_args_in_sig_inheritance: false,
},
Expand All @@ -81,25 +100,9 @@ impl<'hir> HirOrTyGenerics<'hir> {
ctx: &mut LoweringContext<'_, 'hir, impl ResolverAstLoweringExt<'hir>>,
span: Span,
) -> &mut HirOrTyGenerics<'hir> {
if let HirOrTyGenerics::Ty(params) = self {
let mut uplift_params = |generics: &'hir [ty::GenericParamDef]| {
ctx.uplift_delegation_generic_params(span, generics)
};

let hir_generics = match params {
DelegationGenerics::UserSpecified => DelegationGenerics::UserSpecified,
DelegationGenerics::Default(params) => {
DelegationGenerics::Default(uplift_params(params))
}
DelegationGenerics::SelfAndUserSpecified(params) => {
DelegationGenerics::SelfAndUserSpecified(uplift_params(params))
}
DelegationGenerics::TraitImpl(params, user_specified) => {
DelegationGenerics::TraitImpl(uplift_params(params), *user_specified)
}
};

*self = HirOrTyGenerics::Hir(hir_generics);
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 });
}

self
Expand All @@ -108,12 +111,7 @@ impl<'hir> HirOrTyGenerics<'hir> {
fn hir_generics_or_empty(&self) -> &'hir hir::Generics<'hir> {
match self {
HirOrTyGenerics::Ty(_) => hir::Generics::empty(),
HirOrTyGenerics::Hir(hir_generics) => match hir_generics {
DelegationGenerics::UserSpecified => hir::Generics::empty(),
DelegationGenerics::Default(generics)
| DelegationGenerics::SelfAndUserSpecified(generics)
| DelegationGenerics::TraitImpl(generics, _) => generics,
},
HirOrTyGenerics::Hir(hir) => hir.generics,
}
}

Expand All @@ -127,21 +125,16 @@ impl<'hir> HirOrTyGenerics<'hir> {
HirOrTyGenerics::Ty(_) => {
bug!("Attempting to get generic args before uplifting to HIR")
}
HirOrTyGenerics::Hir(hir_generics) => match hir_generics {
DelegationGenerics::UserSpecified => hir::GenericArgs::NONE,
DelegationGenerics::Default(generics)
| DelegationGenerics::SelfAndUserSpecified(generics)
| DelegationGenerics::TraitImpl(generics, _) => {
ctx.create_generics_args_from_params(generics.params, add_lifetimes, span)
}
},
HirOrTyGenerics::Hir(hir) => {
ctx.create_generics_args_from_params(hir.generics.params, add_lifetimes, span)
}
}
}

pub(super) fn args_propagation_details(&self) -> GenericArgsPropagationDetails {
match self {
HirOrTyGenerics::Ty(ty_generics) => ty_generics.args_propagation_details(),
HirOrTyGenerics::Hir(hir_generics) => hir_generics.args_propagation_details(),
HirOrTyGenerics::Ty(ty) => ty.kind.args_propagation_details(),
HirOrTyGenerics::Hir(hir) => hir.kind.args_propagation_details(),
}
}
}
Expand Down Expand Up @@ -231,9 +224,10 @@ 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 = GenericsGenerationResult::new(DelegationGenerics::TraitImpl(&[], true));
let parent = DelegationGenerics::trait_impl(&[], true);
let parent = GenericsGenerationResult::new(parent);

let child = DelegationGenerics::TraitImpl(sig_params, child_user_specified);
let child = DelegationGenerics::trait_impl(sig_params, child_user_specified);
let child = GenericsGenerationResult::new(child);

return GenericsGenerationResults { parent, child };
Expand All @@ -257,22 +251,28 @@ impl<'hir, R: ResolverAstLoweringExt<'hir>> LoweringContext<'_, 'hir, R> {
if segments[len - 2].args.is_some() {
if generate_self {
// Take only first Self parameter, it is trait so Self must be present.
DelegationGenerics::SelfAndUserSpecified(&sig_parent_params[..1])
DelegationGenerics {
kind: DelegationGenericsKind::SelfAndUserSpecified,
generics: &sig_parent_params[..1],
}
} else {
DelegationGenerics::UserSpecified
DelegationGenerics::user_specified(&[])
}
} else {
let skip_self = usize::from(!generate_self);
DelegationGenerics::Default(&sig_parent_params[skip_self..])
DelegationGenerics::default(&sig_parent_params[skip_self..])
}
} else {
DelegationGenerics::<&'hir [ty::GenericParamDef]>::Default(&[])
DelegationGenerics::default(&[])
};

let child_generics = if child_user_specified {
DelegationGenerics::UserSpecified
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..])
} else {
DelegationGenerics::Default(sig_params)
DelegationGenerics::default(sig_params)
};

GenericsGenerationResults {
Expand Down
18 changes: 13 additions & 5 deletions compiler/rustc_hir_analysis/src/delegation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -318,11 +318,15 @@ fn create_generic_args<'tcx>(
let (caller_kind, callee_kind) = (fn_kind(tcx, delegation_id), fn_kind(tcx, sig_id));

let delegation_args = ty::GenericArgs::identity_for_item(tcx, delegation_id);
let delegation_parent_args_count = tcx.generics_of(delegation_id).parent_count;

let deleg_parent_args_without_self_count =
get_delegation_parent_args_count_without_self(tcx, delegation_id, sig_id);

let delegation_generics = tcx.generics_of(delegation_id);
let real_args_count = delegation_args.len() - delegation_generics.own_synthetic_params_count();
let synth_args = &delegation_args[real_args_count..];
let delegation_args = &delegation_args[..real_args_count];

let args = match (caller_kind, callee_kind) {
(FnKind::Free, FnKind::Free)
| (FnKind::Free, FnKind::AssocTrait)
Expand All @@ -339,14 +343,15 @@ fn create_generic_args<'tcx>(

assert!(child_args.is_empty(), "Child args can not be used in trait impl case");

tcx.mk_args(&delegation_args[delegation_parent_args_count..])
tcx.mk_args(&delegation_args[delegation_generics.parent_count..])
}

(FnKind::AssocInherentImpl, FnKind::AssocTrait) => {
let self_ty = tcx.type_of(tcx.local_parent(delegation_id)).instantiate_identity();

tcx.mk_args_from_iter(
std::iter::once(ty::GenericArg::from(self_ty)).chain(delegation_args.iter()),
std::iter::once(ty::GenericArg::from(self_ty))
.chain(delegation_args.iter().copied()),
)
}

Expand Down Expand Up @@ -411,7 +416,7 @@ fn create_generic_args<'tcx>(

new_args.extend_from_slice(&child_args[child_lifetimes_count..]);
} else if !parent_args.is_empty() {
let child_args = &delegation_args[delegation_parent_args_count..];
let child_args = &delegation_args[delegation_generics.parent_count..];

let child_lifetimes_count =
child_args.iter().take_while(|a| a.as_region().is_some()).count();
Expand All @@ -424,6 +429,8 @@ fn create_generic_args<'tcx>(
new_args.extend(&child_args[child_lifetimes_count + skip_self as usize..]);
}

new_args.extend(synth_args);

new_args
}

Expand Down Expand Up @@ -620,7 +627,8 @@ fn get_delegation_user_specified_args<'tcx>(
)
.0;

&args[parent_args.len()..]
let synth_params_count = tcx.generics_of(def_id).own_synthetic_params_count();
&args[parent_args.len()..args.len() - synth_params_count]
});

(parent_args.unwrap_or_default(), child_args.unwrap_or_default())
Expand Down
4 changes: 3 additions & 1 deletion compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,7 +745,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
GenericParamDefKind::Lifetime => {
self.lowerer.re_infer(self.span, RegionInferReason::Param(param)).into()
}
GenericParamDefKind::Type { has_default, .. } => {
GenericParamDefKind::Type { has_default, synthetic } => {
if !infer_args && has_default {
// No type parameter provided, but a default exists.
if let Some(prev) =
Expand All @@ -761,6 +761,8 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
.type_of(param.def_id)
.instantiate(tcx, preceding_args)
.into()
} else if synthetic {
Ty::new_param(tcx, param.index, param.name).into()
} else if infer_args {
self.lowerer.ty_infer(Some(param), self.span).into()
} else {
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_middle/src/ty/generics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,10 @@ impl<'tcx> Generics {
})
}

pub fn own_synthetic_params_count(&'tcx self) -> usize {
self.own_params.iter().filter(|p| p.kind.is_synthetic()).count()
}

/// Returns the args corresponding to the generic parameters
/// of this item, excluding `Self`.
///
Expand Down
26 changes: 22 additions & 4 deletions tests/ui/delegation/generics/mapping/free-to-free-pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,32 @@
//! delegation parent if applicable. At some tests predicates are
//! added. At some tests user-specified args are specified in reuse statement.

// Testing lifetimes + types + consts, reusing without
// user args, checking predicates inheritance
// Testing lifetimes + types + consts, reusing with(out)
// user args, checking predicates inheritance, testing with impl Traits
mod test_1 {
fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>() {}
trait Bound1 {}
trait Bound2 {}
trait Bound3 {}

struct X {}

impl Bound1 for X {}
impl Bound2 for X {}
impl Bound3 for X {}

fn foo<'a: 'a, 'b: 'b, T: Clone, U: Clone, const N: usize>(
_x: impl Bound1 + Bound2 + Bound3,
_f: impl FnOnce(T) -> U,
) {
}

pub fn check() {
reuse foo as bar;
bar::<i32, i32, 1>();
bar::<i32, i32, 1>(X {}, |x| x);

reuse foo::<'static, 'static, usize, String, 132> as bar1;

bar1(X {}, |x| x.to_string());
}
}

Expand Down
8 changes: 4 additions & 4 deletions tests/ui/delegation/generics/mapping/free-to-trait-pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,23 @@
//! added. At some tests user-specified args are specified in reuse statement.

// Testing lifetimes + types + consts in both parent and child, reusing in
// a function without generic params
// a function without generic params, with impl traits
mod test_1 {
trait Trait<'b, 'c, 'a, T, const N: usize>: Sized {
fn foo<'d: 'd, U, const M: bool>(self) {}
fn foo<'d: 'd, U, const M: bool>(self, _f: impl FnOnce() -> ()) {}
}

impl Trait<'static, 'static, 'static, i32, 1> for u8 {}

pub fn check() {
fn no_ctx() {
reuse Trait::foo as bar;
bar::<'static, 'static, 'static, 'static, u8, i32, 1, String, true>(123);
bar::<'static, 'static, 'static, 'static, u8, i32, 1, String, true>(123, || ());
}

fn with_ctx<'a, 'b, 'c, A, B, C, const N: usize, const M: bool>() {
reuse Trait::foo as bar;
bar::<'static, 'static, 'static, 'a, u8, i32, 1, A, M>(123);
bar::<'static, 'static, 'static, 'a, u8, i32, 1, A, M>(123, || ());
}

no_ctx();
Expand Down
12 changes: 6 additions & 6 deletions tests/ui/delegation/generics/mapping/impl-trait-to-free-pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,19 @@
//! delegation parent if applicable. At some tests predicates are
//! added. At some tests user-specified args are specified in reuse statement.

// Testing lifetimes + types/consts in child reuses,
// Testing lifetimes + types/consts in child reuses, with impl traits,
// with (un)specified user args with additional generic params in delegation parent
mod test_1 {
mod to_reuse {
pub fn foo<'a: 'a, 'b: 'b, A, B, const N: usize>() {}
pub fn bar<'a: 'a, 'b: 'b, A, B, const N: usize>(_x: &super::XX) {}
pub fn bar<'a: 'a, 'b: 'b, A, B, const N: usize>(_x: &super::XX, _f: impl FnOnce(A) -> B) {}
}

trait Trait<'a, 'b, 'c, A, B, const N: usize>: Sized {
fn foo<'x: 'x, 'y: 'y, AA, BB, const NN: usize>() {}
fn bar<'x: 'x, 'y: 'y, AA, BB, const NN: usize>(&self) {}
fn bar<'x: 'x, 'y: 'y, AA, BB, const NN: usize>(&self, _f: impl FnOnce(AA) -> BB) {}
fn oof() {}
fn rab(&self) {}
fn rab(&self, _f: impl FnOnce(A) -> B) {}
}

#[allow(dead_code)] // Fields are used instead of phantom data for generics use
Expand All @@ -44,9 +44,9 @@ mod test_1 {
<XX as Trait<'static, 'static, 'static, i32, i32, 1>>
::foo::<'static, 'static, i8, i16, 123>();
<XX as Trait<'static, 'static, 'static, i32, i32, 1>>
::bar::<'static, 'static, String, i16, 123>(&x);
::bar::<'static, 'static, String, i16, 123>(&x, |_| 123);
<XX as Trait<'static, 'static, 'static, i32, i32, 1>>::oof();
<XX as Trait<'static, 'static, 'static, i32, String, 1>>::rab(&x);
<XX as Trait<'static, 'static, 'static, i32, String, 1>>::rab(&x, |_| 123.to_string());
}
}

Expand Down
Loading
Loading