-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Migrate InferenceTable into next-solver
#20578
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
That seems especially grievous given that unlike Chalk, the next solver gives us precise control on what is in 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. |
|
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; |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 😅
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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>, |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
crates/hir-ty/src/infer/unify.rs
Outdated
|
|
||
| #[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) |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just found what was on my mind.
So the following is the (sparse) call stacks made while coercing opaque/projection type in next-solver
At the last link, it just registers delayed obligation and returns Ok
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 😄
There was a problem hiding this comment.
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
ae0cbc7 to
089ef7e
Compare
| self.unify(&builtin_ret, &ret_ty); | ||
| builtin_ret | ||
| } else { | ||
| ret_ty |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This corresponds with https://github.com/rust-lang/rust/blob/23718020b12d7e5a54f82a19910e8356ee719667/compiler/rustc_hir_typeck/src/op.rs#L150-L152 and is quite crucial for type inferences around builtin binops
| opaque_ty_id: id, | ||
| substitution: subst, | ||
| })) | ||
| | TyKind::OpaqueType(id, subst) => { |
There was a problem hiding this comment.
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(_)) |
There was a problem hiding this comment.
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); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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 😅
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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()) |
There was a problem hiding this comment.
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
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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 😅
|
You seem to be using |
Because rustc does so in myriad places, especially with next-solver, like As I remember, last time I looked into |
ChayimFriedman2
left a comment
There was a problem hiding this 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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| let vid = self.inner.borrow_mut().type_variables().new_var(self.universe(), origin); | |
| let vid = self.next_ty_vid(); |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here.
There was a problem hiding this comment.
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()) |
There was a problem hiding this comment.
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>, |
There was a problem hiding this comment.
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.
Thanks for the thorough review! I’ll take care of the fixes tomorrow once I’m back from work. |
ChayimFriedman2
left a comment
There was a problem hiding this 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]> { |
There was a problem hiding this comment.
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
|
changelog fixup #20329 |
Still work in progress and I have 4 remaining failing tests
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
PlaceholderTycrimes: We are interning its parent generic and local idx and then using that intern id intoPlaceholder's index, quite an abuse.So, next-gen solver is panicking while trying to instantiate canonicals containing that
PlaceholderTyin such a manner like trying to access the tens of thousands index of original values.I'm trying to mitigate it utilizing
BoundTykindto discern those "abused" placeholders and normal placeholders but it still failing with something like: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 😄