Skip to content

delegation: supporting inherent impls - #160505

Draft
aerooneqq wants to merge 5 commits into
rust-lang:mainfrom
aerooneqq:delegation-inherent-methods
Draft

delegation: supporting inherent impls#160505
aerooneqq wants to merge 5 commits into
rust-lang:mainfrom
aerooneqq:delegation-inherent-methods

Conversation

@aerooneqq

@aerooneqq aerooneqq commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

This PR adds support for delegation to inherent impl functions on the delegation side.

Support for inherent impls in delegation consists of two problems: we need to resolve inherent function through ProbeContext routine and then we need to generate delegation function knowing the DefId of the signature function. The first problem is a fundamental problem given current compiler architecture, and it is not solved in this PR. To imitate working resolution for tests we adopt simple resolution by name only in inherent impls (not trait impls, which would work if we implement fair resolution through ProbeContext). A resolve_type_relative_delegations query was created which tries to resolve unresolved delegations after resolve stage. In future, when we will be able to fairly resolve delegations through ProbeContext contents of this query can be changed and all other logic implemented in this pull request will work.

This PR solves the second problem adjusting all delegation-related code such that it can handle delegations to inherent functions.

Free to inherent impl

Unlike free to trait delegation where we generated explicit Self param, here we just use default parameter.

struct X<'a, T, const B: bool>(...);
impl<'a, T, const B: bool> X<'a, T, B> {
  fn foo<'b, U, const X: usize>(&self) { ... }
}

reuse X::foo;
reuse X::<'static, (), false>::foo as foo1;
reuse X::foo::<'static, (), 123> as foo2;
reuse X::foo::<'static, (), false,>::foo::<'static, (), 123> as foo3;

//Desugaring:
#[attr = Inline(Hint)]
fn foo<'a, 'b, T, const B: _, U, const X: _>(self: _) -> _ where 'a:'a,
    'b:'b { X<'a, T, B>::foo::<'b, U, X>(self) }

#[attr = Inline(Hint)]
fn foo1<'b, U, const X: _>(self: _) -> _ where
    'b:'b { X<'static, (), false>::foo::<'b, U, X>(self) }

#[attr = Inline(Hint)]
fn foo2<'a, T, const B: _>(self: _) -> _ where
    'a:'a { X<'a, T, B>::foo::<'static, (), 123>(self) }

#[attr = Inline(Hint)]
fn foo3(self: _) -> _ { X<'static, (), false>::foo::<'static, (), 123>(self) }

Trait to inherent impl

In trait to inherent impl delegation we replace the type of self parameter from impl's type to Self generic param (if the signature function is a method).

trait Trait {
    reuse X::foo;
    reuse X::<'static, (), false>::foo as foo1;
    reuse X::foo::<'static, (), true> as foo2;
    reuse X::<'static, (), false,>::foo::<'static, (), true> as foo3;
}

// Desugaring:
trait Trait {
    #[attr = Inline(Hint)]
    fn foo<'a, 'b, T, const B: _, U, const X: _>(self: _) -> _ where 'a:'a,
        'b:'b { X<'a, T, B>::foo::<'b, U, X>(self) }

    #[attr = Inline(Hint)]
    fn foo1<'b, U, const X: _>(self: _) -> _ where
        'b:'b { X<'static, (), false>::foo::<'b, U, X>(self) }

    #[attr = Inline(Hint)]
    fn foo2<'a, T, const B: _>(self: _) -> _ where
        'a:'a { X<'a, T, B>::foo::<'static, (), 123>(self) }

    #[attr = Inline(Hint)]
    fn foo3(self: _)
        -> _ { X<'static, (), false>::foo::<'static, (), 123>(self) }
}

Note that we didn't specified target expression, so we would get errors like:

error[E0308]: mismatched types
  --> $DIR/xd.rs:10:14
   |
