@@ -9,12 +9,13 @@ use hir::{
9
9
span:: { DynLazySpan , LazySpan } ,
10
10
HirDb , SpannedHirDb ,
11
11
} ;
12
+ use indexmap:: indexset;
12
13
13
14
use crate :: HirAnalysisDb ;
14
15
15
16
use super :: {
16
17
constraint:: PredicateId ,
17
- ty_def:: { Kind , TyId } ,
18
+ ty_def:: { AdtRefId , Kind , TyId } ,
18
19
} ;
19
20
20
21
use itertools:: Itertools ;
@@ -31,6 +32,8 @@ pub struct ImplDefDiagAccumulator(pub(super) TyDiagCollection);
31
32
pub struct FuncDefDiagAccumulator ( pub ( super ) TyDiagCollection ) ;
32
33
#[ salsa:: accumulator]
33
34
pub struct TypeAliasDefDiagAccumulator ( pub ( super ) TyDiagCollection ) ;
35
+ #[ salsa:: accumulator]
36
+ pub struct AdtRecursionConstituentAccumulator ( pub ( super ) AdtRecursionConstituent ) ;
34
37
35
38
#[ derive( Debug , PartialEq , Eq , Hash , Clone , derive_more:: From ) ]
36
39
pub enum TyDiagCollection {
@@ -55,10 +58,7 @@ impl TyDiagCollection {
55
58
pub enum TyLowerDiag {
56
59
NotFullyAppliedType ( DynLazySpan ) ,
57
60
InvalidTypeArgKind ( DynLazySpan , String ) ,
58
- RecursiveType {
59
- primary_span : DynLazySpan ,
60
- field_span : DynLazySpan ,
61
- } ,
61
+ AdtRecursion ( Vec < AdtRecursionConstituent > ) ,
62
62
63
63
UnboundTypeAliasParam {
64
64
span : DynLazySpan ,
@@ -117,11 +117,8 @@ impl TyLowerDiag {
117
117
Self :: InvalidTypeArgKind ( span, msg)
118
118
}
119
119
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)
125
122
}
126
123
127
124
pub ( super ) fn unbound_type_alias_param (
@@ -184,7 +181,7 @@ impl TyLowerDiag {
184
181
match self {
185
182
Self :: NotFullyAppliedType ( _) => 0 ,
186
183
Self :: InvalidTypeArgKind ( _, _) => 1 ,
187
- Self :: RecursiveType { .. } => 2 ,
184
+ Self :: AdtRecursion { .. } => 2 ,
188
185
Self :: UnboundTypeAliasParam { .. } => 3 ,
189
186
Self :: TypeAliasCycle { .. } => 4 ,
190
187
Self :: InconsistentKindBound ( _, _) => 5 ,
@@ -199,7 +196,7 @@ impl TyLowerDiag {
199
196
match self {
200
197
Self :: NotFullyAppliedType ( _) => "expected fully applied type" . to_string ( ) ,
201
198
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 ( ) ,
203
200
204
201
Self :: UnboundTypeAliasParam { .. } => {
205
202
"all type parameters of type alias must be given" . to_string ( )
@@ -235,22 +232,23 @@ impl TyLowerDiag {
235
232
span. resolve( db) ,
236
233
) ] ,
237
234
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 (
244
240
LabelStyle :: Primary ,
245
241
"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 (
249
245
LabelStyle :: Secondary ,
250
246
"recursion occurs here" . to_string ( ) ,
251
- field_span. resolve( db) ,
252
- ) ,
253
- ]
247
+ to. 1 . resolve ( db) ,
248
+ ) ) ;
249
+ }
250
+
251
+ diags
254
252
}
255
253
256
254
Self :: UnboundTypeAliasParam {
@@ -1102,3 +1100,64 @@ impl DiagnosticVoucher for ImplDiag {
1102
1100
CompleteDiagnostic :: new ( severity, message, sub_diags, vec ! [ ] , error_code)
1103
1101
}
1104
1102
}
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
+ }
0 commit comments