12
12
#![ allow( unreachable_code) ]
13
13
14
14
use rustc:: middle:: infer;
15
+ use rustc:: middle:: traits;
15
16
use rustc:: middle:: ty:: { self , Ty } ;
16
17
use rustc:: middle:: ty:: fold:: TypeFoldable ;
17
18
use rustc:: mir:: repr:: * ;
19
+ use rustc:: mir:: tcx:: LvalueTy ;
18
20
use rustc:: mir:: transform:: MirPass ;
19
21
use rustc:: mir:: visit:: { self , Visitor } ;
20
22
@@ -25,11 +27,27 @@ macro_rules! span_mirbug {
25
27
( $context: expr, $elem: expr, $( $message: tt) * ) => ( {
26
28
$context. tcx( ) . sess. span_warn(
27
29
$context. last_span,
28
- & format!( "broken MIR ({:?}): {:? }" , $elem, format!( $( $message) * ) )
30
+ & format!( "broken MIR ({:?}): {}" , $elem, format!( $( $message) * ) )
29
31
)
30
32
} )
31
33
}
32
34
35
+ macro_rules! span_mirbug_and_err {
36
+ ( $context: expr, $elem: expr, $( $message: tt) * ) => ( {
37
+ {
38
+ $context. tcx( ) . sess. span_bug(
39
+ $context. last_span,
40
+ & format!( "broken MIR ({:?}): {:?}" , $elem, format!( $( $message) * ) )
41
+ ) ;
42
+ $context. error( )
43
+ }
44
+ } )
45
+ }
46
+
47
+ enum FieldAccessError {
48
+ OutOfRange { field_count : usize }
49
+ }
50
+
33
51
/// Verifies that MIR types are sane to not crash further
34
52
/// checks.
35
53
struct TypeVerifier < ' a , ' tcx : ' a > {
@@ -46,11 +64,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> {
46
64
}
47
65
}
48
66
49
- fn visit_lvalue ( & mut self , lvalue : & Lvalue < ' tcx > , context : visit:: LvalueContext ) {
50
- self . super_lvalue ( lvalue, context) ;
51
- debug ! ( "visiting lvalue {:?}" , lvalue) ;
52
- let lv_ty = self . mir . lvalue_ty ( self . tcx ( ) , lvalue) . to_ty ( self . tcx ( ) ) ;
53
- self . sanitize_type ( lvalue, lv_ty) ;
67
+ fn visit_lvalue ( & mut self , lvalue : & Lvalue < ' tcx > , _context : visit:: LvalueContext ) {
68
+ self . sanitize_lvalue ( lvalue) ;
54
69
}
55
70
56
71
fn visit_constant ( & mut self , constant : & Constant < ' tcx > ) {
@@ -78,6 +93,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> {
78
93
for ( n, tmp_decl) in mir. temp_decls . iter ( ) . enumerate ( ) {
79
94
self . sanitize_type ( & ( n, tmp_decl) , tmp_decl. ty ) ;
80
95
}
96
+ if self . errors_reported {
97
+ return ;
98
+ }
81
99
self . super_mir ( mir) ;
82
100
}
83
101
}
@@ -96,12 +114,201 @@ impl<'a, 'tcx> TypeVerifier<'a, 'tcx> {
96
114
self . infcx . tcx
97
115
}
98
116
99
- fn sanitize_type ( & mut self , parent : & fmt:: Debug , ty : Ty < ' tcx > ) {
100
- if !( ty. needs_infer ( ) || ty. has_escaping_regions ( ) ) {
101
- return ;
117
+ fn sanitize_type ( & mut self , parent : & fmt:: Debug , ty : Ty < ' tcx > ) -> Ty < ' tcx > {
118
+ if !( ty. needs_infer ( ) || ty. has_escaping_regions ( ) ||
119
+ ty. references_error ( ) ) {
120
+ return ty;
121
+ }
122
+ span_mirbug_and_err ! ( self , parent, "bad type {:?}" , ty)
123
+ }
124
+
125
+ fn sanitize_lvalue ( & mut self , lvalue : & Lvalue < ' tcx > ) -> LvalueTy < ' tcx > {
126
+ debug ! ( "sanitize_lvalue: {:?}" , lvalue) ;
127
+ match * lvalue {
128
+ Lvalue :: Var ( index) => LvalueTy :: Ty { ty : self . mir . var_decls [ index as usize ] . ty } ,
129
+ Lvalue :: Temp ( index) =>
130
+ LvalueTy :: Ty { ty : self . mir . temp_decls [ index as usize ] . ty } ,
131
+ Lvalue :: Arg ( index) =>
132
+ LvalueTy :: Ty { ty : self . mir . arg_decls [ index as usize ] . ty } ,
133
+ Lvalue :: Static ( def_id) =>
134
+ LvalueTy :: Ty { ty : self . tcx ( ) . lookup_item_type ( def_id) . ty } ,
135
+ Lvalue :: ReturnPointer => {
136
+ if let ty:: FnConverging ( return_ty) = self . mir . return_ty {
137
+ LvalueTy :: Ty { ty : return_ty }
138
+ } else {
139
+ LvalueTy :: Ty {
140
+ ty : span_mirbug_and_err ! (
141
+ self , lvalue, "return in diverging function" )
142
+ }
143
+ }
144
+ }
145
+ Lvalue :: Projection ( ref proj) => {
146
+ let base_ty = self . sanitize_lvalue ( & proj. base ) ;
147
+ if let LvalueTy :: Ty { ty } = base_ty {
148
+ if ty. references_error ( ) {
149
+ assert ! ( self . errors_reported) ;
150
+ return LvalueTy :: Ty { ty : self . tcx ( ) . types . err } ;
151
+ }
152
+ }
153
+ self . sanitize_projection ( base_ty, & proj. elem , lvalue)
154
+ }
155
+ }
156
+ }
157
+
158
+ fn sanitize_projection ( & mut self ,
159
+ base : LvalueTy < ' tcx > ,
160
+ pi : & LvalueElem < ' tcx > ,
161
+ lvalue : & Lvalue < ' tcx > )
162
+ -> LvalueTy < ' tcx > {
163
+ debug ! ( "sanitize_projection: {:?} {:?} {:?}" , base, pi, lvalue) ;
164
+ let tcx = self . tcx ( ) ;
165
+ let base_ty = base. to_ty ( tcx) ;
166
+ match * pi {
167
+ ProjectionElem :: Deref => {
168
+ let deref_ty = base_ty. builtin_deref ( true , ty:: LvaluePreference :: NoPreference ) ;
169
+ LvalueTy :: Ty {
170
+ ty : deref_ty. map ( |t| t. ty ) . unwrap_or_else ( || {
171
+ span_mirbug_and_err ! (
172
+ self , lvalue, "deref of non-pointer {:?}" , base_ty)
173
+ } )
174
+ }
175
+ }
176
+ ProjectionElem :: Index ( ref i) => {
177
+ self . visit_operand ( i) ;
178
+ let index_ty = self . mir . operand_ty ( tcx, i) ;
179
+ if index_ty != tcx. types . usize {
180
+ LvalueTy :: Ty {
181
+ ty : span_mirbug_and_err ! ( self , i, "index by non-usize {:?}" , i)
182
+ }
183
+ } else {
184
+ LvalueTy :: Ty {
185
+ ty : base_ty. builtin_index ( ) . unwrap_or_else ( || {
186
+ span_mirbug_and_err ! (
187
+ self , lvalue, "index of non-array {:?}" , base_ty)
188
+ } )
189
+ }
190
+ }
191
+ }
192
+ ProjectionElem :: ConstantIndex { .. } => {
193
+ // consider verifying in-bounds
194
+ LvalueTy :: Ty {
195
+ ty : base_ty. builtin_index ( ) . unwrap_or_else ( || {
196
+ span_mirbug_and_err ! (
197
+ self , lvalue, "index of non-array {:?}" , base_ty)
198
+ } )
199
+ }
200
+ }
201
+ ProjectionElem :: Downcast ( adt_def1, index) =>
202
+ match base_ty. sty {
203
+ ty:: TyEnum ( adt_def, substs) if adt_def == adt_def1 => {
204
+ if index >= adt_def. variants . len ( ) {
205
+ LvalueTy :: Ty {
206
+ ty : span_mirbug_and_err ! (
207
+ self ,
208
+ lvalue,
209
+ "cast to variant #{:?} but enum only has {:?}" ,
210
+ index,
211
+ adt_def. variants. len( ) )
212
+ }
213
+ } else {
214
+ LvalueTy :: Downcast {
215
+ adt_def : adt_def,
216
+ substs : substs,
217
+ variant_index : index
218
+ }
219
+ }
220
+ }
221
+ _ => LvalueTy :: Ty {
222
+ ty : span_mirbug_and_err ! (
223
+ self , lvalue, "can't downcast {:?}" , base_ty)
224
+ }
225
+ } ,
226
+ ProjectionElem :: Field ( field, fty) => {
227
+ let fty = self . sanitize_type ( lvalue, fty) ;
228
+ match self . field_ty ( lvalue, base, field) {
229
+ Ok ( ty) => {
230
+ if let Err ( terr) = infer:: can_mk_subty ( self . infcx , ty, fty) {
231
+ span_mirbug ! (
232
+ self , lvalue, "bad field access ({:?}: {:?}): {:?}" ,
233
+ ty, fty, terr) ;
234
+ }
235
+ }
236
+ Err ( FieldAccessError :: OutOfRange { field_count } ) => {
237
+ span_mirbug ! (
238
+ self , lvalue, "accessed field #{} but variant only has {}" ,
239
+ field. index( ) , field_count)
240
+ }
241
+ }
242
+ LvalueTy :: Ty { ty : fty }
243
+ }
102
244
}
103
- span_mirbug ! ( self , parent, "bad type {:?}" , ty) ;
245
+ }
246
+
247
+ fn error ( & mut self ) -> Ty < ' tcx > {
104
248
self . errors_reported = true ;
249
+ self . tcx ( ) . types . err
250
+ }
251
+
252
+ fn field_ty ( & mut self ,
253
+ parent : & fmt:: Debug ,
254
+ base_ty : LvalueTy < ' tcx > ,
255
+ field : Field )
256
+ -> Result < Ty < ' tcx > , FieldAccessError >
257
+ {
258
+ let tcx = self . tcx ( ) ;
259
+
260
+ let ( variant, substs) = match base_ty {
261
+ LvalueTy :: Downcast { adt_def, substs, variant_index } => {
262
+ ( & adt_def. variants [ variant_index] , substs)
263
+ }
264
+ LvalueTy :: Ty { ty } => match ty. sty {
265
+ ty:: TyStruct ( adt_def, substs) | ty:: TyEnum ( adt_def, substs)
266
+ if adt_def. is_univariant ( ) => {
267
+ ( & adt_def. variants [ 0 ] , substs)
268
+ }
269
+ ty:: TyTuple ( ref tys) | ty:: TyClosure ( _, box ty:: ClosureSubsts {
270
+ upvar_tys : ref tys, ..
271
+ } ) => {
272
+ return match tys. get ( field. index ( ) ) {
273
+ Some ( & ty) => Ok ( ty) ,
274
+ None => Err ( FieldAccessError :: OutOfRange {
275
+ field_count : tys. len ( )
276
+ } )
277
+ }
278
+ }
279
+ _ => return Ok ( span_mirbug_and_err ! (
280
+ self , parent, "can't project out of {:?}" , base_ty) )
281
+ }
282
+ } ;
283
+
284
+ if let Some ( field) = variant. fields . get ( field. index ( ) ) {
285
+ Ok ( self . normalize ( parent, field. ty ( tcx, substs) ) )
286
+ } else {
287
+ Err ( FieldAccessError :: OutOfRange { field_count : variant. fields . len ( ) } )
288
+ }
289
+ }
290
+
291
+ fn normalize ( & mut self , parent : & fmt:: Debug , ty : Ty < ' tcx > ) -> Ty < ' tcx > {
292
+ let mut selcx = traits:: SelectionContext :: new ( & self . infcx ) ;
293
+ let cause = traits:: ObligationCause :: misc ( self . last_span , 0 ) ;
294
+ let traits:: Normalized { value : ty, obligations } =
295
+ traits:: normalize ( & mut selcx, cause, & ty) ;
296
+
297
+ debug ! ( "normalize: ty={:?} obligations={:?}" ,
298
+ ty,
299
+ obligations) ;
300
+
301
+ let mut fulfill_cx = self . infcx . fulfillment_cx . borrow_mut ( ) ;
302
+ for obligation in obligations {
303
+ fulfill_cx. register_predicate_obligation ( & self . infcx , obligation) ;
304
+ }
305
+
306
+ match infer:: drain_fulfillment_cx ( & self . infcx , & mut fulfill_cx, & ty) {
307
+ Ok ( ty) => ty,
308
+ Err ( e) => {
309
+ span_mirbug_and_err ! ( self , parent, "trait fulfillment failed: {:?}" , e)
310
+ }
311
+ }
105
312
}
106
313
}
107
314
0 commit comments