Skip to content

Commit abad1f4

Browse files
author
Grant Wuerker
committed
ADT recursion
1 parent d664a7a commit abad1f4

File tree

7 files changed

+135
-56
lines changed

7 files changed

+135
-56
lines changed

Diff for: Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: crates/hir-analysis/Cargo.toml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ either = "1.8"
1515
derive_more = "0.99"
1616
itertools = "0.10"
1717
ena = "0.14"
18-
18+
indexmap = "1.6.2"
1919
hir = { path = "../hir", package = "fe-hir" }
2020
common = { path = "../common2", package = "fe-common2" }
2121
macros = { path = "../macros", package = "fe-macros" }

Diff for: crates/hir-analysis/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ pub struct Jar(
6767
ty::diagnostics::ImplTraitDefDiagAccumulator,
6868
ty::diagnostics::ImplDefDiagAccumulator,
6969
ty::diagnostics::FuncDefDiagAccumulator,
70+
ty::diagnostics::AdtRecursionConstituentAccumulator,
7071
);
7172

7273
pub trait HirAnalysisDb: salsa::DbWithJar<Jar> + HirDb {

Diff for: crates/hir-analysis/src/ty/def_analysis.rs

+16-9
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ use super::{
3434
collect_impl_block_constraints, collect_super_traits, AssumptionListId, SuperTraitCycle,
3535
},
3636
constraint_solver::{is_goal_satisfiable, GoalSatisfiability},
37-
diagnostics::{ImplDiag, TraitConstraintDiag, TraitLowerDiag, TyDiagCollection, TyLowerDiag},
37+
diagnostics::{
38+
AdtRecursionConstituent, ImplDiag, TraitConstraintDiag, TraitLowerDiag, TyDiagCollection,
39+
TyLowerDiag,
40+
},
3841
trait_def::{ingot_trait_env, Implementor, TraitDef, TraitMethod},
3942
trait_lower::{lower_trait, lower_trait_ref, TraitRefLowerError},
4043
ty_def::{AdtDef, AdtRef, AdtRefId, FuncDef, InvalidCause, TyId},
@@ -44,6 +47,7 @@ use super::{
4447
},
4548
visitor::{walk_ty, TyVisitor},
4649
};
50+
use crate::ty::diagnostics::AdtRecursionConstituentAccumulator;
4751

4852
/// This function implements analysis for the ADT definition.
4953
/// The analysis includes the following:
@@ -64,8 +68,8 @@ pub fn analyze_adt(db: &dyn HirAnalysisDb, adt_ref: AdtRefId) {
6468
AdtDefDiagAccumulator::push(db, diag);
6569
}
6670

67-
if let Some(diag) = check_recursive_adt(db, adt_ref) {
68-
AdtDefDiagAccumulator::push(db, diag);
71+
if let Some(constituent) = check_recursive_adt(db, adt_ref) {
72+
AdtRecursionConstituentAccumulator::push(db, constituent);
6973
}
7074
}
7175

