mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 13:33:31 +00:00
Auto merge of #17924 - ShoyuVanilla:issue-17921, r=Veykril
fix: Panic when a TAIT exists in a RPIT
Fixes #17921
When there is a TAIT inside of a RPIT like;
```rust
trait Foo {}
type Bar = impl Foo;
fn foo<A>() -> impl Future<Output = Bar> { .. }
```
while inferencing `fn foo`, `insert_inference_vars_for_impl_trait` tries to substitute impl trait bounds of `Bar`, i.e. `Implemented(Foo)` with RPITs `placeholders`, and this causes panic
fa00326247/crates/hir-ty/src/infer.rs (L903-L905)
This commit is contained in:
commit
644617361d
2 changed files with 85 additions and 11 deletions
|
@ -604,6 +604,11 @@ fn find_continuable(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ImplTraitReplacingMode {
|
||||||
|
ReturnPosition(FxHashSet<Ty>),
|
||||||
|
TypeAlias,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> InferenceContext<'a> {
|
impl<'a> InferenceContext<'a> {
|
||||||
fn new(
|
fn new(
|
||||||
db: &'a dyn HirDatabase,
|
db: &'a dyn HirDatabase,
|
||||||
|
@ -825,13 +830,19 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.write_binding_ty(self_param, ty);
|
self.write_binding_ty(self_param, ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut params_and_ret_tys = Vec::new();
|
let mut tait_candidates = FxHashSet::default();
|
||||||
for (ty, pat) in param_tys.zip(&*self.body.params) {
|
for (ty, pat) in param_tys.zip(&*self.body.params) {
|
||||||
let ty = self.insert_type_vars(ty);
|
let ty = self.insert_type_vars(ty);
|
||||||
let ty = self.normalize_associated_types_in(ty);
|
let ty = self.normalize_associated_types_in(ty);
|
||||||
|
|
||||||
self.infer_top_pat(*pat, &ty);
|
self.infer_top_pat(*pat, &ty);
|
||||||
params_and_ret_tys.push(ty);
|
if ty
|
||||||
|
.data(Interner)
|
||||||
|
.flags
|
||||||
|
.intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER))
|
||||||
|
{
|
||||||
|
tait_candidates.insert(ty);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let return_ty = &*data.ret_type;
|
let return_ty = &*data.ret_type;
|
||||||
|
|
||||||
|
@ -844,7 +855,12 @@ impl<'a> InferenceContext<'a> {
|
||||||
let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
|
let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
|
||||||
// RPIT opaque types use substitution of their parent function.
|
// RPIT opaque types use substitution of their parent function.
|
||||||
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
|
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
|
||||||
let result = self.insert_inference_vars_for_impl_trait(return_ty, fn_placeholders);
|
let mut mode = ImplTraitReplacingMode::ReturnPosition(FxHashSet::default());
|
||||||
|
let result =
|
||||||
|
self.insert_inference_vars_for_impl_trait(return_ty, fn_placeholders, &mut mode);
|
||||||
|
if let ImplTraitReplacingMode::ReturnPosition(taits) = mode {
|
||||||
|
tait_candidates.extend(taits);
|
||||||
|
}
|
||||||
let rpits = rpits.skip_binders();
|
let rpits = rpits.skip_binders();
|
||||||
for (id, _) in rpits.impl_traits.iter() {
|
for (id, _) in rpits.impl_traits.iter() {
|
||||||
if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
|
if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
|
||||||
|
@ -863,11 +879,23 @@ impl<'a> InferenceContext<'a> {
|
||||||
// Functions might be defining usage sites of TAITs.
|
// Functions might be defining usage sites of TAITs.
|
||||||
// To define an TAITs, that TAIT must appear in the function's signatures.
|
// To define an TAITs, that TAIT must appear in the function's signatures.
|
||||||
// So, it suffices to check for params and return types.
|
// So, it suffices to check for params and return types.
|
||||||
params_and_ret_tys.push(self.return_ty.clone());
|
if self
|
||||||
self.make_tait_coercion_table(params_and_ret_tys.iter());
|
.return_ty
|
||||||
|
.data(Interner)
|
||||||
|
.flags
|
||||||
|
.intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER))
|
||||||
|
{
|
||||||
|
tait_candidates.insert(self.return_ty.clone());
|
||||||
|
}
|
||||||
|
self.make_tait_coercion_table(tait_candidates.iter());
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_inference_vars_for_impl_trait<T>(&mut self, t: T, placeholders: Substitution) -> T
|
fn insert_inference_vars_for_impl_trait<T>(
|
||||||
|
&mut self,
|
||||||
|
t: T,
|
||||||
|
placeholders: Substitution,
|
||||||
|
mode: &mut ImplTraitReplacingMode,
|
||||||
|
) -> T
|
||||||
where
|
where
|
||||||
T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
|
T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
|
||||||
{
|
{
|
||||||
|
@ -880,10 +908,31 @@ impl<'a> InferenceContext<'a> {
|
||||||
};
|
};
|
||||||
let (impl_traits, idx) =
|
let (impl_traits, idx) =
|
||||||
match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
|
match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
|
||||||
|
// We don't replace opaque types from other kind with inference vars
|
||||||
|
// because `insert_inference_vars_for_impl_traits` for each kinds
|
||||||
|
// and unreplaced opaque types of other kind are resolved while
|
||||||
|
// inferencing because of `tait_coercion_table`.
|
||||||
|
// Moreover, calling `insert_inference_vars_for_impl_traits` with same
|
||||||
|
// `placeholders` for other kind may cause trouble because
|
||||||
|
// the substs for the bounds of each impl traits do not match
|
||||||
ImplTraitId::ReturnTypeImplTrait(def, idx) => {
|
ImplTraitId::ReturnTypeImplTrait(def, idx) => {
|
||||||
|
if matches!(mode, ImplTraitReplacingMode::TypeAlias) {
|
||||||
|
// RPITs don't have `tait_coercion_table`, so use inserted inference
|
||||||
|
// vars for them.
|
||||||
|
if let Some(ty) = self.result.type_of_rpit.get(idx) {
|
||||||
|
return ty.clone();
|
||||||
|
}
|
||||||
|
return ty;
|
||||||
|
}
|
||||||
(self.db.return_type_impl_traits(def), idx)
|
(self.db.return_type_impl_traits(def), idx)
|
||||||
}
|
}
|
||||||
ImplTraitId::TypeAliasImplTrait(def, idx) => {
|
ImplTraitId::TypeAliasImplTrait(def, idx) => {
|
||||||
|
if let ImplTraitReplacingMode::ReturnPosition(taits) = mode {
|
||||||
|
// Gather TAITs while replacing RPITs because TAITs inside RPITs
|
||||||
|
// may not visited while replacing TAITs
|
||||||
|
taits.insert(ty.clone());
|
||||||
|
return ty;
|
||||||
|
}
|
||||||
(self.db.type_alias_impl_traits(def), idx)
|
(self.db.type_alias_impl_traits(def), idx)
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
|
@ -892,16 +941,20 @@ impl<'a> InferenceContext<'a> {
|
||||||
return ty;
|
return ty;
|
||||||
};
|
};
|
||||||
let bounds = (*impl_traits)
|
let bounds = (*impl_traits)
|
||||||
.map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.iter()));
|
.map_ref(|its| its.impl_traits[idx].bounds.map_ref(|it| it.iter()));
|
||||||
let var = self.table.new_type_var();
|
let var = self.table.new_type_var();
|
||||||
let var_subst = Substitution::from1(Interner, var.clone());
|
let var_subst = Substitution::from1(Interner, var.clone());
|
||||||
for bound in bounds {
|
for bound in bounds {
|
||||||
let predicate = bound.map(|it| it.cloned()).substitute(Interner, &placeholders);
|
let predicate = bound.map(|it| it.cloned());
|
||||||
|
let predicate = predicate.substitute(Interner, &placeholders);
|
||||||
let (var_predicate, binders) =
|
let (var_predicate, binders) =
|
||||||
predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
|
predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
|
||||||
always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
|
always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
|
||||||
let var_predicate = self
|
let var_predicate = self.insert_inference_vars_for_impl_trait(
|
||||||
.insert_inference_vars_for_impl_trait(var_predicate, placeholders.clone());
|
var_predicate,
|
||||||
|
placeholders.clone(),
|
||||||
|
mode,
|
||||||
|
);
|
||||||
self.push_obligation(var_predicate.cast(Interner));
|
self.push_obligation(var_predicate.cast(Interner));
|
||||||
}
|
}
|
||||||
self.result.type_of_rpit.insert(idx, var.clone());
|
self.result.type_of_rpit.insert(idx, var.clone());
|
||||||
|
@ -1038,7 +1091,11 @@ impl<'a> InferenceContext<'a> {
|
||||||
self.db.lookup_intern_impl_trait_id(id.into())
|
self.db.lookup_intern_impl_trait_id(id.into())
|
||||||
{
|
{
|
||||||
let subst = TyBuilder::placeholder_subst(self.db, alias_id);
|
let subst = TyBuilder::placeholder_subst(self.db, alias_id);
|
||||||
let ty = self.insert_inference_vars_for_impl_trait(ty, subst);
|
let ty = self.insert_inference_vars_for_impl_trait(
|
||||||
|
ty,
|
||||||
|
subst,
|
||||||
|
&mut ImplTraitReplacingMode::TypeAlias,
|
||||||
|
);
|
||||||
Some((id, ty))
|
Some((id, ty))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
|
@ -2211,3 +2211,20 @@ fn f() -> Foo {}
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn issue_17921() {
|
||||||
|
check_infer(
|
||||||
|
r#"
|
||||||
|
//- minicore: future
|
||||||
|
trait Foo {}
|
||||||
|
type Bar = impl Foo;
|
||||||
|
|
||||||
|
async fn f<A, B, C>() -> Bar {}
|
||||||
|
"#,
|
||||||
|
expect![[r#"
|
||||||
|
64..66 '{}': ()
|
||||||
|
64..66 '{}': impl Future<Output = ()>
|
||||||
|
"#]],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue