Skip to content

Commit f9c0c8a

Browse files
committed
Auto merge of rust-lang#17924 - ShoyuVanilla:issue-17921, r=Veykril
fix: Panic when a TAIT exists in a RPIT Fixes rust-lang#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 https://github.com/rust-lang/rust-analyzer/blob/fa003262474185fd62168379500fe906b331824b/crates/hir-ty/src/infer.rs#L903-L905
2 parents f17e9a0 + ea12d79 commit f9c0c8a

File tree

2 files changed

+85
-11
lines changed

2 files changed

+85
-11
lines changed

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

Lines changed: 68 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,11 @@ fn find_continuable(
604604
}
605605
}
606606

607+
enum ImplTraitReplacingMode {
608+
ReturnPosition(FxHashSet<Ty>),
609+
TypeAlias,
610+
}
611+
607612
impl<'a> InferenceContext<'a> {
608613
fn new(
609614
db: &'a dyn HirDatabase,
@@ -825,13 +830,19 @@ impl<'a> InferenceContext<'a> {
825830
self.write_binding_ty(self_param, ty);
826831
}
827832
}
828-
let mut params_and_ret_tys = Vec::new();
833+
let mut tait_candidates = FxHashSet::default();
829834
for (ty, pat) in param_tys.zip(&*self.body.params) {
830835
let ty = self.insert_type_vars(ty);
831836
let ty = self.normalize_associated_types_in(ty);
832837

833838
self.infer_top_pat(*pat, &ty);
834-
params_and_ret_tys.push(ty);
839+
if ty
840+
.data(Interner)
841+
.flags
842+
.intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER))
843+
{
844+
tait_candidates.insert(ty);
845+
}
835846
}
836847
let return_ty = &*data.ret_type;
837848

