@@ -4,14 +4,13 @@ use rustc_index::bit_set::BitSet;
4
4
use rustc_infer:: infer:: TyCtxtInferExt ;
5
5
use rustc_middle:: mir:: interpret:: Scalar ;
6
6
use rustc_middle:: mir:: visit:: { PlaceContext , Visitor } ;
7
- use rustc_middle:: mir:: { traversal, Place } ;
8
7
use rustc_middle:: mir:: {
9
- AggregateKind , BasicBlock , Body , BorrowKind , Local , Location , MirPass , MirPhase , Operand ,
10
- PlaceElem , PlaceRef , ProjectionElem , Rvalue , SourceScope , Statement , StatementKind , Terminator ,
11
- TerminatorKind , START_BLOCK ,
8
+ traversal , AggregateKind , BasicBlock , BinOp , Body , BorrowKind , Local , Location , MirPass ,
9
+ MirPhase , Operand , Place , PlaceElem , PlaceRef , ProjectionElem , Rvalue , SourceScope , Statement ,
10
+ StatementKind , Terminator , TerminatorKind , UnOp , START_BLOCK ,
12
11
} ;
13
12
use rustc_middle:: ty:: fold:: BottomUpFolder ;
14
- use rustc_middle:: ty:: { self , ParamEnv , Ty , TyCtxt , TypeFoldable } ;
13
+ use rustc_middle:: ty:: { self , InstanceDef , ParamEnv , Ty , TyCtxt , TypeFoldable } ;
15
14
use rustc_mir_dataflow:: impls:: MaybeStorageLive ;
16
15
use rustc_mir_dataflow:: storage:: AlwaysLiveLocals ;
17
16
use rustc_mir_dataflow:: { Analysis , ResultsCursor } ;
@@ -36,6 +35,13 @@ pub struct Validator {
36
35
37
36
impl < ' tcx > MirPass < ' tcx > for Validator {
38
37
fn run_pass ( & self , tcx : TyCtxt < ' tcx > , body : & mut Body < ' tcx > ) {
38
+ // FIXME(JakobDegen): These bodies never instantiated in codegend anyway, so it's not
39
+ // terribly important that they pass the validator. However, I think other passes might
40
+ // still see them, in which case they might be surprised. It would probably be better if we
41
+ // didn't put this through the MIR pipeline at all.
42
+ if matches ! ( body. source. instance, InstanceDef :: Intrinsic ( ..) | InstanceDef :: Virtual ( ..) ) {
43
+ return ;
44
+ }
39
45
let def_id = body. source . def_id ( ) ;
40
46
let param_env = tcx. param_env ( def_id) ;
41
47
let mir_phase = self . mir_phase ;
@@ -248,58 +254,174 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
248
254
}
249
255
}
250
256
251
- fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
252
- match & statement. kind {
253
- StatementKind :: Assign ( box ( dest, rvalue) ) => {
254
- // LHS and RHS of the assignment must have the same type.
255
- let left_ty = dest. ty ( & self . body . local_decls , self . tcx ) . ty ;
256
- let right_ty = rvalue. ty ( & self . body . local_decls , self . tcx ) ;
257
- if !self . mir_assign_valid_types ( right_ty, left_ty) {
257
+ fn visit_rvalue ( & mut self , rvalue : & Rvalue < ' tcx > , location : Location ) {
258
+ macro_rules! check_kinds {
259
+ ( $t: expr, $text: literal, $( $patterns: tt) * ) => {
260
+ if !matches!( ( $t) . kind( ) , $( $patterns) * ) {
261
+ self . fail( location, format!( $text, $t) ) ;
262
+ }
263
+ } ;
264
+ }
265
+ match rvalue {
266
+ Rvalue :: Use ( _) => { }
267
+ Rvalue :: Aggregate ( agg_kind, _) => {
268
+ let disallowed = match * * agg_kind {
269
+ AggregateKind :: Array ( ..) => false ,
270
+ AggregateKind :: Generator ( ..) => self . mir_phase >= MirPhase :: GeneratorsLowered ,
271
+ _ => self . mir_phase >= MirPhase :: Deaggregated ,
272
+ } ;
273
+ if disallowed {
258
274
self . fail (
259
275
location,
260
- format ! (
261
- "encountered `{:?}` with incompatible types:\n \
262
- left-hand side has type: {}\n \
263
- right-hand side has type: {}",
264
- statement. kind, left_ty, right_ty,
265
- ) ,
276
+ format ! ( "{:?} have been lowered to field assignments" , rvalue) ,
277
+ )
278
+ }
279
+ }
280
+ Rvalue :: Ref ( _, BorrowKind :: Shallow , _) => {
281
+ if self . mir_phase >= MirPhase :: DropsLowered {
282
+ self . fail (
283
+ location,
284
+ "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase" ,
266
285
) ;
267
286
}
268
- match rvalue {
269
- // The sides of an assignment must not alias. Currently this just checks whether the places
270
- // are identical.
271
- Rvalue :: Use ( Operand :: Copy ( src) | Operand :: Move ( src) ) => {
272
- if dest == src {
287
+ }
288
+ Rvalue :: Len ( p) => {
289
+ let pty = p. ty ( & self . body . local_decls , self . tcx ) . ty ;
290
+ check_kinds ! (
291
+ pty,
292
+ "Cannot compute length of non-array type {:?}" ,
293
+ ty:: Array ( ..) | ty:: Slice ( ..)
294
+ ) ;
295
+ }
296
+ Rvalue :: BinaryOp ( op, vals) | Rvalue :: CheckedBinaryOp ( op, vals) => {
297
+ use BinOp :: * ;
298
+ let a = vals. 0 . ty ( & self . body . local_decls , self . tcx ) ;
299
+ let b = vals. 1 . ty ( & self . body . local_decls , self . tcx ) ;
300
+ match op {
301
+ Offset => {
302
+ check_kinds ! ( a, "Cannot offset non-pointer type {:?}" , ty:: RawPtr ( ..) ) ;
303
+ if b != self . tcx . types . isize && b != self . tcx . types . usize {
304
+ self . fail ( location, format ! ( "Cannot offset by non-isize type {:?}" , b) ) ;
305
+ }
306
+ }
307
+ Eq | Lt | Le | Ne | Ge | Gt => {
308
+ for x in [ a, b] {
309
+ check_kinds ! (
310
+ x,
311
+ "Cannot compare type {:?}" ,
312
+ ty:: Bool
313
+ | ty:: Char
314
+ | ty:: Int ( ..)
315
+ | ty:: Uint ( ..)
316
+ | ty:: Float ( ..)
317
+ | ty:: RawPtr ( ..)
318
+ | ty:: FnPtr ( ..)
319
+ )
320
+ }
321
+ // None of the possible types have lifetimes, so we can just compare
322
+ // directly
323
+ if a != b {
273
324
self . fail (
274
325
location,
275
- "encountered `Assign` statement with overlapping memory" ,
326
+ format ! ( "Cannot compare unequal types {:?} and {:?}" , a , b ) ,
276
327
) ;
277
328
}
278
329
}
279
- Rvalue :: Aggregate ( agg_kind, _) => {
280
- let disallowed = match * * agg_kind {
281
- AggregateKind :: Array ( ..) => false ,
282
- AggregateKind :: Generator ( ..) => {
283
- self . mir_phase >= MirPhase :: GeneratorsLowered
284
- }
285
- _ => self . mir_phase >= MirPhase :: Deaggregated ,
286
- } ;
287
- if disallowed {
330
+ Shl | Shr => {
331
+ for x in [ a, b] {
332
+ check_kinds ! (
333
+ x,
334
+ "Cannot shift non-integer type {:?}" ,
335
+ ty:: Uint ( ..) | ty:: Int ( ..)
336
+ )
337
+ }
338
+ }
339
+ BitAnd | BitOr | BitXor => {
340
+ for x in [ a, b] {
341
+ check_kinds ! (
342
+ x,
343
+ "Cannot perform bitwise op on type {:?}" ,
344
+ ty:: Uint ( ..) | ty:: Int ( ..) | ty:: Bool
345
+ )
346
+ }
347
+ if a != b {
288
348
self . fail (
289
349
location,
290
- format ! ( "{:?} have been lowered to field assignments" , rvalue) ,
291
- )
350
+ format ! (
351
+ "Cannot perform bitwise op on unequal types {:?} and {:?}" ,
352
+ a, b
353
+ ) ,
354
+ ) ;
292
355
}
293
356
}
294
- Rvalue :: Ref ( _, BorrowKind :: Shallow , _) => {
295
- if self . mir_phase >= MirPhase :: DropsLowered {
357
+ Add | Sub | Mul | Div | Rem => {
358
+ for x in [ a, b] {
359
+ check_kinds ! (
360
+ x,
361
+ "Cannot perform op on type {:?}" ,
362
+ ty:: Uint ( ..) | ty:: Int ( ..) | ty:: Float ( ..)
363
+ )
364
+ }
365
+ if a != b {
296
366
self . fail (
297
367
location,
298
- "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase" ,
368
+ format ! ( "Cannot perform op on unequal types {:?} and {:?}" , a , b ) ,
299
369
) ;
300
370
}
301
371
}
302
- _ => { }
372
+ }
373
+ }
374
+ Rvalue :: UnaryOp ( op, operand) => {
375
+ let a = operand. ty ( & self . body . local_decls , self . tcx ) ;
376
+ match op {
377
+ UnOp :: Neg => {
378
+ check_kinds ! ( a, "Cannot negate type {:?}" , ty:: Int ( ..) | ty:: Float ( ..) )
379
+ }
380
+ UnOp :: Not => {
381
+ check_kinds ! (
382
+ a,
383
+ "Cannot binary not type {:?}" ,
384
+ ty:: Int ( ..) | ty:: Uint ( ..) | ty:: Bool
385
+ ) ;
386
+ }
387
+ }
388
+ }
389
+ Rvalue :: ShallowInitBox ( operand, _) => {
390
+ let a = operand. ty ( & self . body . local_decls , self . tcx ) ;
391
+ check_kinds ! ( a, "Cannot shallow init type {:?}" , ty:: RawPtr ( ..) ) ;
392
+ }
393
+ _ => { }
394
+ }
395
+ self . super_rvalue ( rvalue, location) ;
396
+ }
397
+
398
+ fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
399
+ match & statement. kind {
400
+ StatementKind :: Assign ( box ( dest, rvalue) ) => {
401
+ // LHS and RHS of the assignment must have the same type.
402
+ let left_ty = dest. ty ( & self . body . local_decls , self . tcx ) . ty ;
403
+ let right_ty = rvalue. ty ( & self . body . local_decls , self . tcx ) ;
404
+ if !self . mir_assign_valid_types ( right_ty, left_ty) {
405
+ self . fail (
406
+ location,
407
+ format ! (
408
+ "encountered `{:?}` with incompatible types:\n \
409
+ left-hand side has type: {}\n \
410
+ right-hand side has type: {}",
411
+ statement. kind, left_ty, right_ty,
412
+ ) ,
413
+ ) ;
414
+ }
415
+ // FIXME(JakobDegen): Check this for all rvalues, not just this one.
416
+ if let Rvalue :: Use ( Operand :: Copy ( src) | Operand :: Move ( src) ) = rvalue {
417
+ // The sides of an assignment must not alias. Currently this just checks whether
418
+ // the places are identical.
419
+ if dest == src {
420
+ self . fail (
421
+ location,
422
+ "encountered `Assign` statement with overlapping memory" ,
423
+ ) ;
424
+ }
303
425
}
304
426
}
305
427
StatementKind :: AscribeUserType ( ..) => {
0 commit comments