LL | trait Trait {
   | ----------- found this type parameter
LL |     reuse X::foo;
   |              ^^^
   |              |
   |              expected `&X<'_, T, B>`, found `&Self`
   |              arguments to this function are incorrect
   |
   = note: expected reference `&X<'_, T, B>`
              found reference `&Self`

Trait impl to inherent impl

Here the resolution should look signature in trait as in other cases where we delegate from trait impl. We generate function whose signature matches the resolved function in trait. We propagate only child generics if they are not specified.

trait Trait {
    fn foo<A, B, C>(&self) { }
    fn foo1<T, U, V>(&self) { }
    fn foo2<'a, T, U, V>(&self) where 'a:'a { }
    fn foo3(&self) { }
}

impl Trait for X {
    reuse X::foo;
    reuse X::<'static, (), false>::foo as foo1;
    reuse X::foo::<'static, (), 123> as foo2;
    reuse X::<'static, (), false,>::foo::<'static, (), 123> as foo3;
}

// Desugaring:
impl Trait for X<'_> {
    #[attr = Inline(Hint)]
    fn foo<A, B, C>(self: _) -> _ { X::foo::<A, B, C>(self) }

    #[attr = Inline(Hint)]
    fn foo1<T, U, V>(self: _)
        -> _ { X<'static, (), false>::foo::<T, U, V>(self) }

    #[attr = Inline(Hint)]
    fn foo2<'a, T, U, V>(self: _) -> _ where
        'a:'a { X::foo::<'static, (), 123>(self) }

    #[attr = Inline(Hint)]
    fn foo3(self: _)
        -> _ { X<'static, (), false>::foo::<'static, (), 123>(self) }
}

Inherent impl to inherent impl

In inherent impl to inherent impl delegation we replace signature self type with delegation parent self type in case of methods.

trait Trait {
    fn foo<A, B, C>(&self) { }
    fn foo1<T, U, V>(&self) { }
    fn foo2<'a, T, U, V>(&self) where 'a:'a { }
    fn foo3(&self) { }
}

struct Y;

impl Trait for Y {
    reuse X::foo;
    reuse X::<'static, (), false>::foo as foo1;
    reuse X::foo::<'static, (), 123> as foo2;
    reuse X::<'static, (), false,>::foo::<'static, (), 123> as foo3;
}

impl Trait for Y {
    #[attr = Inline(Hint)]
    fn foo<A, B, C>(self: _) -> _ { X::foo::<A, B, C>(self) }

    #[attr = Inline(Hint)]
    fn foo1<T, U, V>(self: _)
        -> _ { X<'static, (), false>::foo::<T, U, V>(self) }

    #[attr = Inline(Hint)]
    fn foo2<'a, T, U, V>(self: _) -> _ where
        'a:'a { X::foo::<'static, (), 123>(self) }

    #[attr = Inline(Hint)]
    fn foo3(self: _)
        -> _ { X<'static, (), false>::foo::<'static, (), 123>(self) }
}

We did not specify target expression so we would get errors like:

error[E0308]: mismatched types
  --> $DIR/xd.rs:12:14
   |
LL |     reuse X::foo;
   |              ^^^
   |              |
   |              expected `&X<'_, T, B>`, found `Y`
   |              arguments to this function are incorrect
   |
   = note: expected reference `&X<'_, T, B>`
                 found struct `Y`

Generics

TODO

Other concerns

Glob and list delegations

List delegations are supported, glob delegations are not supported:

struct X;

impl X {
    fn foo(&self) {}
    fn foo2(&self) {}
}

struct Y;

impl Y {
    reuse X::{foo, foo2} { X }
}

impl Y {
    reuse X::*;
    //~^ ERROR: expected trait, found struct `X`
}

Self type adjustments and target expression deletion

Adjustments for receiver are applied, adjustments for other parameters whose types contain Self are not applied as Self acts as a type alias to the struct, not a generic param which will can get replaced. The deletion of target expression should work as before.

enum X {
   ...
}

impl X {
    fn static_f() {}
    fn by_value(self) {}
    fn by_ref(&self) {}
    fn by_mut_ref(&mut self) {}
}

struct Y;

impl Y {
    fn get_x(&self) -> X { X }
    reuse X::{static_f, by_value, by_ref, by_mut_ref} { self.get_x() }
}

impl Y {
    fn get_x(&self) -> X { X }
    
    #[attr = Inline(Hint)]
    fn static_f() -> _ { X::static_f() }

    #[attr = Inline(Hint)]
    fn by_value(self: _) -> _ { X::by_value(self.get_x()) }

    #[attr = Inline(Hint)]
    fn by_ref(self: _) -> _ { X::by_ref(self.get_x()) }

    #[attr = Inline(Hint)]
    fn by_mut_ref(self: _) -> _ { X::by_mut_ref(self.get_x()) }
}

fn main() {
    let y = Y;
    y.by_ref();
    y.by_mut_ref();
    //~^ ERROR: cannot borrow `y` as mutable, as it is not declared as mutable
    y.by_value();

    let y = &Y;
    y.by_value();
    //~^ ERROR: cannot move out of `*y` which is behind a shared reference
    y.by_ref();
    y.by_mut_ref();
    //~^ ERROR: cannot borrow `*y` as mutable, as it is behind a `&` reference

    let y = &mut Y;
    y.by_value();
    //~^ ERROR: cannot move out of `*y` which is behind a mutable reference
    y.by_ref();
    y.by_mut_ref();
}

Recursive delegations

Works as before, we just check the resolution chain and we do not care whether it came from resolution at resolve stage or from resolution of type relative delegations.

Checking generic arguments in impl

In the example below there will be ICE as lifetime 'a is missing from struct S in inherent impl. This causes ICE as we get generic arguments of the signature from inherent impl (not from the struct declaration) and we will miss one, so there can be ICE when trying to instantiate signature or predicates with wrong number of generic args. So we check this at AST -> HIR lowering, as if we check it during hir_analysis and return error signature and empty clauses there will be another ICE. So I think it is better to handle it earlier and generate error delegation instead.

struct S<'a, A, const C: usize> {
    xd: &'a [A; C],
}

impl<'a, 'b, 'c, A, const C: usize> S<A, C> {
//~^ ERROR: implicit elided lifetime not allowed here
    fn foo_self<'d: 'd, 'e, T, const B: bool>(self) {}
}

trait Trait<'a, AA, BB> where Self: Sized {
    reuse S::foo_self;
    //~^ ERROR: this function takes 1 argument but 0 arguments were supplied
}

r? @petrochenkov

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Aug 4, 2026
@petrochenkov

Copy link
Copy Markdown
Contributor

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Aug 4, 2026
@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Aug 4, 2026
@rust-log-analyzer

This comment has been minimized.

@rust-bors

rust-bors Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 38e5ff7 (38e5ff7311d36112747c8aa89f4bb52168984074)
Base parent: c9ff496 (c9ff496891c278ad660bc0ab85c1f0b72059464a)

@rust-timer

This comment has been minimized.

@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (38e5ff7): comparison URL.

Overall result: ❌ regressions - no action needed

Benchmarking means the PR may be perf-sensitive. Consider adding rollup=never if this change is not fit for rolling up.

@rustbot label: -S-waiting-on-perf -perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
0.3% [0.3%, 0.3%] 1
Regressions ❌
(secondary)
0.2% [0.2%, 0.2%] 2
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.3% [0.3%, 0.3%] 1

Max RSS (memory usage)

Results (primary 1.9%, secondary -0.0%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
1.9% [0.4%, 4.2%] 3
Regressions ❌
(secondary)
1.4% [0.5%, 2.4%] 2
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-0.6% [-1.0%, -0.4%] 5
All ❌✅ (primary) 1.9% [0.4%, 4.2%] 3

Cycles

Results (primary -1.1%, secondary 0.4%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
0.5% [0.4%, 0.5%] 3
Regressions ❌
(secondary)
1.6% [0.4%, 5.3%] 6
Improvements ✅
(primary)
-1.8% [-6.6%, -0.5%] 7
Improvements ✅
(secondary)
-1.4% [-3.6%, -0.5%] 4
All ❌✅ (primary) -1.1% [-6.6%, 0.5%] 10

Binary size

This perf run didn't have relevant results for this metric.

Bootstrap: 489.838s -> 487.916s (-0.39%)
Artifact size: 390.28 MiB -> 391.07 MiB (0.20%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Aug 4, 2026
@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@aerooneqq aerooneqq changed the title delegation: supporting inherent methods delegation: supporting inherent impls Aug 6, 2026
@petrochenkov

Copy link
Copy Markdown
Contributor

@bors try @rust-timer queue

@rust-timer

This comment has been minimized.

@rustbot rustbot added the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Aug 7, 2026
@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Aug 7, 2026
@rust-bors

rust-bors Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 40975b9 (40975b9db210413c2edea725163a22b41796ad3e)
Base parent: 65bcac4 (65bcac45b3d8a8b2126e5cc844cf6fff5795d32a)

@rust-timer

This comment has been minimized.

@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (40975b9): comparison URL.

Overall result: ❌ regressions - no action needed

Benchmarking means the PR may be perf-sensitive. Consider adding rollup=never if this change is not fit for rolling up.

@rustbot label: -S-waiting-on-perf -perf-regression

Instruction count

Our most reliable metric. Used to determine the overall result above. However, even this metric can be noisy.

mean range count
Regressions ❌
(primary)
0.2% [0.2%, 0.2%] 1
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) 0.2% [0.2%, 0.2%] 1

Max RSS (memory usage)

Results (primary 1.1%, secondary -0.2%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
1.1% [0.8%, 1.3%] 2
Regressions ❌
(secondary)
0.6% [0.4%, 0.8%] 7
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-1.7% [-3.5%, -0.4%] 4
All ❌✅ (primary) 1.1% [0.8%, 1.3%] 2

Cycles

Results (primary 2.2%, secondary 0.2%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
2.2% [2.2%, 2.2%] 1
Regressions ❌
(secondary)
0.9% [0.4%, 2.7%] 12
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-0.8% [-1.2%, -0.4%] 8
All ❌✅ (primary) 2.2% [2.2%, 2.2%] 1

Binary size

Results (primary -0.1%, secondary -0.1%)

A less reliable metric. May be of interest, but not used to determine the overall result above.

mean range count
Regressions ❌
(primary)
- - 0
Regressions ❌
(secondary)
- - 0
Improvements ✅
(primary)
-0.1% [-0.1%, -0.0%] 48
Improvements ✅
(secondary)
-0.1% [-0.1%, -0.0%] 23
All ❌✅ (primary) -0.1% [-0.1%, -0.0%] 48

Bootstrap: 458.664s -> 461.027s (0.52%)
Artifact size: 398.60 MiB -> 398.58 MiB (-0.01%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Aug 7, 2026
@rust-bors

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@rust-log-analyzer

This comment has been minimized.

@aerooneqq
aerooneqq force-pushed the delegation-inherent-methods branch from 8376dfe to 5aca266 Compare August 12, 2026 07:29
@rust-bors

rust-bors Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

☔ The latest upstream changes (presumably #160974) made this pull request unmergeable. Please resolve the merge conflicts by rebasing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants