Skip to content

-Znext-solver Allow method calls on chains of assoc types of not-yet defined opaque types - #161414

Draft
ShoyuVanilla wants to merge 8 commits into
rust-lang:mainfrom
ShoyuVanilla:opaque-hidden-bounds
Draft

-Znext-solver Allow method calls on chains of assoc types of not-yet defined opaque types#161414
ShoyuVanilla wants to merge 8 commits into
rust-lang:mainfrom
ShoyuVanilla:opaque-hidden-bounds

Conversation

@ShoyuVanilla

@ShoyuVanilla ShoyuVanilla commented Aug 20, 2026

Copy link
Copy Markdown
Member

View all comments

Fixes rust-lang/trait-system-refactor-initiative#248

Still lacks of tons of comments and some tests (such as recursive projections or assoc item of supertraits) 😅

And it have some temporary FIXME comments as I'm leaving for a vacation until next Monday and will fix them as soon as I can

r? lcnr

@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. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver) labels Aug 20, 2026

pub type PredefinedOpaques<'tcx> = &'tcx ty::List<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)>;
pub type HiddenTypesOfOpaques<'tcx> =
&'tcx ty::List<(Ty<'tcx>, Option<ty::OpaqueHiddenTyBound<'tcx>>)>;

@ShoyuVanilla ShoyuVanilla Aug 20, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the flattend version of { (?x, [p1, p2]), (?y, []) } into [(?x, Some(p1)), (?x, Some(p2)), (?y, None) ], to make it slice-internable.

I'm not sure whether we should use a single representation 🤔
And I'm not sure whether we really need the empty bounds, as we still have <?x: MetaSized> bounds for impl ?Sized (would we have some real empty one someday?)

View changes since the review

@rust-log-analyzer

This comment has been minimized.

Comment thread compiler/rustc_next_trait_solver/src/solve/mod.rs Outdated
@ShoyuVanilla

Copy link
Copy Markdown
Member Author

@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 20, 2026
@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Aug 20, 2026
`-Znext-solver` Allow method calls on (recursive) assoc types of not-yet defined opaque types
@rust-bors

rust-bors Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 9b6316b (9b6316b23f8c267f599a254241d3b4bc0bac90c9)
Base parent: f7d782a (f7d782a3be46d6bb4b9792fe69a61db389ba1769)

@rust-timer

This comment has been minimized.

@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (9b6316b): comparison URL.

Overall result: ❌ regressions - please read:

Benchmarking means the PR may be perf-sensitive. It's automatically marked not fit for rolling up. Overriding is possible but disadvised: it risks changing compiler perf.

Next, please: If you can, justify the regressions found in this try perf run in writing along with @rustbot label: +perf-regression-triaged. If not, fix the regressions and do another perf run. Neutral or positive results will clear the label automatically.

@bors rollup=never rustc-perf
@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.1%, 0.3%] 6
Regressions ❌
(secondary)
2.9% [0.2%, 15.7%] 29
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-1.0% [-1.0%, -1.0%] 1
All ❌✅ (primary) 0.2% [0.1%, 0.3%] 6

Max RSS (memory usage)

Results (primary -1.1%, secondary 1.8%)

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)
3.9% [0.7%, 7.6%] 14
Improvements ✅
(primary)
-1.1% [-1.1%, -1.1%] 1
Improvements ✅
(secondary)
-4.1% [-7.9%, -2.1%] 5
All ❌✅ (primary) -1.1% [-1.1%, -1.1%] 1

Cycles

Results (secondary 6.5%)

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)
6.5% [3.2%, 11.4%] 9
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Binary size

Results (primary -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.1%] 1
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) -0.1% [-0.1%, -0.1%] 1

Bootstrap: 456.256s -> 457.797s (0.34%)
Artifact size: 398.95 MiB -> 399.18 MiB (0.06%)

@rustbot rustbot added perf-regression Performance regression. and removed S-waiting-on-perf Status: Waiting on a perf run to be completed. labels Aug 20, 2026
Comment thread compiler/rustc_infer/src/infer/opaque_types/table.rs Outdated
Comment thread compiler/rustc_type_ir/src/opaque_ty.rs Outdated
self.cx
}

