Skip to content

Conversation

@ShoyuVanilla
Copy link
Member

@ShoyuVanilla ShoyuVanilla commented Aug 30, 2025

Still work in progress and I have 4 remaining failing tests

failures:
    consteval::tests::closure_and_impl_fn
    mir::eval::tests::closure_layout_in_rpit
    mir::lower::tests::dyn_projection_with_auto_traits_regression_next_solver
    tests::traits::builtin_binop_expectation_works_on_single_reference

The last one seems that we are not applying some relates between int inference vars and the other three are related to rust-analyzer's PlaceholderTy crimes: We are interning its parent generic and local idx and then using that intern id into Placeholder's index, quite an abuse.

So, next-gen solver is panicking while trying to instantiate canonicals containing that PlaceholderTy in such a manner like trying to access the tens of thousands index of original values.
I'm trying to mitigate it utilizing BoundTykind to discern those "abused" placeholders and normal placeholders but it still failing with something like:

ra-ap-rustc_next_trait_solver-0.123.0/src/solve/eval_ctxt/canonical.rs:422:29:
index out of bounds: the len is 1 but the index is 1

Still a problem but much better than the len is 2 but the index is 42002 🙃

I'm drafting this PR as I'm going on a vacation for next three days and cannot work on this so much for that period 😅 So fill free to use this as coarse ingredients if we should do the migration in a rush 😄

@ChayimFriedman2
Copy link
Contributor

That seems especially grievous given that unlike Chalk, the next solver gives us precise control on what is in Placeholder and Param, so no transmute is needed 🫨

I'll try to see if we still need the info, and if yes create a new PR to switch to them, to not overload this PR.

@ShoyuVanilla
Copy link
Member Author

The same tests are still failing, but things improved significantly after #20586. One failure is related to InferenceVar relations (same as before). The other three have mitigated from index-out-of-bound errors to method resolution failures, since they now encounter unnormalized opaque types during MIR lowering. Normalizing them in the right place should resolve the issues.

I’ll work on finishing this once I’m back from work today 😄


