33use rustc_index:: bit_set:: BitSet ;
44use rustc_infer:: infer:: TyCtxtInferExt ;
55use rustc_middle:: mir:: interpret:: Scalar ;
6- use rustc_middle:: mir:: traversal;
76use rustc_middle:: mir:: visit:: { PlaceContext , Visitor } ;
87use 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 ,
1211} ;
1312use 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 } ;
1514use rustc_mir_dataflow:: impls:: MaybeStorageLive ;
1615use rustc_mir_dataflow:: storage:: AlwaysLiveLocals ;
1716use rustc_mir_dataflow:: { Analysis , ResultsCursor } ;
@@ -36,6 +35,13 @@ pub struct Validator {
3635
3736impl < ' tcx > MirPass < ' tcx > for Validator {
3837 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+ }
3945 let def_id = body. source . def_id ( ) ;
4046 let param_env = tcx. param_env ( def_id) ;
4147 let mir_phase = self . mir_phase ;
@@ -240,58 +246,179 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
240246 self . super_projection_elem ( local, proj_base, elem, context, location) ;
241247 }
242248
243- fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
244- match & statement. kind {
245- StatementKind :: Assign ( box ( dest, rvalue) ) => {
246- // LHS and RHS of the assignment must have the same type.
247- let left_ty = dest. ty ( & self . body . local_decls , self . tcx ) . ty ;
248- let right_ty = rvalue. ty ( & self . body . local_decls , self . tcx ) ;
249- if !self . mir_assign_valid_types ( right_ty, left_ty) {
249+ fn visit_place ( & mut self , place : & Place < ' tcx > , _: PlaceContext , _: Location ) {
250+ // Set off any `bug!`s in the type computation code
251+ let _ = place. ty ( & self . body . local_decls , self . tcx ) ;
252+ }
253+
254+ fn visit_rvalue ( & mut self , rvalue : & Rvalue < ' tcx > , location : Location ) {
255+ macro_rules! check_kinds {
256+ ( $t: expr, $text: literal, $( $patterns: tt) * ) => {
257+ if !matches!( ( $t) . kind( ) , $( $patterns) * ) {
258+ self . fail( location, format!( $text, $t) ) ;
259+ }
260+ } ;
261+ }
262+ match rvalue {
263+ Rvalue :: Use ( _) => { }
264+ Rvalue :: Aggregate ( agg_kind, _) => {
265+ let disallowed = match * * agg_kind {
266+ AggregateKind :: Array ( ..) => false ,
267+ AggregateKind :: Generator ( ..) => self . mir_phase >= MirPhase :: GeneratorsLowered ,
268+ _ => self . mir_phase >= MirPhase :: Deaggregated ,
269+ } ;
270+ if disallowed {
250271 self . fail (
251272 location,
252- format ! (
253- "encountered `{:?}` with incompatible types:\n \
254- left-hand side has type: {}\n \
255- right-hand side has type: {}",
256- statement. kind, left_ty, right_ty,
257- ) ,
273+ format ! ( "{:?} have been lowered to field assignments" , rvalue) ,
274+ )
275+ }
276+ }
277+ Rvalue :: Ref ( _, BorrowKind :: Shallow , _) => {
278+ if self . mir_phase >= MirPhase :: DropsLowered {
279+ self . fail (
280+ location,
281+ "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase" ,
258282 ) ;
259283 }
260- match rvalue {
261- // The sides of an assignment must not alias. Currently this just checks whether the places
262- // are identical.
263- Rvalue :: Use ( Operand :: Copy ( src) | Operand :: Move ( src) ) => {
264- if dest == src {
284+ }
285+ Rvalue :: Len ( p) => {
286+ let pty = p. ty ( & self . body . local_decls , self . tcx ) . ty ;
287+ check_kinds ! (
288+ pty,
289+ "Cannot compute length of non-array type {:?}" ,
290+ ty:: Array ( ..) | ty:: Slice ( ..)
291+ ) ;
292+ }
293+ Rvalue :: BinaryOp ( op, vals) | Rvalue :: CheckedBinaryOp ( op, vals) => {
294+ use BinOp :: * ;
295+ let a = vals. 0 . ty ( & self . body . local_decls , self . tcx ) ;
296+ let b = vals. 1 . ty ( & self . body . local_decls , self . tcx ) ;
297+ match op {
298+ Offset => {
299+ check_kinds ! ( a, "Cannot offset non-pointer type {:?}" , ty:: RawPtr ( ..) ) ;
300+ if b != self . tcx . types . isize && b != self . tcx . types . usize {
301+ self . fail ( location, format ! ( "Cannot offset by non-isize type {:?}" , b) ) ;
302+ }
303+ }
304+ Eq | Lt | Le | Ne | Ge | Gt => {
305+ for x in [ a, b] {
306+ check_kinds ! (
307+ x,
308+ "Cannot compare type {:?}" ,
309+ ty:: Bool
310+ | ty:: Char
311+ | ty:: Int ( ..)
312+ | ty:: Uint ( ..)
313+ | ty:: Float ( ..)
314+ | ty:: RawPtr ( ..)
315+ | ty:: FnPtr ( ..)
316+ )
317+ }
318+ // None of the possible types have lifetimes, so we can just compare
319+ // directly
320+ if a != b {
265321 self . fail (
266322 location,
267- "encountered `Assign` statement with overlapping memory" ,
323+ format ! ( "Cannot compare unequal types {:?} and {:?}" , a , b ) ,
268324 ) ;
269325 }
270326 }
271- Rvalue :: Aggregate ( agg_kind, _) => {
272- let disallowed = match * * agg_kind {
273- AggregateKind :: Array ( ..) => false ,
274- AggregateKind :: Generator ( ..) => {
275- self . mir_phase >= MirPhase :: GeneratorsLowered
276- }
277- _ => self . mir_phase >= MirPhase :: Deaggregated ,
278- } ;
279- if disallowed {
327+ Shl | Shr => {
328+ for x in [ a, b] {
329+ check_kinds ! (
330+ x,
331+ "Cannot shift non-integer type {:?}" ,
332+ ty:: Uint ( ..) | ty:: Int ( ..)
333+ )
334+ }
335+ }
336+ BitAnd | BitOr | BitXor => {
337+ for x in [ a, b] {
338+ check_kinds ! (
339+ x,
340+ "Cannot perform bitwise op on type {:?}" ,
341+ ty:: Uint ( ..) | ty:: Int ( ..) | ty:: Bool
342+ )
343+ }
344+ if a != b {
280345 self . fail (
281346 location,
282- format ! ( "{:?} have been lowered to field assignments" , rvalue) ,
283- )
347+ format ! (
348+ "Cannot perform bitwise op on unequal types {:?} and {:?}" ,
349+ a, b
350+ ) ,
351+ ) ;
284352 }
285353 }
286- Rvalue :: Ref ( _, BorrowKind :: Shallow , _) => {
287- if self . mir_phase >= MirPhase :: DropsLowered {
354+ Add | Sub | Mul | Div | Rem => {
355+ for x in [ a, b] {
356+ check_kinds ! (
357+ x,
358+ "Cannot perform op on type {:?}" ,
359+ ty:: Uint ( ..) | ty:: Int ( ..) | ty:: Float ( ..)
360+ )
361+ }
362+ if a != b {
288363 self . fail (
289364 location,
290- "`Assign` statement with a `Shallow` borrow should have been removed after drop lowering phase" ,
365+ format ! ( "Cannot perform op on unequal types {:?} and {:?}" , a , b ) ,
291366 ) ;
292367 }
293368 }
294- _ => { }
369+ }
370+ }
371+ Rvalue :: UnaryOp ( op, operand) => {
372+ let a = operand. ty ( & self . body . local_decls , self . tcx ) ;
373+ match op {
374+ UnOp :: Neg => {
375+ check_kinds ! ( a, "Cannot negate type {:?}" , ty:: Int ( ..) | ty:: Float ( ..) )
376+ }
377+ UnOp :: Not => {
378+ check_kinds ! (
379+ a,
380+ "Cannot binary not type {:?}" ,
381+ ty:: Int ( ..) | ty:: Uint ( ..) | ty:: Bool
382+ ) ;
383+ }
384+ }
385+ }
386+ Rvalue :: ShallowInitBox ( operand, _) => {
387+ let a = operand. ty ( & self . body . local_decls , self . tcx ) ;
388+ check_kinds ! ( a, "Cannot shallow init type {:?}" , ty:: RawPtr ( ..) ) ;
389+ }
390+ _ => { }
391+ }
392+ self . super_rvalue ( rvalue, location) ;
393+ }
394+
395+ fn visit_statement ( & mut self , statement : & Statement < ' tcx > , location : Location ) {
396+ match & statement. kind {
397+ StatementKind :: Assign ( box ( dest, rvalue) ) => {
398+ // LHS and RHS of the assignment must have the same type.
399+ let left_ty = dest. ty ( & self . body . local_decls , self . tcx ) . ty ;
400+ let right_ty = rvalue. ty ( & self . body . local_decls , self . tcx ) ;
401+ if !self . mir_assign_valid_types ( right_ty, left_ty) {
402+ self . fail (
403+ location,
404+ format ! (
405+ "encountered `{:?}` with incompatible types:\n \
406+ left-hand side has type: {}\n \
407+ right-hand side has type: {}",
408+ statement. kind, left_ty, right_ty,
409+ ) ,
410+ ) ;
411+ }
412+ // FIXME(JakobDegen): Check this for all rvalues, not just this one.
413+ if let Rvalue :: Use ( Operand :: Copy ( src) | Operand :: Move ( src) ) = rvalue {
414+ // The sides of an assignment must not alias. Currently this just checks whether
415+ // the places are identical.
416+ if dest == src {
417+ self . fail (
418+ location,
419+ "encountered `Assign` statement with overlapping memory" ,
420+ ) ;
421+ }
295422 }
296423 }
297424 StatementKind :: AscribeUserType ( ..) => {
@@ -512,6 +639,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
512639 }
513640 }
514641 TerminatorKind :: Yield { resume, drop, .. } => {
642+ if self . body . generator . is_none ( ) {
643+ self . fail ( location, "`Yield` cannot appear outside generator bodies" ) ;
644+ }
515645 if self . mir_phase >= MirPhase :: GeneratorsLowered {
516646 self . fail ( location, "`Yield` should have been replaced by generator lowering" ) ;
517647 }
@@ -551,18 +681,29 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
551681 }
552682 }
553683 TerminatorKind :: GeneratorDrop => {
684+ if self . body . generator . is_none ( ) {
685+ self . fail ( location, "`GeneratorDrop` cannot appear outside generator bodies" ) ;
686+ }
554687 if self . mir_phase >= MirPhase :: GeneratorsLowered {
555688 self . fail (
556689 location,
557690 "`GeneratorDrop` should have been replaced by generator lowering" ,
558691 ) ;
559692 }
560693 }
561- // Nothing to validate for these.
562- TerminatorKind :: Resume
563- | TerminatorKind :: Abort
564- | TerminatorKind :: Return
565- | TerminatorKind :: Unreachable => { }
694+ TerminatorKind :: Resume | TerminatorKind :: Abort => {
695+ let bb = location. block ;
696+ if !self . body . basic_blocks ( ) [ bb] . is_cleanup {
697+ self . fail ( location, "Cannot `Resume` or `Abort` from non-cleanup basic block" )
698+ }
699+ }
700+ TerminatorKind :: Return => {
701+ let bb = location. block ;
702+ if self . body . basic_blocks ( ) [ bb] . is_cleanup {
703+ self . fail ( location, "Cannot `Return` from cleanup basic block" )
704+ }
705+ }
706+ TerminatorKind :: Unreachable => { }
566707 }
567708
568709 self . super_terminator ( terminator, location) ;
0 commit comments