fn fold_ty(&mut self, ty: I::Ty) -> I::Ty {

@lcnr lcnr Aug 25, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this needs to fold_binder to because we need to shift the index of the anon-bound inwards. Otherwise, use could use https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/enum.BoundVarIndexKind.html#variant.Canonical and change this to BoundVarIndexKind::UniqueOuterScope

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The former sounds more preferable, as the later might not work well with the canonicalization 😄

@lcnr lcnr left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

really cool. Pretty much what I expected from looking at the main changes :3

View changes since this review

Comment on lines 25 to +27
opaque_types: usize,
duplicate_entries: usize,
hidden_types_of_opaques: FxIndexMap<Ty<'tcx>, usize>,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that seems quite expensive 🤔

I guess this and more importantly, canonicalization of opaque types now has to also canonicalize hidden_types_of_opaques

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you mean the canonicalizations in the rustc_infer? (as the canonicaliztions in the solver handles them)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For most cases, hidden_types_of_opaques doesn't grow too large by now, so it might make sense to just flatten this in the storage and track the whole len() for it

@lcnr

lcnr commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

I feel like one big change is to change the way we handle opaque types in inputs and external constraints in the response outside of this PR. The fact that we intern and then canonicalize and then reintern seems clearly bad 🤣

You have a FIXME in the code somewhere for that already :>

@ShoyuVanilla

Copy link
Copy Markdown
Member Author

I feel like one big change is to change the way we handle opaque types in inputs and external constraints in the response outside of this PR. The fact that we intern and then canonicalize and then reintern seems clearly bad 🤣

You have a FIXME in the code somewhere for that already :>

Yeah, I really should refactor and optimize them. Currently I'm trying to make things correct wrt the above my comment (has_no_inference_or_external_constraints) It won't take too long though 😅

assert!(delegate.clone_opaque_types_lookup_table().is_empty());
}

for chunk in input.hidden_types_of_opaques_in_body.as_slice().chunk_by(|a, b| a.0 == b.0) {

@adwinwhite adwinwhite Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we flatten the bounds outside the method_autoderef query and gather them by hidden type here again?

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's kinda mess how I pass hidden_types_of_opaques in this PR yet, basically in two ways: flattened vec and the nested one keyed by hidden type.
I gathered them by the hidden type here just to prevent pushing to many undo logs. I'll fix the hidden_types_of_opaques eventually and I hope it would be look much nicer by then 😅

let self_ty_is_opaque = |ty: Ty<'_>| {
if let &ty::Infer(ty::TyVar(vid)) = ty.kind() {
infcx.has_opaques_with_sub_unified_hidden_type(vid)
infcx.has_hidden_types_of_opaques_modulo_sub_unification(vid)

@adwinwhite adwinwhite Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be obvious but I'm struggling to understand why we can detect the projection term via hidden infer var. 😢
Issue 248 is about <hidden as Trait>::Projection: ProjectionItemBound. The self_ty in method lookup is an fresh infer var from normalizing <hidden as Trait>::Projection, I presume? So it's not the hidden infer var. However, item bounds we gather from the projection item are keyed by hidden 🤔

View changes since the review

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The self_ty in method lookup is an fresh infer var from normalizing <hidden as Trait>::Projection, I presume?

Yes, it is. So has_hidden_types_of_opaques_modulo_sub_unification checks both cases for ?fresh_infer_var_for_opaque and ?fresh_infer_var_for_possibly_multiple_projection_on_opaque), the key for those bounds.

It is necessary to check the later as we have the cases with <<{opaque} as TraitA>::Assoc> as TraitB>::Assoc like the following, which is compiled with the old solver (and this PR) but not with the next-solver:

trait Foo {
    fn foo(&self) {}
}

trait Bar {
    type Assoc: Foo;

    fn bar(&self) -> Self::Assoc {
        loop {}
    }
}

trait Baz {
    type Assoc: Bar;

    fn baz(&self) -> Self::Assoc {
        loop {}
    }
}

impl Foo for () {}

impl Bar for () {
    type Assoc = ();
}

impl Baz for () {
    type Assoc = ();
}

fn heck() -> impl Baz {
    heck().baz().bar().foo()
}

fn main() {}

@adwinwhite adwinwhite Aug 26, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So has_hidden_types_of_opaques_modulo_sub_unification checks both cases for ?fresh_infer_var_for_opaque and ?fresh_infer_var_for_possibly_multiple_projection_on_opaque), the key for those bounds.