@@ -701,7 +705,7 @@ impl<'db> Visitor for DefAnalyzer<'db> {
701705
pub(crate) fn check_recursive_adt(
702706
db: &dyn HirAnalysisDb,
703707
adt: AdtRefId,
704-
) -> Option<TyDiagCollection> {
708+
) -> Option<AdtRecursionConstituent> {
705709
let adt_def = lower_adt(db, adt);
706710
for field in adt_def.fields(db) {
707711
for ty in field.iter_types(db) {
@@ -718,7 +722,7 @@ fn check_recursive_adt_impl(
718722
db: &dyn HirAnalysisDb,
719723
cycle: &salsa::Cycle,
720724
adt: AdtRefId,
721-
) -> Option<TyDiagCollection> {
725+
) -> Option<AdtRecursionConstituent> {
722726
let participants: FxHashSet<_> = cycle
723727
.participant_keys()
724728
.map(|key| check_recursive_adt::key_from_id(key.key_index()))
@@ -729,11 +733,14 @@ fn check_recursive_adt_impl(
729733
for (ty_idx, ty) in field.iter_types(db).enumerate() {
730734
for field_adt_ref in ty.collect_direct_adts(db) {
731735
if participants.contains(&field_adt_ref) && participants.contains(&adt) {
732-
let diag = TyLowerDiag::recursive_type(
733-
adt.name_span(db),
734-
adt_def.variant_ty_span(db, field_idx, ty_idx),
736+
let constituent = AdtRecursionConstituent::new(
737+
(adt, adt.name_span(db)),
738+
(
739+
field_adt_ref,
740+
adt_def.variant_ty_span(db, field_idx, ty_idx),
741+
),
735742
);
736-
return Some(diag.into());
743+
return Some(constituent);
737744
}
738745
}
739746
}

Diff for: crates/hir-analysis/src/ty/diagnostics.rs

+83-24
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@ use hir::{
99
span::{DynLazySpan, LazySpan},
1010
HirDb, SpannedHirDb,
1111
};
12+
use indexmap::indexset;
1213

1314
use crate::HirAnalysisDb;
1415

1516
use super::{
1617
constraint::PredicateId,
17-
ty_def::{Kind, TyId},
18+
ty_def::{AdtRefId, Kind, TyId},
1819
};
1920

2021
use itertools::Itertools;
@@ -31,6 +32,8 @@ pub struct ImplDefDiagAccumulator(pub(super) TyDiagCollection);
3132
pub struct FuncDefDiagAccumulator(pub(super) TyDiagCollection);
3233
#[salsa::accumulator]
3334
pub struct TypeAliasDefDiagAccumulator(pub(super) TyDiagCollection);
35+
#[salsa::accumulator]
36+
pub struct AdtRecursionConstituentAccumulator(pub(super) AdtRecursionConstituent);
3437

3538
#[derive(Debug, PartialEq, Eq, Hash, Clone, derive_more::From)]
3639
pub enum TyDiagCollection {
@@ -55,10 +58,7 @@ impl TyDiagCollection {
5558
pub enum TyLowerDiag {
5659
NotFullyAppliedType(DynLazySpan),
5760
InvalidTypeArgKind(DynLazySpan, String),
58-
RecursiveType {
59-
primary_span: DynLazySpan,
60-
field_span: DynLazySpan,
61-
},
61+
AdtRecursion(Vec<AdtRecursionConstituent>),
6262

6363
UnboundTypeAliasParam {
6464
span: DynLazySpan,
@@ -117,11 +117,8 @@ impl TyLowerDiag {
117117
Self::InvalidTypeArgKind(span, msg)
118118
}
119119

120-
pub(super) fn recursive_type(primary_span: DynLazySpan, field_span: DynLazySpan) -> Self {
121-
Self::RecursiveType {
122-
primary_span,
123-
field_span,
124-
}
120+
pub(super) fn adt_recursion(constituents: Vec<AdtRecursionConstituent>) -> Self {
121+
Self::AdtRecursion(constituents)
125122
}
126123

127124
pub(super) fn unbound_type_alias_param(
@@ -184,7 +181,7 @@ impl TyLowerDiag {
184181
match self {
185182
Self::NotFullyAppliedType(_) => 0,
186183
Self::InvalidTypeArgKind(_, _) => 1,
187-
Self::RecursiveType { .. } => 2,
184+
Self::AdtRecursion { .. } => 2,
188185
Self::UnboundTypeAliasParam { .. } => 3,
189186
Self::TypeAliasCycle { .. } => 4,
190187
Self::InconsistentKindBound(_, _) => 5,
@@ -199,7 +196,7 @@ impl TyLowerDiag {
199196
match self {
200197
Self::NotFullyAppliedType(_) => "expected fully applied type".to_string(),
201198
Self::InvalidTypeArgKind(_, _) => "invalid type argument kind".to_string(),
202-
Self::RecursiveType { .. } => "recursive type is not allowed".to_string(),
199+
Self::AdtRecursion { .. } => "recursive type is not allowed".to_string(),
203200

204201
Self::UnboundTypeAliasParam { .. } => {
205202
"all type parameters of type alias must be given".to_string()
@@ -235,22 +232,23 @@ impl TyLowerDiag {
235232
span.resolve(db),
236233
)],
237234

238-
Self::RecursiveType {
239-
primary_span,
240-
field_span,
241-
} => {
242-
vec![
243-
SubDiagnostic::new(
235+
Self::AdtRecursion(constituents) => {
236+
let mut diags = vec![];
237+
238+
for AdtRecursionConstituent { from, to } in constituents {
239+
diags.push(SubDiagnostic::new(
244240
LabelStyle::Primary,
245241
"recursive type definition".to_string(),
246-
primary_span.resolve(db),
247-
),
248-
SubDiagnostic::new(
242+
from.1.resolve(db),
243+
));
244+
diags.push(SubDiagnostic::new(
249245
LabelStyle::Secondary,
250246
"recursion occurs here".to_string(),
251-
field_span.resolve(db),
252-
),
253-
]
247+
to.1.resolve(db),
248+
));
249+
}
250+
251+
diags
254252
}
255253

256254
Self::UnboundTypeAliasParam {
@@ -1102,3 +1100,64 @@ impl DiagnosticVoucher for ImplDiag {
11021100
CompleteDiagnostic::new(severity, message, sub_diags, vec![], error_code)
11031101
}
11041102
}
1103+
1104+
/// Generates diagnostics from a list of ADT recursion constituents.
1105+
pub fn adt_recursion_diags(constituents: &[AdtRecursionConstituent]) -> Vec<TyDiagCollection> {
1106+
let mut diags = vec![];
1107+
1108+
// `unified_constituents` tracks constituents that have been included in recursions.
1109+
// Constituents in this set cannot be used to construct other recursions.
1110+
let mut unified_constituents = indexset! {};
1111+
1112+
// `cur` is set to the first item in `constituents` that has not been included in another recursion.
1113+
while let Some(mut cur) =
1114+
(0..constituents.len()).find(|index| !unified_constituents.contains(index))
1115+
{
1116+
unified_constituents.insert(cur);
1117+
let mut recursion = vec![cur];
1118+
1119+
// The recursion is complete if the `from` of the first constituent is equal to the `to` of `cur`.
1120+
while constituents[recursion[0]].from.0 != constituents[cur].to.0 {
1121+
// The next constituent of the recursion is found by comparing the `to` of `cur` with `from` of the candidate constituent.
1122+
if let Some(index) = (0..constituents.len()).find(|index| {
1123+
!unified_constituents.contains(index)
1124+
&& constituents[cur].to.0 == constituents[*index].from.0
1125+
}) {
1126+
cur = index;
1127+
unified_constituents.insert(index);
1128+
recursion.push(index);
1129+
} else {
1130+
break;
1131+
};
1132+
}
1133+
1134+
diags.push(
1135+
TyLowerDiag::adt_recursion(
1136+
recursion
1137+
.iter()
1138+
.map(|index| constituents[*index].to_owned())
1139+
.collect(),
1140+
)
1141+
.into(),
1142+
);
1143+
}
1144+
1145+
diags
1146+
}
1147+
1148+
/// Constituent of an ADT recursion.
1149+
///
1150+
/// A full ADT recursion can be represented using a list of `AdtRecursionConstituent`s.
1151+
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
1152+
pub struct AdtRecursionConstituent {
1153+
/// The ADT definition from which the constituent originates and its name span.
1154+
pub from: (AdtRefId, DynLazySpan),
1155+
/// The ADT to which this recursion continues and the span where this occurs
1156+
pub to: (AdtRefId, DynLazySpan),
1157+
}
1158+
1159+
impl AdtRecursionConstituent {
1160+
pub fn new(from: (AdtRefId, DynLazySpan), to: (AdtRefId, DynLazySpan)) -> Self {
1161+
Self { from, to }
1162+
}
1163+
}

Diff for: crates/hir-analysis/src/ty/mod.rs

+25-8
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
use crate::HirAnalysisDb;
22
use hir::{analysis_pass::ModuleAnalysisPass, hir_def::TopLevelMod};
3+
use itertools::Itertools;
34

45
use self::{
56
def_analysis::{
67
analyze_adt, analyze_func, analyze_impl, analyze_impl_trait, analyze_trait,
78
analyze_type_alias,
89
},
910
diagnostics::{
10-
AdtDefDiagAccumulator, FuncDefDiagAccumulator, ImplDefDiagAccumulator,
11-
ImplTraitDefDiagAccumulator, TraitDefDiagAccumulator, TypeAliasDefDiagAccumulator,
11+
adt_recursion_diags, AdtDefDiagAccumulator, AdtRecursionConstituentAccumulator,
12+
FuncDefDiagAccumulator, ImplDefDiagAccumulator, ImplTraitDefDiagAccumulator,
13+
TraitDefDiagAccumulator, TypeAliasDefDiagAccumulator,
1214
},
1315
ty_def::AdtRefId,
1416
};
@@ -60,12 +62,27 @@ impl<'db> ModuleAnalysisPass for TypeDefAnalysisPass<'db> {
6062
.iter()
6163
.map(|c| AdtRefId::from_contract(self.db, *c)),
6264
);
63-
64-
adts.flat_map(|adt| {
65-
analyze_adt::accumulated::<AdtDefDiagAccumulator>(self.db, adt).into_iter()
66-
})
67-
.map(|diag| diag.to_voucher())
68-
.collect()
65+
let (diags, recursion_constituents): (Vec<_>, Vec<_>) = adts
66+
.map(|adt| {
67+
(
68+
analyze_adt::accumulated::<AdtDefDiagAccumulator>(self.db, adt),
69+
analyze_adt::accumulated::<AdtRecursionConstituentAccumulator>(self.db, adt),
70+
)
71+
})
72+
.unzip();
73+
let (diags, recursion_constituents) = (
74+
diags.into_iter().flatten().collect_vec(),
75+
recursion_constituents.into_iter().flatten().collect_vec(),
76+
);
77+
diags
78+
.into_iter()
79+
.map(|diag| diag.to_voucher())
80+
.chain(
81+
adt_recursion_diags(&recursion_constituents)
82+
.iter()
83+
.map(|diag| diag.to_voucher()),
84+
)
85+
.collect()
6986
}
7087
}
7188

Diff for: crates/uitest/fixtures/ty/def/recursive_type.snap

+8-14
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
source: crates/uitest/tests/ty.rs
33
expression: diags
4-
input_file: crates/uitest/fixtures/ty/recursive_type.fe
4+
input_file: crates/uitest/fixtures/ty/def/recursive_type.fe
55
---
66
error[3-0002]: recursive type is not allowed
77
┌─ recursive_type.fe:1:12
@@ -12,24 +12,18 @@ error[3-0002]: recursive type is not allowed
1212
│ -- recursion occurs here
1313

1414
error[3-0002]: recursive type is not allowed
15-
┌─ recursive_type.fe:5:12
16-
17-
5 │ pub struct S2 {
18-
│ ^^ recursive type definition
19-
6 │ s: S3
20-
│ -- recursion occurs here
21-
22-
error[3-0002]: recursive type is not allowed
23-
┌─ recursive_type.fe:9:12
15+
┌─ recursive_type.fe:5:12
2416
17+
5 │ pub struct S2 {
18+
│ ^^ recursive type definition
19+
6 │ s: S3
20+
│ -- recursion occurs here
21+
·
2522
9 │ pub struct S3 {
2623
│ ^^ recursive type definition
2724
10 │ s: S4
2825
│ -- recursion occurs here
29-
30-
error[3-0002]: recursive type is not allowed
31-
┌─ recursive_type.fe:13:12
32-
26+
·
3327
13 │ pub struct S4 {
3428
│ ^^ recursive type definition
3529
14 │ s: S2

0 commit comments

Comments
 (0)