1212#![ allow( unreachable_code) ]
1313
1414use rustc:: middle:: infer;
15+ use rustc:: middle:: traits;
1516use rustc:: middle:: ty:: { self , Ty } ;
1617use rustc:: middle:: ty:: fold:: TypeFoldable ;
1718use rustc:: mir:: repr:: * ;
19+ use rustc:: mir:: tcx:: LvalueTy ;
1820use rustc:: mir:: transform:: MirPass ;
1921use rustc:: mir:: visit:: { self , Visitor } ;
2022
@@ -25,11 +27,27 @@ macro_rules! span_mirbug {
2527 ( $context: expr, $elem: expr, $( $message: tt) * ) => ( {
2628 $context. tcx( ) . sess. span_warn(
2729 $context. last_span,
28- & format!( "broken MIR ({:?}): {:? }" , $elem, format!( $( $message) * ) )
30+ & format!( "broken MIR ({:?}): {}" , $elem, format!( $( $message) * ) )
2931 )
3032 } )
3133}
3234
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+
3351/// Verifies that MIR types are sane to not crash further
3452/// checks.
3553struct TypeVerifier < ' a , ' tcx : ' a > {
@@ -46,11 +64,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> {
4664 }
4765 }
4866
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) ;
5469 }
5570
5671 fn visit_constant ( & mut self , constant : & Constant < ' tcx > ) {
@@ -78,6 +93,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> {
7893 for ( n, tmp_decl) in mir. temp_decls . iter ( ) . enumerate ( ) {
7994 self . sanitize_type ( & ( n, tmp_decl) , tmp_decl. ty ) ;
8095 }
96+ if self . errors_reported {
97+ return ;
98+ }
8199 self . super_mir ( mir) ;
82100 }
83101}
@@ -96,12 +114,201 @@ impl<'a, 'tcx> TypeVerifier<'a, 'tcx> {
96114 self . infcx . tcx
97115 }
98116
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+ }
102244 }
103- span_mirbug ! ( self , parent, "bad type {:?}" , ty) ;
245+ }
246+
247+ fn error ( & mut self ) -> Ty < ' tcx > {
104248 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+ }
105312 }
106313}
107314
0 commit comments