In the method body, has_hidden_types_of_opaques_modulo_sub_unification only checks the keys of hidden_types_of_opaques so you mean those keys also contains fresh infer vars from normalizing projections 🤔
But I don't find where we add bounds for these fresh infer vars? They're all for hidden infers of opaques?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally got it. Thank you for the explanation!
We could use better naming for these things now that they handle more than hidden types of opaques?
Ofc we should fix correctness issues first :>

@ShoyuVanilla ShoyuVanilla Aug 27, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I really should fix those namings 😄 I hope most of the correctness things(except folding binders and some canonicalization things) might be fixed by now so I'll try the perf and the naming/comments sides soon

// FIXME: Explain this hack. Why this is needed and why should be done here
// FIXME: Maybe we need probing for whole this call as the following lines
// directly add `hidden_types_of_opaques` to the context without probing or
// instantiating the response.

@ShoyuVanilla ShoyuVanilla Aug 26, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, thinking twice, this feel might be okay to me, as the num_entries are taken from the delegate before the canonicalization of the goal and compared to the evaluation result for both function-like evaluation of normalized-to and the outer projection goal

View changes since the review

@rust-log-analyzer

This comment has been minimized.

@ShoyuVanilla

ShoyuVanilla commented Aug 27, 2026

Copy link
Copy Markdown
Member Author

Hmm, the CI failure looks unfortunate

fn features_ok1() -> impl Iterator<Item = ()> {
    None.into_iter()
}

fn features_ok2<'a>() -> impl Iterator<Item = &'a ()> {
    None.into_iter()
}

// This emits an error :<
fn features_err() -> impl Iterator<Item = &'static ()> {
    None.into_iter()
}

'static lifetime in the bound of hidden_types_of_opaques from the input is canonicalized and instantiated into a placeholder region in the nested context, while the same lifetime in the response is canonicalized as-is.

So we have two different hidden_types_of_opaques in the nested context, one from the input <{opaque} as Iterator>::Item = &'!1 () and one we make trying to normalize the opaque inside the query <{opaque} as Iterator>::Item = &'static (). They are actually unified into the single one from the outer context, but inside the nested context, the later doesn't exist from the input so it keeps goal evaluation progressed forever with no actual progress.

So, basically I'm fixing such infinite progress with no fruitful result at all over multiple times 😅 I think I would be able to fix this one as well.

Edit) Fixed by checking whether the instantiation of the response actually changes the num_entries of the opaque storage

@ShoyuVanilla ShoyuVanilla changed the title -Znext-solver Allow method calls on (recursive) assoc types of not-yet defined opaque types -Znext-solver Allow method calls on chains of assoc types of not-yet defined opaque types Aug 27, 2026
@ShoyuVanilla

Copy link
Copy Markdown
Member Author

I'd like to do a perf run before trying some optimizations

@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 30, 2026
@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Aug 30, 2026
`-Znext-solver` Allow method calls on chains of assoc types of not-yet defined opaque types
@rust-bors

rust-bors Bot commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 3d55cff (3d55cff0498709d6bb560ae69670c9eb17280a97)
Base parent: 2e071b2 (2e071b28ef7e8a066b49a179e1da753c53500c62)

@rust-timer

This comment has been minimized.

@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (3d55cff): comparison URL.

Overall result: ❌✅ regressions and improvements - please read:

Benchmarking means the PR may be perf-sensitive. It's automatically marked not fit for rolling up. Overriding is possible but disadvised: it risks changing compiler perf.