@@ -844,7 +855,12 @@ impl<'a> InferenceContext<'a> {
844855
let return_ty = if let Some(rpits) = self.db.return_type_impl_traits(func) {
845856
// RPIT opaque types use substitution of their parent function.
846857
let fn_placeholders = TyBuilder::placeholder_subst(self.db, func);
847-
let result = self.insert_inference_vars_for_impl_trait(return_ty, fn_placeholders);
858+
let mut mode = ImplTraitReplacingMode::ReturnPosition(FxHashSet::default());
859+
let result =
860+
self.insert_inference_vars_for_impl_trait(return_ty, fn_placeholders, &mut mode);
861+
if let ImplTraitReplacingMode::ReturnPosition(taits) = mode {
862+
tait_candidates.extend(taits);
863+
}
848864
let rpits = rpits.skip_binders();
849865
for (id, _) in rpits.impl_traits.iter() {
850866
if let Entry::Vacant(e) = self.result.type_of_rpit.entry(id) {
@@ -863,11 +879,23 @@ impl<'a> InferenceContext<'a> {
863879
// Functions might be defining usage sites of TAITs.
864880
// To define an TAITs, that TAIT must appear in the function's signatures.
865881
// So, it suffices to check for params and return types.
866-
params_and_ret_tys.push(self.return_ty.clone());
867-
self.make_tait_coercion_table(params_and_ret_tys.iter());
882+
if self
883+
.return_ty
884+
.data(Interner)
885+
.flags
886+
.intersects(TypeFlags::HAS_TY_OPAQUE.union(TypeFlags::HAS_TY_INFER))
887+
{
888+
tait_candidates.insert(self.return_ty.clone());
889+
}
890+
self.make_tait_coercion_table(tait_candidates.iter());
868891
}
869892

870-
fn insert_inference_vars_for_impl_trait<T>(&mut self, t: T, placeholders: Substitution) -> T
893+
fn insert_inference_vars_for_impl_trait<T>(
894+
&mut self,
895+
t: T,
896+
placeholders: Substitution,
897+
mode: &mut ImplTraitReplacingMode,
898+
) -> T
871899
where
872900
T: crate::HasInterner<Interner = Interner> + crate::TypeFoldable<Interner>,
873901
{
@@ -880,10 +908,31 @@ impl<'a> InferenceContext<'a> {
880908
};
881909
let (impl_traits, idx) =
882910
match self.db.lookup_intern_impl_trait_id(opaque_ty_id.into()) {
911+
// We don't replace opaque types from other kind with inference vars
912+
// because `insert_inference_vars_for_impl_traits` for each kinds
913+
// and unreplaced opaque types of other kind are resolved while
914+
// inferencing because of `tait_coercion_table`.
915+
// Moreover, calling `insert_inference_vars_for_impl_traits` with same
916+
// `placeholders` for other kind may cause trouble because
917+
// the substs for the bounds of each impl traits do not match
883918
ImplTraitId::ReturnTypeImplTrait(def, idx) => {
919+
if matches!(mode, ImplTraitReplacingMode::TypeAlias) {
920+
// RPITs don't have `tait_coercion_table`, so use inserted inference
921+
// vars for them.
922+
if let Some(ty) = self.result.type_of_rpit.get(idx) {
923+
return ty.clone();
924+
}
925+
return ty;
926+
}
884927
(self.db.return_type_impl_traits(def), idx)
885928
}
886929
ImplTraitId::TypeAliasImplTrait(def, idx) => {
930+
if let ImplTraitReplacingMode::ReturnPosition(taits) = mode {
931+
// Gather TAITs while replacing RPITs because TAITs inside RPITs
932+
// may not visited while replacing TAITs
933+
taits.insert(ty.clone());
934+
return ty;
935+
}
887936
(self.db.type_alias_impl_traits(def), idx)
888937
}
889938
_ => unreachable!(),
@@ -892,16 +941,20 @@ impl<'a> InferenceContext<'a> {
892941
return ty;
893942
};
894943
let bounds = (*impl_traits)
895-
.map_ref(|rpits| rpits.impl_traits[idx].bounds.map_ref(|it| it.iter()));
944+
.map_ref(|its| its.impl_traits[idx].bounds.map_ref(|it| it.iter()));
896945
let var = self.table.new_type_var();
897946
let var_subst = Substitution::from1(Interner, var.clone());
898947
for bound in bounds {
899-
let predicate = bound.map(|it| it.cloned()).substitute(Interner, &placeholders);
948+
let predicate = bound.map(|it| it.cloned());
949+
let predicate = predicate.substitute(Interner, &placeholders);
900950
let (var_predicate, binders) =
901951
predicate.substitute(Interner, &var_subst).into_value_and_skipped_binders();
902952
always!(binders.is_empty(Interner)); // quantified where clauses not yet handled
903-
let var_predicate = self
904-
.insert_inference_vars_for_impl_trait(var_predicate, placeholders.clone());
953+
let var_predicate = self.insert_inference_vars_for_impl_trait(
954+
var_predicate,
955+
placeholders.clone(),
956+
mode,
957+
);
905958
self.push_obligation(var_predicate.cast(Interner));
906959
}
907960
self.result.type_of_rpit.insert(idx, var.clone());
@@ -1038,7 +1091,11 @@ impl<'a> InferenceContext<'a> {
10381091
self.db.lookup_intern_impl_trait_id(id.into())
10391092
{
10401093
let subst = TyBuilder::placeholder_subst(self.db, alias_id);
1041-
let ty = self.insert_inference_vars_for_impl_trait(ty, subst);
1094+
let ty = self.insert_inference_vars_for_impl_trait(
1095+
ty,
1096+
subst,
1097+
&mut ImplTraitReplacingMode::TypeAlias,
1098+
);
10421099
Some((id, ty))
10431100
} else {
10441101
None

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2211,3 +2211,20 @@ fn f() -> Foo {}
22112211
"#]],
22122212
);
22132213
}
2214+
2215+
#[test]
2216+
fn issue_17921() {
2217+
check_infer(
2218+
r#"
2219+
//- minicore: future
2220+
trait Foo {}
2221+
type Bar = impl Foo;
2222+
2223+
async fn f<A, B, C>() -> Bar {}
2224+
"#,
2225+
expect![[r#"
2226+
64..66 '{}': ()
2227+
64..66 '{}': impl Future<Output = ()>
2228+
"#]],
2229+
);
2230+
}

0 commit comments

Comments
 (0)