Skip to content

Commit 89cd585

Browse files
committed
Auto merge of rust-lang#17882 - ShoyuVanilla:issue-17866, r=lnicola
fix: Panic while canonicalizing erroneous projection type Fixes rust-lang#17866 The root cause of rust-lang#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
2 parents e2f2e73 + 81b68b2 commit 89cd585

File tree

3 files changed

+35
-3
lines changed

3 files changed

+35
-3
lines changed

src/tools/rust-analyzer/crates/hir-ty/src/infer.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1436,7 +1436,8 @@ impl<'a> InferenceContext<'a> {
14361436
let remaining = unresolved.map(|it| path.segments()[it..].len()).filter(|it| it > &0);
14371437
let ty = match ty.kind(Interner) {
14381438
TyKind::Alias(AliasTy::Projection(proj_ty)) => {
1439-
self.db.normalize_projection(proj_ty.clone(), self.table.trait_env.clone())
1439+
let ty = self.table.normalize_projection_ty(proj_ty.clone());
1440+
self.table.resolve_ty_shallow(&ty)
14401441
}
14411442
_ => ty,
14421443
};

src/tools/rust-analyzer/crates/hir-ty/src/tests/regression.rs

+21
Original file line numberDiff line numberDiff line change
@@ -2141,3 +2141,24 @@ fn test() {
21412141
}"#,
21422142
);
21432143
}
2144+
2145+
#[test]
2146+
fn issue_17866() {
2147+
check_infer(
2148+
r#"
2149+
trait T {
2150+
type A;
2151+
}
2152+
2153+
type Foo = <S as T>::A;
2154+
2155+
fn main() {
2156+
Foo {};
2157+
}
2158+
"#,
2159+
expect![[r#"
2160+
60..75 '{ Foo {}; }': ()
2161+
66..72 'Foo {}': {unknown}
2162+
"#]],
2163+
);
2164+
}

src/tools/rust-analyzer/crates/hir-ty/src/traits.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ use hir_def::{
1414
};
1515
use hir_expand::name::Name;
1616
use intern::sym;
17-
use stdx::panic_context;
17+
use stdx::{never, panic_context};
1818
use triomphe::Arc;
1919

2020
use crate::{
2121
db::HirDatabase, infer::unify::InferenceTable, utils::UnevaluatedConstEvaluatorFolder, AliasEq,
2222
AliasTy, Canonical, DomainGoal, Goal, Guidance, InEnvironment, Interner, ProjectionTy,
23-
ProjectionTyExt, Solution, TraitRefExt, Ty, TyKind, WhereClause,
23+
ProjectionTyExt, Solution, TraitRefExt, Ty, TyKind, TypeFlags, WhereClause,
2424
};
2525

2626
/// This controls how much 'time' we give the Chalk solver before giving up.
@@ -90,6 +90,16 @@ pub(crate) fn normalize_projection_query(
9090
projection: ProjectionTy,
9191
env: Arc<TraitEnvironment>,
9292
) -> Ty {
93+
if projection.substitution.iter(Interner).any(|arg| {
94+
arg.ty(Interner)
95+
.is_some_and(|ty| ty.data(Interner).flags.intersects(TypeFlags::HAS_TY_INFER))
96+
}) {
97+
never!(
98+
"Invoking `normalize_projection_query` with a projection type containing inference var"
99+
);
100+
return TyKind::Error.intern(Interner);
101+
}
102+
93103
let mut table = InferenceTable::new(db, env);
94104
let ty = table.normalize_projection_ty(projection);
95105
table.resolve_completely(ty)

0 commit comments

Comments
 (0)