Skip to content

Commit b5b4a1f

Browse files
Merge #8692
8692: Fix panic caused by new Try trait definition r=flodiebold a=flodiebold The new Try trait definition caused a query cycle for us. This adds recovery for that cycle, but also fixes the cause, which is that we went through the supertraits when resolving `<T as Trait>::Assoc`, which isn't actually necessary. I also rewrote `all_super_trait_refs` to an iterator before I realized what the actual problem was, so I kept that. Fixes #8686. Co-authored-by: Florian Diebold <[email protected]>
2 parents 6ea91a4 + c2aefd5 commit b5b4a1f

File tree

4 files changed

+100
-25
lines changed

4 files changed

+100
-25
lines changed

crates/hir_ty/src/db.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
7070
fn trait_environment(&self, def: GenericDefId) -> Arc<crate::TraitEnvironment>;
7171

7272
#[salsa::invoke(crate::lower::generic_defaults_query)]
73+
#[salsa::cycle(crate::lower::generic_defaults_recover)]
7374
fn generic_defaults(&self, def: GenericDefId) -> Arc<[Binders<Ty>]>;
7475

7576
#[salsa::invoke(InherentImpls::inherent_impls_in_crate_query)]

crates/hir_ty/src/lower.rs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -414,17 +414,16 @@ impl<'a> TyLoweringContext<'a> {
414414
self.lower_trait_ref_from_resolved_path(trait_, resolved_segment, self_ty);
415415
let ty = if remaining_segments.len() == 1 {
416416
let segment = remaining_segments.first().unwrap();
417-
let found = associated_type_by_name_including_super_traits(
418-
self.db,
419-
trait_ref,
420-
&segment.name,
421-
);
417+
let found = self
418+
.db
419+
.trait_data(trait_ref.hir_trait_id())
420+
.associated_type_by_name(&segment.name);
422421
match found {
423-
Some((super_trait_ref, associated_ty)) => {
422+
Some(associated_ty) => {
424423
// FIXME handle type parameters on the segment
425424
TyKind::Alias(AliasTy::Projection(ProjectionTy {
426425
associated_ty_id: to_assoc_type_id(associated_ty),
427-
substitution: super_trait_ref.substitution,
426+
substitution: trait_ref.substitution,
428427
}))
429428
.intern(&Interner)
430429
}
@@ -1089,6 +1088,27 @@ pub(crate) fn generic_defaults_query(
10891088
defaults
10901089
}
10911090

1091+
pub(crate) fn generic_defaults_recover(
1092+
db: &dyn HirDatabase,
1093+
_cycle: &[String],
1094+
def: &GenericDefId,
1095+
) -> Arc<[Binders<Ty>]> {
1096+
let generic_params = generics(db.upcast(), *def);
1097+
1098+
// we still need one default per parameter
1099+
let defaults = generic_params
1100+
.iter()
1101+
.enumerate()
1102+
.map(|(idx, _)| {
1103+
let ty = TyKind::Error.intern(&Interner);
1104+
1105+
crate::make_only_type_binders(idx, ty)
1106+
})
1107+
.collect();
1108+
1109+
defaults
1110+
}
1111+
10921112
fn fn_sig_for_fn(db: &dyn HirDatabase, def: FunctionId) -> PolyFnSig {
10931113
let data = db.function_data(def);
10941114
let resolver = def.resolver(db.upcast());

crates/hir_ty/src/tests/regression.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,3 +1012,41 @@ fn lifetime_from_chalk_during_deref() {
10121012
"#,
10131013
)
10141014
}
1015+
1016+
#[test]
1017+
fn issue_8686() {
1018+
check_infer(
1019+
r#"
1020+
pub trait Try: FromResidual {
1021+
type Output;
1022+
type Residual;
1023+
}
1024+
pub trait FromResidual<R = <Self as Try>::Residual> {
1025+
fn from_residual(residual: R) -> Self;
1026+
}
1027+
1028+
struct ControlFlow<B, C>;
1029+
impl<B, C> Try for ControlFlow<B, C> {
1030+
type Output = C;
1031+
type Residual = ControlFlow<B, !>;
1032+
}
1033+
impl<B, C> FromResidual for ControlFlow<B, C> {
1034+
fn from_residual(r: ControlFlow<B, !>) -> Self { ControlFlow }
1035+
}
1036+
1037+
fn test() {
1038+
ControlFlow::from_residual(ControlFlow::<u32, !>);
1039+
}
1040+
"#,
1041+
expect![[r#"
1042+
144..152 'residual': R
1043+
365..366 'r': ControlFlow<B, !>
1044+
395..410 '{ ControlFlow }': ControlFlow<B, C>
1045+
397..408 'ControlFlow': ControlFlow<B, C>
1046+
424..482 '{ ...!>); }': ()
1047+
430..456 'Contro...sidual': fn from_residual<ControlFlow<u32, {unknown}>, ControlFlow<u32, !>>(ControlFlow<u32, !>) -> ControlFlow<u32, {unknown}>
1048+
430..479 'Contro...2, !>)': ControlFlow<u32, {unknown}>
1049+
457..478 'Contro...32, !>': ControlFlow<u32, !>
1050+
"#]],
1051+
);
1052+
}

crates/hir_ty/src/utils.rs

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
//! Helper functions for working with def, which don't need to be a separate
22
//! query, but can't be computed directly from `*Data` (ie, which need a `db`).
33
4+
use std::iter;
5+
46
use chalk_ir::{fold::Shift, BoundVar, DebruijnIndex};
57
use hir_def::{
68
db::DefDatabase,
@@ -14,8 +16,12 @@ use hir_def::{
1416
AssocContainerId, GenericDefId, Lookup, TraitId, TypeAliasId, TypeParamId,
1517
};
1618
use hir_expand::name::{name, Name};
19+
use rustc_hash::FxHashSet;
1720

18-
use crate::{db::HirDatabase, Interner, Substitution, TraitRef, TraitRefExt, TyKind, WhereClause};
21+
use crate::{
22+
db::HirDatabase, ChalkTraitId, Interner, Substitution, TraitRef, TraitRefExt, TyKind,
23+
WhereClause,
24+
};
1925

2026
fn direct_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
2127
let resolver = trait_.resolver(db);
@@ -102,33 +108,43 @@ pub fn all_super_traits(db: &dyn DefDatabase, trait_: TraitId) -> Vec<TraitId> {
102108
/// `all_super_traits` is that we keep track of type parameters; for example if
103109
/// we have `Self: Trait<u32, i32>` and `Trait<T, U>: OtherTrait<U>` we'll get
104110
/// `Self: OtherTrait<i32>`.
105-
pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) -> Vec<TraitRef> {
106-
// FIXME: replace by Chalk's `super_traits`, maybe make this a query
111+
pub(super) fn all_super_trait_refs(db: &dyn HirDatabase, trait_ref: TraitRef) -> SuperTraits {
112+
SuperTraits { db, seen: iter::once(trait_ref.trait_id).collect(), stack: vec![trait_ref] }
113+
}
107114

108-
// we need to take care a bit here to avoid infinite loops in case of cycles
109-
// (i.e. if we have `trait A: B; trait B: A;`)
110-
let mut result = vec![trait_ref];
111-
let mut i = 0;
112-
while i < result.len() {
113-
let t = &result[i];
114-
// yeah this is quadratic, but trait hierarchies should be flat
115-
// enough that this doesn't matter
116-
for tt in direct_super_trait_refs(db, t) {
117-
if !result.iter().any(|tr| tr.trait_id == tt.trait_id) {
118-
result.push(tt);
119-
}
115+
pub(super) struct SuperTraits<'a> {
116+
db: &'a dyn HirDatabase,
117+
stack: Vec<TraitRef>,
118+
seen: FxHashSet<ChalkTraitId>,
119+
}
120+
121+
impl<'a> SuperTraits<'a> {
122+
fn elaborate(&mut self, trait_ref: &TraitRef) {
123+
let mut trait_refs = direct_super_trait_refs(self.db, trait_ref);
124+
trait_refs.retain(|tr| !self.seen.contains(&tr.trait_id));
125+
self.stack.extend(trait_refs);
126+
}
127+
}
128+
129+
impl<'a> Iterator for SuperTraits<'a> {
130+
type Item = TraitRef;
131+
132+
fn next(&mut self) -> Option<Self::Item> {
133+
if let Some(next) = self.stack.pop() {
134+
self.elaborate(&next);
135+
Some(next)
136+
} else {
137+
None
120138
}
121-
i += 1;
122139
}
123-
result
124140
}
125141

126142
pub(super) fn associated_type_by_name_including_super_traits(
127143
db: &dyn HirDatabase,
128144
trait_ref: TraitRef,
129145
name: &Name,
130146
) -> Option<(TraitRef, TypeAliasId)> {
131-
all_super_trait_refs(db, trait_ref).into_iter().find_map(|t| {
147+
all_super_trait_refs(db, trait_ref).find_map(|t| {
132148
let assoc_type = db.trait_data(t.hir_trait_id()).associated_type_by_name(name)?;
133149
Some((t, assoc_type))
134150
})

0 commit comments

Comments
 (0)