Next, please: If you can, justify the regressions found in this try perf run in writing along with @rustbot label: +perf-regression-triaged. If not, fix the regressions and do another perf run. Neutral or positive results will clear the label automatically.

@bors rollup=never rustc-perf
@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.1%, 0.3%] 8
Regressions ❌
(secondary)
2.9% [0.4%, 13.6%] 27
Improvements ✅
(primary)
-0.2% [-0.2%, -0.2%] 2
Improvements ✅
(secondary)
-0.2% [-0.2%, -0.2%] 1
All ❌✅ (primary) 0.1% [-0.2%, 0.3%] 10

Max RSS (memory usage)

Results (primary -0.4%, secondary 1.8%)

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.6%, 1.6%] 2
Regressions ❌
(secondary)
3.8% [0.6%, 11.2%] 17
Improvements ✅
(primary)
-3.4% [-3.4%, -3.4%] 1
Improvements ✅
(secondary)
-3.8% [-5.8%, -2.5%] 6
All ❌✅ (primary) -0.4% [-3.4%, 1.6%] 3

Cycles

Results (secondary 5.0%)

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)
5.0% [2.3%, 9.4%] 10
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) - - 0

Binary size

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

Bootstrap: 475.013s -> 478.006s (0.63%)
Artifact size: 402.89 MiB -> 403.05 MiB (0.04%)

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

This comment has been minimized.

@ShoyuVanilla

Copy link
Copy Markdown
Member Author

I'd like to do a perf run before trying some optimizations, again 😅

@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 Sep 6, 2026
@rust-bors

This comment has been minimized.

rust-bors Bot pushed a commit that referenced this pull request Sep 6, 2026
`-Znext-solver` Allow method calls on chains of assoc types of not-yet defined opaque types
@rust-bors

rust-bors Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

☀️ Try build successful (CI)
Build commit: 3f30747 (3f3074761460f29e6fd9b22330231bf7778f903d)
Base parent: dbad1ba (dbad1bab60868d49978b716635ac2c3203e327cf)

@rust-timer

This comment has been minimized.

@rust-timer

Copy link
Copy Markdown
Collaborator

Finished benchmarking commit (3f30747): comparison URL.

Overall result: ❌✅ regressions and improvements - please read:

Benchmarking means the PR may be perf-sensitive. It's automatically marked not fit for rolling up. Overriding is possible but disadvised: it risks changing compiler perf.

Next, please: If you can, justify the regressions found in this try perf run in writing along with @rustbot label: +perf-regression-triaged. If not, fix the regressions and do another perf run. Neutral or positive results will clear the label automatically.

@bors rollup=never rustc-perf
@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
Regressions ❌
(secondary)
2.6% [0.1%, 13.7%] 30
Improvements ✅
(primary)
-0.2% [-0.2%, -0.2%] 2
Improvements ✅
(secondary)
- - 0
All ❌✅ (primary) -0.2% [-0.2%, -0.2%] 2

Max RSS (memory usage)

Results (secondary -2.8%)

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)
3.0% [0.7%, 4.6%] 4
Improvements ✅
(primary)
- - 0
Improvements ✅
(secondary)
-6.6% [-11.5%, -2.2%] 6
All ❌✅ (primary) - - 0

Cycles

Results (primary -2.5%, secondary 4.4%)

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)
5.5% [2.7%, 7.9%] 7
Improvements ✅
(primary)
-2.5% [-2.6%, -2.5%] 2
Improvements ✅
(secondary)
-3.3% [-3.3%, -3.3%] 1
All ❌✅ (primary) -2.5% [-2.6%, -2.5%] 2

Binary size

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

Bootstrap: 478.423s -> 479.616s (0.25%)
Artifact size: 403.41 MiB -> 403.61 MiB (0.05%)

@rustbot rustbot removed the S-waiting-on-perf Status: Waiting on a perf run to be completed. label Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

perf-regression Performance regression. 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. WG-trait-system-refactor The Rustc Trait System Refactor Initiative (-Znext-solver)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unconstrained assoc type of opaque type inference regression

6 participants