pub trait ChalkToNextSolver<'db, Out> {
fn to_nextsolver(&self, interner: DbInterner<'db>) -> Out;
fn from_nextsolver(out: Out, interner: DbInterner<'db>) -> Self;
Copy link
Contributor

Choose a reason for hiding this comment

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

These things already exist, just under the name convert_X_from_result. Maybe a method in the trait make sense (but I guess then ChalkToNextSolver isn't an appropriate name?) but no need to recreate them.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I'm thinking of separating this into two traits, as after a while, it has panned out that some from_nextsolver method aren't called at all. Maybe I could do something like

enum Never {}

trait ChalkToNextSolver<'db, Out> {
    type FromNextSolver = Self;
    fn from_nextsolver(..) -> Self::FromNextSolver;
    ...
}

impl<'db> ChalkToNextSolver<'db, Bar> for Foo {
    type FromNextSolver = Never;
    ...
}

but it would not save for me not so many keystrokes 😅

Copy link
Member

Choose a reason for hiding this comment

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

Yes, so, I just moved the things from convert_X_for_result to trait methods (no need to keep the free functions around).

A separate trait or an associated type makes sense - I had done this because there were a lot of things that I had to convert back and forth compared to previously and this seemed better than creating a bunch of free functions.

Copy link
Contributor

Choose a reason for hiding this comment

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

A separate trait will allow us to use method call syntax, which is nice.

Copy link
Member Author

Choose a reason for hiding this comment

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

Separated this into new NextSolverToChalk

/// temporary allocations.
resolve_obligations_buffer: Vec<Canonicalized<InEnvironment<Goal>>>,
pub(crate) infer_ctxt: InferCtxt<'a>,
diverging_tys: FxHashSet<Ty>,
Copy link
Contributor

Choose a reason for hiding this comment

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

This whole "diverging" flag on an infer var is probably incorrect, that's not how rustc does it AFAIK and it also leads to incorrect results (at least on edition 2024, see #20460 (comment)). But it's probably better to leave it out of this PR, just maybe leave a FIXME.

Copy link
Member Author

Choose a reason for hiding this comment

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

Okay, I'll check this

Copy link
Member Author

Choose a reason for hiding this comment

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

I think it would be better to defer resolving this as a follow up, as it would be a bit complicated


#[tracing::instrument(level = "debug", skip(self))]
fn register_obligation_in_env(&mut self, goal: InEnvironment<Goal>) {
// If this goal is an `AliasEq` for an opaque type, just unify instead of trying to solve (since the next-solver is lazy)
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it the correct thing to do? I don't think we should deviate from rustc here, rather follow it and change the callsites, otherwise we will have bugs.

If this is just a temporary workaround to shorten this PR then fine, though.

Copy link
Member Author

Choose a reason for hiding this comment

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

Actually, most lines of this PR are cherry-picked from Jack's branch and I haven't looked into this close enough yet 😅
But IIRC, next solver is quite lazy on unifying opaque types in general. I'll take relevant lines from rustc soon.

Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

The new solver uses lazy normalization, sure, but that means we should not deviate from rustc and eagerly normalize, rather we should adjust the callsites that require a normalized type (probably those inspecting the TyKind) to normalize themselves, and leave the rest doing lazy normalization, like rustc.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ah, I see. So you’re suggesting that a more structured approach to lazy normalization would be preferable to a quick fix like this?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yes. As I said, if you prefer this hack for this PR that's also fine, as long as eventually we will do that.

Copy link
Member Author

Choose a reason for hiding this comment

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

Okay. I'll revisit this once all tests turn green (still at my office 😅) Maybe we could reuse those infra from rustc_type_ir

Copy link
Member

Choose a reason for hiding this comment

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

I don't even remember what I was doing here, lol. At some point, when other things around start to fall into place, I think this should fairly easily be able to be removed. But for now, idk.

Copy link
Member Author

Choose a reason for hiding this comment

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

I don't even remember what I was doing here, lol. At some point, when other things around start to fall into place, I think this should fairly easily be able to be removed. But for now, idk.

Simply removing it causes more failures in tests. Will check how rustc handles it 😄

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed this and managed to pass tests

@ShoyuVanilla ShoyuVanilla force-pushed the inftbl branch 3 times, most recently from ae0cbc7 to 089ef7e Compare September 7, 2025 20:46
@ShoyuVanilla ShoyuVanilla marked this pull request as ready for review September 7, 2025 20:47
@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 7, 2025
self.unify(&builtin_ret, &ret_ty);
builtin_ret
} else {
ret_ty
Copy link
Member Author

@ShoyuVanilla ShoyuVanilla Sep 7, 2025

Choose a reason for hiding this comment

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

opaque_ty_id: id,
substitution: subst,
}))
| TyKind::OpaqueType(id, subst) => {
Copy link
Member Author

@ShoyuVanilla ShoyuVanilla Sep 8, 2025

Choose a reason for hiding this comment

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

rustc_type_ir::TyKind doesn't have TyKind::OpaqueType and we are converting chalk_ir::TyKind::OpaqueType into rustc_type_ir::TyKind::Alias(Opaque) and back into chalk_ir::TyKind::Alias(Opaque) so we need to handle these cases as well 🥲

}
TyKind::AssociatedType(_, _)
| TyKind::OpaqueType(_, _)
| TyKind::Alias(AliasTy::Opaque(_))
Copy link
Member Author

Choose a reason for hiding this comment

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

ImplTraitId::AsyncBlockTypeImplTrait(..) => unreachable!(),
ImplTraitId::AsyncBlockTypeImplTrait(def, _) => {
return handle_async_block_type_impl_trait(def);
}
Copy link
Member Author

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

In general, we are inserting less inference vars in next-solver inference table so iv indices has been changed

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not sure these are regressions or improvements but seems that we are now leaving projection types of unknown selves as it is, rather than creating new unknown for them

Copy link
Contributor

Choose a reason for hiding this comment

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

Probably because of lazy normalization of the new solver, we only normalize at the end and to the same projection (can't tell why it's different from Chalk though). Not a problem I believe.

56..58 '{}': ()
72..171 '{ ... x); }': ()
78..81 'foo': fn foo<&'? (i32, &'? str), i32, impl FnOnce(&'? (i32, &'? str)) -> i32>(&'? (i32, &'? str), impl FnOnce(&'? (i32, &'? str)) -> i32) -> i32
78..81 'foo': fn foo<&'? (i32, &'static str), i32, impl FnOnce(&'? (i32, &'static str)) -> i32>(&'? (i32, &'static str), impl FnOnce(&'? (i32, &'static str)) -> i32) -> i32
Copy link
Member Author

Choose a reason for hiding this comment

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

Lifetime labels has been improved in some places and regressed in other places 🤔

