Skip to content

delegation: supporting inherent impls - #160505

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

delegation: supporting inherent impls#160505
aerooneqq wants to merge 9 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.

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

After some experiments I think that we should force user to always specify generics for parent segment of delegation to inherent impls. Consider the following example and imagine that we can use fair resolution through ProbeContext:

trait M1 {}
trait M2 {}

struct S1;
struct S2;

impl M1 for S1 {}
impl M2 for S2 {}

struct X<T, U>(T, U);

impl<T: M1> X<T, ()> {
    fn foo() {}
}

impl<T: M2> X<T, usize> {
    fn foo() {}
}

reuse X::foo;

How to resolve X::foo? If we generate parent generics (fn foo<T, U>() { X::<T, U>::foo() }) which clauses should we inherit? It is impossible to determine which function to reuse, and despite the fact that there may be some cases where it is possible, I don't think that we should write heuristics for that. So always specifying parent generics seems to be a good option. Also I think we should ban infers in parent segment too.

One implementation aspect of how we map generic args for signature and predicates inheritance, as we inherit predicates not from the ADT declaration but from the impl block we need to take generic args from this impl, not from the declaration. So indices of generic args are taken from the impl block and then they are used in mapping and future instantiation:

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

// index of A = 3
// index of C = 4
impl<'a, 'b, 'c, A, const C: usize> S<A, C> {
    fn foo_self<'d: 'd, 'e, T, const B: bool>(self) {}
}

trait Trait<'a, AA, BB> where Self: Sized {
    reuse S::<(), ()>::foo_self;
    // Args: [Self/#0, 'a/#1, AA/#2, BB/#3, '{region error}, 'd/#4, (), {const error}, T/#5, B/#6]
    // Mapping: {0: 0, 7: 9, 5: 5, 3: 6, 6: 8, 4: 7}, A (index 3) is mapped into index 6 (`()`), C (index 4) mapped into index 7 (const error)
}

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.

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

This comment has been minimized.

@aerooneqq
aerooneqq force-pushed the delegation-inherent-methods branch from 686789f to 7057504 Compare August 13, 2026 06:38
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