-
Notifications
You must be signed in to change notification settings - Fork 1.9k
fix: Panic while canonicalizing erroneous projection type #17882
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
2b18567 to
84c562f
Compare
84c562f to
8bcaba1
Compare
I'm trying to remember their names, but there were a few arena crates whose keys tracked its creator's provenance. Do you think an approach like this would help here? |
I think that this PR is just a mitigation and what you said is the "correct" way to fix this, maybe. |
|
Yes, sorry! Lemme merge this. @bors r+ I'll make a new issue. |
fix: Panic while canonicalizing erroneous projection type Fixes #17866 The root cause of #17866 is quite horrifyng 😨 ```rust trait T { type A; } type Foo = <S as T>::A; // note that S isn't defined fn main() { Foo {} } ``` While inferencing alias type `Foo = <S as T>::A`; https://github.com/rust-lang/rust-analyzer/blob/78c2bdce860dbd996a8083224d01a96660dd6a15/crates/hir-ty/src/infer.rs#L1388-L1398 the error type `S` in it is substituted by inference var in L1396 above as below; https://github.com/rust-lang/rust-analyzer/blob/78c2bdce860dbd996a8083224d01a96660dd6a15/crates/hir-ty/src/infer/unify.rs#L866-L869 This new inference var's index is `1`, as the type inferecing procedure here previously inserted another inference var into same `InferenceTable`. But after that, the projection type made from the above then passed to the following function; https://github.com/rust-lang/rust-analyzer/blob/78c2bdce860dbd996a8083224d01a96660dd6a15/crates/hir-ty/src/traits.rs#L88-L96 here, a whole new `InferenceTable` is made, without any inference var and in the L94, this table calls; https://github.com/rust-lang/rust-analyzer/blob/78c2bdce860dbd996a8083224d01a96660dd6a15/crates/hir-ty/src/infer/unify.rs#L364-L370 And while registering `AliasEq` `obligation`, this obligation contains inference var `?1` made from the previous table, but this table has only one inference var `?0` made at L365. So, the chalk panics when we try to canonicalize that obligation to register it, because the obligation contains an inference var `?1` that the canonicalizing table doesn't have. Currently, we are calling `InferenceTable::new()` to do some normalizing, unifying or coercing things to some targets that might contain inference var that the new table doesn't have. I think that this is quite dangerous footgun because the inference var is just an index that does not contain the information which table does it made from, so sometimes this "foreign" index might cause panic like this case, or point at the wrong variable. This PR mitigates such behaviour simply by inserting sufficient number of inference vars to new table to avoid such problem. This strategy doesn't harm current r-a's intention because the inference vars that passed into new tables are just "unresolved" variables in current r-a, so this is just making sure that such "unresolved" variables exist in the new table
|
On the other side, apparently this |
|
@bors r- |
Sorry about that! |
|
A bit too slow in writing my comment 😅 |
|
Okay, I'll rethink about this tommorrow(it's past midnight here 😅) |
|
☀️ Try build successful - checks-actions |
Basically, a type that still contains inference vars "crossing over" to a new inference table is always a bug. Most of the places you changed already take a |
It no longer matters, but I think they're called generational indexes or arenas. |
8bcaba1 to
b01360c
Compare
I was too obssesed with the projection type containing ivs and learned a lot from your comments. Thanks 😄 |
flodiebold
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.
@bors delegate+
crates/hir-ty/src/infer.rs
Outdated
| TyKind::Alias(AliasTy::Projection(proj_ty)) => { | ||
| self.db.normalize_projection(proj_ty.clone(), self.table.trait_env.clone()) | ||
| let ty = self.table.normalize_projection_ty(proj_ty.clone()); | ||
| self.table.resolve_completely(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.
resolve_completely is the wrong thing to use during inference since it applies fallback. I think this should just be resolve_ty_shallow
crates/hir-ty/src/traits.rs
Outdated
| struct InferenceVarVisitor(Option<()>); | ||
| impl TypeVisitor<Interner> for InferenceVarVisitor { | ||
| type BreakTy = (); | ||
|
|
||
| fn as_dyn(&mut self) -> &mut dyn TypeVisitor<Interner, BreakTy = Self::BreakTy> { | ||
| self | ||
| } | ||
|
|
||
| fn interner(&self) -> Interner { | ||
| Interner | ||
| } | ||
|
|
||
| fn visit_inference_var( | ||
| &mut self, | ||
| _var: chalk_ir::InferenceVar, | ||
| _outer_binder: DebruijnIndex, | ||
| ) -> std::ops::ControlFlow<Self::BreakTy> { | ||
| self.0 = Some(()); | ||
| std::ops::ControlFlow::Break(()) | ||
| } | ||
| } | ||
|
|
||
| let mut visitor = InferenceVarVisitor(None); | ||
| projection.visit_with(visitor.as_dyn(), DebruijnIndex::INNERMOST); | ||
| if visitor.0.is_some() { |
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 could be a function has_inference_variables. Also, the flags in a Ty already store whether it contains any inference variable, so it could be easier to use that :)
b01360c to
e6d8970
Compare
|
@bors r+ |
|
@ShoyuVanilla: 🔑 Insufficient privileges: Not in reviewers |
|
@bors r+ |
|
☀️ Test successful - checks-actions |
Fixes #17866
The root cause of #17866 is quite horrifyng 😨
While inferencing alias type
Foo = <S as T>::A;rust-analyzer/crates/hir-ty/src/infer.rs
Lines 1388 to 1398 in 78c2bdc
the error type
Sin it is substituted by inference var in L1396 above as below;rust-analyzer/crates/hir-ty/src/infer/unify.rs
Lines 866 to 869 in 78c2bdc
This new inference var's index is
1, as the type inferecing procedure here previously inserted another inference var into sameInferenceTable.But after that, the projection type made from the above then passed to the following function;
rust-analyzer/crates/hir-ty/src/traits.rs
Lines 88 to 96 in 78c2bdc
here, a whole new
InferenceTableis made, without any inference var and in the L94, this table calls;rust-analyzer/crates/hir-ty/src/infer/unify.rs
Lines 364 to 370 in 78c2bdc
And while registering
AliasEqobligation, this obligation contains inference var?1made from the previous table, but this table has only one inference var?0made at L365.So, the chalk panics when we try to canonicalize that obligation to register it, because the obligation contains an inference var
?1that the canonicalizing table doesn't have.Currently, we are calling
InferenceTable::new()to do some normalizing, unifying or coercing things to some targets that might contain inference var that the new table doesn't have.I think that this is quite dangerous footgun because the inference var is just an index that does not contain the information which table does it made from, so sometimes this "foreign" index might cause panic like this case, or point at the wrong variable.
This PR mitigates such behaviour simply by inserting sufficient number of inference vars to new table to avoid such problem.
This strategy doesn't harm current r-a's intention because the inference vars that passed into new tables are just "unresolved" variables in current r-a, so this is just making sure that such "unresolved" variables exist in the new table