fn test<T>(t: T) where T: UnificationStoreMut {
fn test<T>(mut t: T) where T: UnificationStoreMut {
let x;
Copy link
Member Author

Choose a reason for hiding this comment

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

This and the above changes are fixing compilation errors on the test code and are necessary to make the type inference work. Next-solver (or this migration) is less forgiving to some errors 😅

Copy link
Contributor

Choose a reason for hiding this comment

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

That's quite unfortunate but I don't think that's something we can change.

Copy link
Contributor

Choose a reason for hiding this comment

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

Although... How can the mutness of a local impact inference?

Copy link
Member Author

@ShoyuVanilla ShoyuVanilla Sep 9, 2025

Choose a reason for hiding this comment

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

Oh, mut wasn't necessary, but the above type param usize was. Seems that I just mindlessly fixed all compilation errors on this 😅

fn main() {
run(f()) // FIXME: remove this error
//^^^ error: expected Rate<5>, found Rate<_>
run(f())
Copy link
Member Author

@ShoyuVanilla ShoyuVanilla Sep 8, 2025

Choose a reason for hiding this comment

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

Ideally, we should emit an error on const param N mismatch above while emitting no error here but the former should be done in wfcheck we are currently lacking of in rust-analyzer

Copy link
Contributor

Choose a reason for hiding this comment

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

Why is it a well-formedness check? It seems to be a normal type mismatch. Anyway we should not emit an error here, no idea why we did with Chalk.

Copy link
Member Author

Choose a reason for hiding this comment

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

I mean, wfcheck should emit an error on L523 and of course we should not emit an error here as fixed in this PR. I'm quite surprised that chalk has been emitting an error here 😅

@ChayimFriedman2
Copy link
Contributor

You seem to be using structurally_resolve_type() as a replacement for resolve_ty_shallow(), but this method deeply resolve vars, so it seem to not be a correct replacement. Why are you using it?

@ShoyuVanilla
Copy link
Member Author

You seem to be using structurally_resolve_type() as a replacement for resolve_ty_shallow(), but this method deeply resolve vars, so it seem to not be a correct replacement. Why are you using it?

Because rustc does so in myriad places, especially with next-solver, like
https://github.com/rust-lang/rust/blob/68baa87ba6f03f8b6af2a368690161f1601e4040/compiler/rustc_hir_typeck/src/coercion.rs#L1039
https://github.com/search?q=repo%3Arust-lang%2Frust%20structurally_resolve_ty&type=code

As I remember, last time I looked into rustc_hir_typeck before it used mostly shallow_resolve_ty but I was quite surprised that most of them has been replaced by this when I looked into it again to get hints for fixing type inference failures caused by removing the above hack on #20578 (comment)

Copy link
Contributor

@ChayimFriedman2 ChayimFriedman2 left a comment

Choose a reason for hiding this comment

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

I have a few comments, but otherwise LGTM.

}

pub fn next_ty_var_with_origin(&self, origin: TypeVariableOrigin) -> Ty<'db> {
let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin);
let vid = self.next_ty_vid();

Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, wait. We should pass origin here.

}

pub fn next_const_var_with_origin(&self, origin: ConstVariableOrigin) -> Const<'db> {
let vid = self
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here.

Copy link
Member Author

Choose a reason for hiding this comment

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

Here's origin too

fn main() {
run(f()) // FIXME: remove this error
//^^^ error: expected Rate<5>, found Rate<_>
run(f())
Copy link
Contributor

Choose a reason for hiding this comment

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

Why is it a well-formedness check? It seems to be a normal type mismatch. Anyway we should not emit an error here, no idea why we did with Chalk.

pub(crate) struct InferenceTable<'a> {
pub(crate) db: &'a dyn HirDatabase,
pub(crate) interner: DbInterner<'a>,
pub(crate) trait_env: Arc<TraitEnvironment>,
Copy link
Contributor

Choose a reason for hiding this comment

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

We will eventually need to replace TraitEnvironment with ParamEnv, but not in this PR.

@ShoyuVanilla
Copy link
Member Author

I have a few comments, but otherwise LGTM.

Thanks for the thorough review! I’ll take care of the fixes tomorrow once I’m back from work.

Copy link
Contributor

@ChayimFriedman2 ChayimFriedman2 left a comment

Choose a reason for hiding this comment

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

Extremely excited for this!

pub(super) fn clauses_for_self_ty(
&mut self,
self_ty: InferenceVar,
) -> SmallVec<[WhereClause; 4]> {
Copy link
Member Author

Choose a reason for hiding this comment

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

It would be better to return rustc_type_ir's predicates here but the callsite applies elaboration(in our codebase) to the output and I'm suspecting that current Elaboratable implementation in our next-solver that called from rustc_type_ir is a bit buggy and that might be the cause of #20582. Will fix it after this PR

@ChayimFriedman2 ChayimFriedman2 added this pull request to the merge queue Sep 9, 2025
Merged via the queue into rust-lang:master with commit 412932e Sep 9, 2025
15 checks passed
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Sep 9, 2025
@ShoyuVanilla ShoyuVanilla deleted the inftbl branch September 9, 2025 17:09
@lnicola
Copy link
Member

lnicola commented Oct 14, 2025

changelog fixup #20329

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants