1
- use rustc_middle:: ty:: { self , AdtSizedConstraint , Ty , TyCtxt } ;
1
+ use rustc_data_structures:: fx:: FxHashSet ;
2
+ use rustc_errors:: { pluralize, struct_span_err, Applicability , MultiSpan } ;
3
+ use rustc_hir as hir;
4
+ use rustc_hir:: def:: DefKind ;
5
+ use rustc_middle:: ty:: Representability ;
6
+ use rustc_middle:: ty:: { self , AdtSizedConstraint , DefIdTree , Ty , TyCtxt } ;
7
+ use rustc_query_system:: query:: QueryInfo ;
2
8
use rustc_query_system:: Value ;
9
+ use rustc_span:: def_id:: LocalDefId ;
10
+ use rustc_span:: Span ;
11
+
12
+ use std:: fmt:: Write ;
3
13
4
14
impl < ' tcx > Value < TyCtxt < ' tcx > > for Ty < ' _ > {
5
- fn from_cycle_error ( tcx : TyCtxt < ' tcx > ) -> Self {
15
+ fn from_cycle_error ( tcx : TyCtxt < ' tcx > , _ : & [ QueryInfo ] ) -> Self {
6
16
// SAFETY: This is never called when `Self` is not `Ty<'tcx>`.
7
17
// FIXME: Represent the above fact in the trait system somehow.
8
18
unsafe { std:: mem:: transmute :: < Ty < ' tcx > , Ty < ' _ > > ( tcx. ty_error ( ) ) }
9
19
}
10
20
}
11
21
12
22
impl < ' tcx > Value < TyCtxt < ' tcx > > for ty:: SymbolName < ' _ > {
13
- fn from_cycle_error ( tcx : TyCtxt < ' tcx > ) -> Self {
23
+ fn from_cycle_error ( tcx : TyCtxt < ' tcx > , _ : & [ QueryInfo ] ) -> Self {
14
24
// SAFETY: This is never called when `Self` is not `SymbolName<'tcx>`.
15
25
// FIXME: Represent the above fact in the trait system somehow.
16
26
unsafe {
@@ -22,7 +32,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::SymbolName<'_> {
22
32
}
23
33
24
34
impl < ' tcx > Value < TyCtxt < ' tcx > > for AdtSizedConstraint < ' _ > {
25
- fn from_cycle_error ( tcx : TyCtxt < ' tcx > ) -> Self {
35
+ fn from_cycle_error ( tcx : TyCtxt < ' tcx > , _ : & [ QueryInfo ] ) -> Self {
26
36
// SAFETY: This is never called when `Self` is not `AdtSizedConstraint<'tcx>`.
27
37
// FIXME: Represent the above fact in the trait system somehow.
28
38
unsafe {
@@ -34,7 +44,7 @@ impl<'tcx> Value<TyCtxt<'tcx>> for AdtSizedConstraint<'_> {
34
44
}
35
45
36
46
impl < ' tcx > Value < TyCtxt < ' tcx > > for ty:: Binder < ' _ , ty:: FnSig < ' _ > > {
37
- fn from_cycle_error ( tcx : TyCtxt < ' tcx > ) -> Self {
47
+ fn from_cycle_error ( tcx : TyCtxt < ' tcx > , _ : & [ QueryInfo ] ) -> Self {
38
48
let err = tcx. ty_error ( ) ;
39
49
// FIXME(compiler-errors): It would be nice if we could get the
40
50
// query key, so we could at least generate a fn signature that
@@ -52,3 +62,153 @@ impl<'tcx> Value<TyCtxt<'tcx>> for ty::Binder<'_, ty::FnSig<'_>> {
52
62
unsafe { std:: mem:: transmute :: < ty:: PolyFnSig < ' tcx > , ty:: Binder < ' _ , ty:: FnSig < ' _ > > > ( fn_sig) }
53
63
}
54
64
}
65
+
66
+ impl < ' tcx > Value < TyCtxt < ' tcx > > for Representability {
67
+ fn from_cycle_error ( tcx : TyCtxt < ' tcx > , cycle : & [ QueryInfo ] ) -> Self {
68
+ let mut item_and_field_ids = Vec :: new ( ) ;
69
+ let mut representable_ids = FxHashSet :: default ( ) ;
70
+ for info in cycle {
71
+ if info. query . name == "representability"
72
+ && let Some ( field_id) = info. query . def_id
73
+ && let Some ( field_id) = field_id. as_local ( )
74
+ && let Some ( DefKind :: Field ) = info. query . def_kind
75
+ {
76
+ let parent_id = tcx. parent ( field_id. to_def_id ( ) ) ;
77
+ let item_id = match tcx. def_kind ( parent_id) {
78
+ DefKind :: Variant => tcx. parent ( parent_id) ,
79
+ _ => parent_id,
80
+ } ;
81
+ item_and_field_ids. push ( ( item_id. expect_local ( ) , field_id) ) ;
82
+ }
83
+ }
84
+ for info in cycle {
85
+ if info. query . name == "representability_adt_ty"
86
+ && let Some ( def_id) = info. query . ty_adt_id
87
+ && let Some ( def_id) = def_id. as_local ( )
88
+ && !item_and_field_ids. iter ( ) . any ( |& ( id, _) | id == def_id)
89
+ {
90
+ representable_ids. insert ( def_id) ;
91
+ }
92
+ }
93
+ recursive_type_error ( tcx, item_and_field_ids, & representable_ids) ;
94
+ Representability :: Infinite
95
+ }
96
+ }
97
+
98
+ // item_and_field_ids should form a cycle where each field contains the
99
+ // type in the next element in the list
100
+ pub fn recursive_type_error (
101
+ tcx : TyCtxt < ' _ > ,
102
+ mut item_and_field_ids : Vec < ( LocalDefId , LocalDefId ) > ,
103
+ representable_ids : & FxHashSet < LocalDefId > ,
104
+ ) {
105
+ const ITEM_LIMIT : usize = 5 ;
106
+
107
+ // Rotate the cycle so that the item with the lowest span is first
108
+ let start_index = item_and_field_ids
109
+ . iter ( )
110
+ . enumerate ( )
111
+ . min_by_key ( |& ( _, & ( id, _) ) | tcx. def_span ( id) )
112
+ . unwrap ( )
113
+ . 0 ;
114
+ item_and_field_ids. rotate_left ( start_index) ;
115
+
116
+ let cycle_len = item_and_field_ids. len ( ) ;
117
+ let show_cycle_len = cycle_len. min ( ITEM_LIMIT ) ;
118
+
119
+ let mut err_span = MultiSpan :: from_spans (
120
+ item_and_field_ids[ ..show_cycle_len]
121
+ . iter ( )
122
+ . map ( |( id, _) | tcx. def_span ( id. to_def_id ( ) ) )
123
+ . collect ( ) ,
124
+ ) ;
125
+ let mut suggestion = Vec :: with_capacity ( show_cycle_len * 2 ) ;
126
+ for i in 0 ..show_cycle_len {
127
+ let ( _, field_id) = item_and_field_ids[ i] ;
128
+ let ( next_item_id, _) = item_and_field_ids[ ( i + 1 ) % cycle_len] ;
129
+ // Find the span(s) that contain the next item in the cycle
130
+ let hir_id = tcx. hir ( ) . local_def_id_to_hir_id ( field_id) ;
131
+ let hir:: Node :: Field ( field) = tcx. hir ( ) . get ( hir_id) else { bug ! ( "expected field" ) } ;
132
+ let mut found = Vec :: new ( ) ;
133
+ find_item_ty_spans ( tcx, field. ty , next_item_id, & mut found, representable_ids) ;
134
+
135
+ // Couldn't find the type. Maybe it's behind a type alias?
136
+ // In any case, we'll just suggest boxing the whole field.
137
+ if found. is_empty ( ) {
138
+ found. push ( field. ty . span ) ;
139
+ }
140
+
141
+ for span in found {
142
+ err_span. push_span_label ( span, "recursive without indirection" ) ;
143
+ // FIXME(compiler-errors): This suggestion might be erroneous if Box is shadowed
144
+ suggestion. push ( ( span. shrink_to_lo ( ) , "Box<" . to_string ( ) ) ) ;
145
+ suggestion. push ( ( span. shrink_to_hi ( ) , ">" . to_string ( ) ) ) ;
146
+ }
147
+ }
148
+ let items_list = {
149
+ let mut s = String :: new ( ) ;
150
+ for ( i, ( item_id, _) ) in item_and_field_ids. iter ( ) . enumerate ( ) {
151
+ let path = tcx. def_path_str ( item_id. to_def_id ( ) ) ;
152
+ write ! ( & mut s, "`{path}`" ) . unwrap ( ) ;
153
+ if i == ( ITEM_LIMIT - 1 ) && cycle_len > ITEM_LIMIT {
154
+ write ! ( & mut s, " and {} more" , cycle_len - 5 ) . unwrap ( ) ;
155
+ break ;
156
+ }
157
+ if cycle_len > 1 && i < cycle_len - 2 {
158
+ s. push_str ( ", " ) ;
159
+ } else if cycle_len > 1 && i == cycle_len - 2 {
160
+ s. push_str ( " and " )
161
+ }
162
+ }
163
+ s
164
+ } ;
165
+ let mut err = struct_span_err ! (
166
+ tcx. sess,
167
+ err_span,
168
+ E0072 ,
169
+ "recursive type{} {} {} infinite size" ,
170
+ pluralize!( cycle_len) ,
171
+ items_list,
172
+ pluralize!( "has" , cycle_len) ,
173
+ ) ;
174
+ err. multipart_suggestion (
175
+ "insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle" ,
176
+ suggestion,
177
+ Applicability :: HasPlaceholders ,
178
+ ) ;
179
+ err. emit ( ) ;
180
+ }
181
+
182
+ fn find_item_ty_spans (
183
+ tcx : TyCtxt < ' _ > ,
184
+ ty : & hir:: Ty < ' _ > ,
185
+ needle : LocalDefId ,
186
+ spans : & mut Vec < Span > ,
187
+ seen_representable : & FxHashSet < LocalDefId > ,
188
+ ) {
189
+ match ty. kind {
190
+ hir:: TyKind :: Path ( hir:: QPath :: Resolved ( _, path) ) => {
191
+ if let Some ( def_id) = path. res . opt_def_id ( ) {
192
+ let check_params = def_id. as_local ( ) . map_or ( true , |def_id| {
193
+ if def_id == needle {
194
+ spans. push ( ty. span ) ;
195
+ }
196
+ seen_representable. contains ( & def_id)
197
+ } ) ;
198
+ if check_params && let Some ( args) = path. segments . last ( ) . unwrap ( ) . args {
199
+ let params_in_repr = tcx. params_in_repr ( def_id) ;
200
+ for ( i, arg) in args. args . iter ( ) . enumerate ( ) {
201
+ if let hir:: GenericArg :: Type ( ty) = arg && params_in_repr. contains ( i as u32 ) {
202
+ find_item_ty_spans ( tcx, ty, needle, spans, seen_representable) ;
203
+ }
204
+ }
205
+ }
206
+ }
207
+ }
208
+ hir:: TyKind :: Array ( ty, _) => find_item_ty_spans ( tcx, ty, needle, spans, seen_representable) ,
209
+ hir:: TyKind :: Tup ( tys) => {
210
+ tys. iter ( ) . for_each ( |ty| find_item_ty_spans ( tcx, ty, needle, spans, seen_representable) )
211
+ }
212
+ _ => { }
213
+ }
214
+ }
0 commit comments