Skip to content

Commit a8e12ab

Browse files
author
Grant Wuerker
committed
ADT recursion
1 parent fd274c9 commit a8e12ab

File tree

5 files changed

+135
-57
lines changed

5 files changed

+135
-57
lines changed

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

+1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ pub struct Jar(
7272
ty::diagnostics::ImplTraitDefDiagAccumulator,
7373
ty::diagnostics::ImplDefDiagAccumulator,
7474
ty::diagnostics::FuncDefDiagAccumulator,
75+
ty::diagnostics::AdtRecursionConstituentAccumulator,
7576
);
7677

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

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

+18-11
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,10 @@ use super::{
2121
collect_impl_block_constraints, collect_super_traits, AssumptionListId, SuperTraitCycle,
2222
},
2323
constraint_solver::{is_goal_satisfiable, GoalSatisfiability},
24-
diagnostics::{ImplDiag, TraitConstraintDiag, TraitLowerDiag, TyDiagCollection, TyLowerDiag},
24+
diagnostics::{
25+
AdtRecursionConstituent, ImplDiag, TraitConstraintDiag, TraitLowerDiag, TyDiagCollection,
26+
TyLowerDiag,
27+
},
2528
trait_def::{ingot_trait_env, Implementor, TraitDef, TraitMethod},
2629
trait_lower::{lower_trait, lower_trait_ref, TraitRefLowerError},
2730
ty_def::{AdtDef, AdtRef, AdtRefId, FuncDef, InvalidCause, TyData, TyId},
@@ -32,8 +35,9 @@ use crate::{
3235
name_resolution::{resolve_path_early, EarlyResolvedPath, NameDomain, NameResKind},
3336
ty::{
3437
diagnostics::{
35-
AdtDefDiagAccumulator, FuncDefDiagAccumulator, ImplDefDiagAccumulator,
36-
ImplTraitDefDiagAccumulator, TraitDefDiagAccumulator, TypeAliasDefDiagAccumulator,
38+
AdtDefDiagAccumulator, AdtRecursionConstituentAccumulator, FuncDefDiagAccumulator,
39+
ImplDefDiagAccumulator, ImplTraitDefDiagAccumulator, TraitDefDiagAccumulator,
40+
TypeAliasDefDiagAccumulator,
3741
},
3842
method_table::collect_methods,
3943
trait_lower::lower_impl_trait,
@@ -62,8 +66,8 @@ pub fn analyze_adt(db: &dyn HirAnalysisDb, adt_ref: AdtRefId) {
6266
AdtDefDiagAccumulator::push(db, diag);
6367
}
6468

65-
if let Some(diag) = check_recursive_adt(db, adt_ref) {
66-
AdtDefDiagAccumulator::push(db, diag);
69+
if let Some(constituent) = check_recursive_adt(db, adt_ref) {
70+
AdtRecursionConstituentAccumulator::push(db, constituent);
6771
}
6872
}
6973

@@ -764,7 +768,7 @@ impl<'db> Visitor for DefAnalyzer<'db> {
764768
pub(crate) fn check_recursive_adt(
765769
db: &dyn HirAnalysisDb,
766770
adt: AdtRefId,
767-
) -> Option<TyDiagCollection> {
771+
) -> Option<AdtRecursionConstituent> {
768772
let adt_def = lower_adt(db, adt);
769773
for field in adt_def.fields(db) {
770774
for ty in field.iter_types(db) {
@@ -781,7 +785,7 @@ fn check_recursive_adt_impl(
781785
db: &dyn HirAnalysisDb,
782786
cycle: &salsa::Cycle,
783787
adt: AdtRefId,
784-
) -> Option<TyDiagCollection> {
788+
) -> Option<AdtRecursionConstituent> {
785789
let participants: FxHashSet<_> = cycle
786790
.participant_keys()
787791
.map(|key| check_recursive_adt::key_from_id(key.key_index()))
@@ -792,11 +796,14 @@ fn check_recursive_adt_impl(
792796
for (ty_idx, ty) in field.iter_types(db).enumerate() {
793797
for field_adt_ref in ty.collect_direct_adts(db) {
794798
if participants.contains(&field_adt_ref) && participants.contains(&adt) {
795-
let diag = TyLowerDiag::recursive_type(
796-
adt.name_span(db),
797-
adt_def.variant_ty_span(db, field_idx, ty_idx),
799+
let constituent = AdtRecursionConstituent::new(
800+
(adt, adt.name_span(db)),
801+
(
802+
field_adt_ref,
803+
adt_def.variant_ty_span(db, field_idx, ty_idx),
804+
),
798805
);
799-
return Some(diag.into());
806+
return Some(constituent);
800807
}
801808
}
802809
}

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

+85-25
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ use hir::{
1010
HirDb, SpannedHirDb,
1111
};
1212
use itertools::Itertools;
13+
use rustc_hash::FxHashSet;
14+
15+
use crate::HirAnalysisDb;
1316

1417
use super::{
1518
constraint::PredicateId,
16-
ty_def::{Kind, TyId},
19+
ty_def::{AdtRefId, Kind, TyId},
1720
};
18-
use crate::HirAnalysisDb;
1921

2022
#[salsa::accumulator]
2123
pub struct AdtDefDiagAccumulator(pub(super) TyDiagCollection);
@@ -29,6 +31,8 @@ pub struct ImplDefDiagAccumulator(pub(super) TyDiagCollection);
2931
pub struct FuncDefDiagAccumulator(pub(super) TyDiagCollection);
3032
#[salsa::accumulator]
3133
pub struct TypeAliasDefDiagAccumulator(pub(super) TyDiagCollection);
34+
#[salsa::accumulator]
35+
pub struct AdtRecursionConstituentAccumulator(pub(super) AdtRecursionConstituent);
3236

3337
#[derive(Debug, PartialEq, Eq, Hash, Clone, derive_more::From)]
3438
pub enum TyDiagCollection {
@@ -53,10 +57,7 @@ impl TyDiagCollection {
5357
pub enum TyLowerDiag {
5458
ExpectedStarKind(DynLazySpan),
5559
InvalidTypeArgKind(DynLazySpan, String),
56-
RecursiveType {
57-
primary_span: DynLazySpan,
58-
field_span: DynLazySpan,
59-
},
60+
AdtRecursion(Vec<AdtRecursionConstituent>),
6061

6162
UnboundTypeAliasParam {
6263
span: DynLazySpan,
@@ -140,11 +141,8 @@ impl TyLowerDiag {
140141
Self::InvalidTypeArgKind(span, msg)
141142
}
142143

143-
pub(super) fn recursive_type(primary_span: DynLazySpan, field_span: DynLazySpan) -> Self {
144-
Self::RecursiveType {
145-
primary_span,
146-
field_span,
147-
}
144+
pub(super) fn adt_recursion(constituents: Vec<AdtRecursionConstituent>) -> Self {
145+
Self::AdtRecursion(constituents)
148146
}
149147

150148
pub(super) fn unbound_type_alias_param(
@@ -249,7 +247,7 @@ impl TyLowerDiag {
249247
match self {
250248
Self::ExpectedStarKind(_) => 0,
251249
Self::InvalidTypeArgKind(_, _) => 1,
252-
Self::RecursiveType { .. } => 2,
250+
Self::AdtRecursion { .. } => 2,
253251
Self::UnboundTypeAliasParam { .. } => 3,
254252
Self::TypeAliasCycle { .. } => 4,
255253
Self::InconsistentKindBound(_, _) => 5,
@@ -270,7 +268,7 @@ impl TyLowerDiag {
270268
match self {
271269
Self::ExpectedStarKind(_) => "expected `*` kind in this context".to_string(),
272270
Self::InvalidTypeArgKind(_, _) => "invalid type argument kind".to_string(),
273-
Self::RecursiveType { .. } => "recursive type is not allowed".to_string(),
271+
Self::AdtRecursion { .. } => "recursive type is not allowed".to_string(),
274272

275273
Self::UnboundTypeAliasParam { .. } => {
276274
"all type parameters of type alias must be given".to_string()
@@ -326,22 +324,23 @@ impl TyLowerDiag {
326324
span.resolve(db),
327325
)],
328326

329-
Self::RecursiveType {
330-
primary_span,
331-
field_span,
332-
} => {
333-
vec![
334-
SubDiagnostic::new(
327+
Self::AdtRecursion(constituents) => {
328+
let mut diags = vec![];
329+
330+
for AdtRecursionConstituent { from, to } in constituents {
331+
diags.push(SubDiagnostic::new(
335332
LabelStyle::Primary,
336333
"recursive type definition".to_string(),
337-
primary_span.resolve(db),
338-
),
339-
SubDiagnostic::new(
334+
from.1.resolve(db),
335+
));
336+
diags.push(SubDiagnostic::new(
340337
LabelStyle::Secondary,
341338
"recursion occurs here".to_string(),
342-
field_span.resolve(db),
343-
),
344-
]
339+
to.1.resolve(db),
340+
));
341+
}
342+
343+
diags
345344
}
346345

347346
Self::UnboundTypeAliasParam {
@@ -1260,3 +1259,64 @@ impl DiagnosticVoucher for ImplDiag {
12601259
CompleteDiagnostic::new(severity, message, sub_diags, vec![], error_code)
12611260
}
12621261
}
1262+
1263+
/// Generates diagnostics from a list of ADT recursion constituents.
1264+
pub fn adt_recursion_diags(constituents: &[AdtRecursionConstituent]) -> Vec<TyDiagCollection> {
1265+
let mut diags = vec![];
1266+
1267+
// `unified_constituents` tracks constituents that have been included in recursions.
1268+
// Constituents in this set cannot be used to construct other recursions.
1269+
let mut unified_constituents = FxHashSet::default();
1270+
1271+
// `cur` is set to the first item in `constituents` that has not been included in another recursion.
1272+
while let Some(mut cur) =
1273+
(0..constituents.len()).find(|index| !unified_constituents.contains(index))
1274+
{
1275+
unified_constituents.insert(cur);
1276+
let mut recursion = vec![cur];
1277+
1278+
// The recursion is complete if the `from` of the first constituent is equal to the `to` of `cur`.
1279+
while constituents[recursion[0]].from.0 != constituents[cur].to.0 {
1280+
// The next constituent of the recursion is found by comparing the `to` of `cur` with `from` of the candidate constituent.
1281+
if let Some(index) = (0..constituents.len()).find(|index| {
1282+
!unified_constituents.contains(index)
1283+
&& constituents[cur].to.0 == constituents[*index].from.0
1284+
}) {
1285+
cur = index;
1286+
unified_constituents.insert(index);
1287+
recursion.push(index);
1288+
} else {
1289+
break;
1290+
};
1291+
}
1292+
1293+
diags.push(
1294+
TyLowerDiag::adt_recursion(
1295+
recursion
1296+
.iter()
1297+
.map(|index| constituents[*index].to_owned())
1298+
.collect(),
1299+
)
1300+
.into(),
1301+
);
1302+
}
1303+
1304+
diags
1305+
}
1306+
1307+
/// Constituent of an ADT recursion.
1308+
///
1309+
/// A full ADT recursion can be represented using a list of `AdtRecursionConstituent`s.
1310+
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
1311+
pub struct AdtRecursionConstituent {
1312+
/// The ADT definition from which the constituent originates and its name span.
1313+
pub from: (AdtRefId, DynLazySpan),
1314+
/// The ADT to which this recursion continues and the span where this occurs
1315+
pub to: (AdtRefId, DynLazySpan),
1316+
}
1317+
1318+
impl AdtRecursionConstituent {
1319+
pub fn new(from: (AdtRefId, DynLazySpan), to: (AdtRefId, DynLazySpan)) -> Self {
1320+
Self { from, to }
1321+
}
1322+
}

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

+23-7
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
use hir::{analysis_pass::ModuleAnalysisPass, hir_def::TopLevelMod};
2+
use itertools::Itertools;
23

34
use self::{
45
def_analysis::{
56
analyze_adt, analyze_func, analyze_impl, analyze_impl_trait, analyze_trait,
67
analyze_type_alias,
78
},
89
diagnostics::{
9-
AdtDefDiagAccumulator, FuncDefDiagAccumulator, ImplDefDiagAccumulator,
10-
ImplTraitDefDiagAccumulator, TraitDefDiagAccumulator, TypeAliasDefDiagAccumulator,
10+
adt_recursion_diags, AdtDefDiagAccumulator, AdtRecursionConstituentAccumulator,
11+
FuncDefDiagAccumulator, ImplDefDiagAccumulator, ImplTraitDefDiagAccumulator,
12+
TraitDefDiagAccumulator, TypeAliasDefDiagAccumulator,
1113
},
1214
ty_def::AdtRefId,
1315
};
@@ -61,12 +63,26 @@ impl<'db> ModuleAnalysisPass for TypeDefAnalysisPass<'db> {
6163
.iter()
6264
.map(|c| AdtRefId::from_contract(self.db, *c)),
6365
);
66+
let (diags, recursion_constituents): (Vec<_>, Vec<_>) = adts
67+
.map(|adt| {
68+
(
69+
analyze_adt::accumulated::<AdtDefDiagAccumulator>(self.db, adt),
70+
analyze_adt::accumulated::<AdtRecursionConstituentAccumulator>(self.db, adt),
71+
)
72+
})
73+
.unzip();
74+
let recursion_constituents = recursion_constituents.into_iter().flatten().collect_vec();
6475

65-
adts.flat_map(|adt| {
66-
analyze_adt::accumulated::<AdtDefDiagAccumulator>(self.db, adt).into_iter()
67-
})
68-
.map(|diag| diag.to_voucher())
69-
.collect()
76+
diags
77+
.into_iter()
78+
.flatten()
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()
7086
}
7187
}
7288

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)