]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_const_eval/src/transform/validate.rs
:arrow_up: rust-analyzer
[rust.git] / compiler / rustc_const_eval / src / transform / validate.rs
1 //! Validates the MIR to ensure that invariants are upheld.
2
3 use rustc_data_structures::fx::FxHashSet;
4 use rustc_index::bit_set::BitSet;
5 use rustc_infer::infer::TyCtxtInferExt;
6 use rustc_middle::mir::interpret::Scalar;
7 use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
8 use rustc_middle::mir::visit::{PlaceContext, Visitor};
9 use rustc_middle::mir::{
10     traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, CastKind, CopyNonOverlapping,
11     Local, Location, MirPass, MirPhase, NonDivergingIntrinsic, Operand, Place, PlaceElem, PlaceRef,
12     ProjectionElem, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind, Terminator,
13     TerminatorKind, UnOp, START_BLOCK,
14 };
15 use rustc_middle::ty::fold::BottomUpFolder;
16 use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable, TypeVisitable};
17 use rustc_mir_dataflow::impls::MaybeStorageLive;
18 use rustc_mir_dataflow::storage::always_storage_live_locals;
19 use rustc_mir_dataflow::{Analysis, ResultsCursor};
20 use rustc_target::abi::{Size, VariantIdx};
21
22 #[derive(Copy, Clone, Debug)]
23 enum EdgeKind {
24     Unwind,
25     Normal,
26 }
27
28 pub struct Validator {
29     /// Describes at which point in the pipeline this validation is happening.
30     pub when: String,
31     /// The phase for which we are upholding the dialect. If the given phase forbids a specific
32     /// element, this validator will now emit errors if that specific element is encountered.
33     /// Note that phases that change the dialect cause all *following* phases to check the
34     /// invariants of the new dialect. A phase that changes dialects never checks the new invariants
35     /// itself.
36     pub mir_phase: MirPhase,
37 }
38
39 impl<'tcx> MirPass<'tcx> for Validator {
40     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
41         // FIXME(JakobDegen): These bodies never instantiated in codegend anyway, so it's not
42         // terribly important that they pass the validator. However, I think other passes might
43         // still see them, in which case they might be surprised. It would probably be better if we
44         // didn't put this through the MIR pipeline at all.
45         if matches!(body.source.instance, InstanceDef::Intrinsic(..) | InstanceDef::Virtual(..)) {
46             return;
47         }
48         let def_id = body.source.def_id();
49         let param_env = tcx.param_env(def_id);
50         let mir_phase = self.mir_phase;
51
52         let always_live_locals = always_storage_live_locals(body);
53         let storage_liveness = MaybeStorageLive::new(always_live_locals)
54             .into_engine(tcx, body)
55             .iterate_to_fixpoint()
56             .into_results_cursor(body);
57
58         TypeChecker {
59             when: &self.when,
60             body,
61             tcx,
62             param_env,
63             mir_phase,
64             reachable_blocks: traversal::reachable_as_bitset(body),
65             storage_liveness,
66             place_cache: Vec::new(),
67             value_cache: Vec::new(),
68         }
69         .visit_body(body);
70     }
71 }
72
73 /// Returns whether the two types are equal up to lifetimes.
74 /// All lifetimes, including higher-ranked ones, get ignored for this comparison.
75 /// (This is unlike the `erasing_regions` methods, which keep higher-ranked lifetimes for soundness reasons.)
76 ///
77 /// The point of this function is to approximate "equal up to subtyping".  However,
78 /// the approximation is incorrect as variance is ignored.
79 pub fn equal_up_to_regions<'tcx>(
80     tcx: TyCtxt<'tcx>,
81     param_env: ParamEnv<'tcx>,
82     src: Ty<'tcx>,
83     dest: Ty<'tcx>,
84 ) -> bool {
85     // Fast path.
86     if src == dest {
87         return true;
88     }
89
90     // Normalize lifetimes away on both sides, then compare.
91     let normalize = |ty: Ty<'tcx>| {
92         tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty).fold_with(
93             &mut BottomUpFolder {
94                 tcx,
95                 // FIXME: We erase all late-bound lifetimes, but this is not fully correct.
96                 // If you have a type like `<for<'a> fn(&'a u32) as SomeTrait>::Assoc`,
97                 // this is not necessarily equivalent to `<fn(&'static u32) as SomeTrait>::Assoc`,
98                 // since one may have an `impl SomeTrait for fn(&32)` and
99                 // `impl SomeTrait for fn(&'static u32)` at the same time which
100                 // specify distinct values for Assoc. (See also #56105)
101                 lt_op: |_| tcx.lifetimes.re_erased,
102                 // Leave consts and types unchanged.
103                 ct_op: |ct| ct,
104                 ty_op: |ty| ty,
105             },
106         )
107     };
108     tcx.infer_ctxt().build().can_eq(param_env, normalize(src), normalize(dest)).is_ok()
109 }
110
111 struct TypeChecker<'a, 'tcx> {
112     when: &'a str,
113     body: &'a Body<'tcx>,
114     tcx: TyCtxt<'tcx>,
115     param_env: ParamEnv<'tcx>,
116     mir_phase: MirPhase,
117     reachable_blocks: BitSet<BasicBlock>,
118     storage_liveness: ResultsCursor<'a, 'tcx, MaybeStorageLive>,
119     place_cache: Vec<PlaceRef<'tcx>>,
120     value_cache: Vec<u128>,
121 }
122
123 impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
124     fn fail(&self, location: Location, msg: impl AsRef<str>) {
125         let span = self.body.source_info(location).span;
126         // We use `delay_span_bug` as we might see broken MIR when other errors have already
127         // occurred.
128         self.tcx.sess.diagnostic().delay_span_bug(
129             span,
130             &format!(
131                 "broken MIR in {:?} ({}) at {:?}:\n{}",
132                 self.body.source.instance,
133                 self.when,
134                 location,
135                 msg.as_ref()
136             ),
137         );
138     }
139
140     fn check_edge(&self, location: Location, bb: BasicBlock, edge_kind: EdgeKind) {
141         if bb == START_BLOCK {
142             self.fail(location, "start block must not have predecessors")
143         }
144         if let Some(bb) = self.body.basic_blocks.get(bb) {
145             let src = self.body.basic_blocks.get(location.block).unwrap();
146             match (src.is_cleanup, bb.is_cleanup, edge_kind) {
147                 // Non-cleanup blocks can jump to non-cleanup blocks along non-unwind edges
148                 (false, false, EdgeKind::Normal)
149                 // Non-cleanup blocks can jump to cleanup blocks along unwind edges
150                 | (false, true, EdgeKind::Unwind)
151                 // Cleanup blocks can jump to cleanup blocks along non-unwind edges
152                 | (true, true, EdgeKind::Normal) => {}
153                 // All other jumps are invalid
154                 _ => {
155                     self.fail(
156                         location,
157                         format!(
158                             "{:?} edge to {:?} violates unwind invariants (cleanup {:?} -> {:?})",
159                             edge_kind,
160                             bb,
161                             src.is_cleanup,
162                             bb.is_cleanup,
163                         )
164                     )
165                 }
166             }
167         } else {
168             self.fail(location, format!("encountered jump to invalid basic block {:?}", bb))
169         }
170     }
171
172     /// Check if src can be assigned into dest.
173     /// This is not precise, it will accept some incorrect assignments.
174     fn mir_assign_valid_types(&self, src: Ty<'tcx>, dest: Ty<'tcx>) -> bool {
175         // Fast path before we normalize.
176         if src == dest {
177             // Equal types, all is good.
178             return true;
179         }
180         // Normalization reveals opaque types, but we may be validating MIR while computing
181         // said opaque types, causing cycles.
182         if (src, dest).has_opaque_types() {
183             return true;
184         }
185
186         // Normalize projections and things like that.
187         // Type-changing assignments can happen when subtyping is used. While
188         // all normal lifetimes are erased, higher-ranked types with their
189         // late-bound lifetimes are still around and can lead to type
190         // differences. So we compare ignoring lifetimes.
191
192         // First, try with reveal_all. This might not work in some cases, as the predicates
193         // can be cleared in reveal_all mode. We try the reveal first anyways as it is used
194         // by some other passes like inlining as well.
195         let param_env = self.param_env.with_reveal_all_normalized(self.tcx);
196         if equal_up_to_regions(self.tcx, param_env, src, dest) {
197             return true;
198         }
199
200         // If this fails, we can try it without the reveal.
201         equal_up_to_regions(self.tcx, self.param_env, src, dest)
202     }
203 }
204
205 impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
206     fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
207         if self.body.local_decls.get(local).is_none() {
208             self.fail(
209                 location,
210                 format!("local {:?} has no corresponding declaration in `body.local_decls`", local),
211             );
212         }
213
214         if self.reachable_blocks.contains(location.block) && context.is_use() {
215             // We check that the local is live whenever it is used. Technically, violating this
216             // restriction is only UB and not actually indicative of not well-formed MIR. This means
217             // that an optimization which turns MIR that already has UB into MIR that fails this
218             // check is not necessarily wrong. However, we have no such optimizations at the moment,
219             // and so we include this check anyway to help us catch bugs. If you happen to write an
220             // optimization that might cause this to incorrectly fire, feel free to remove this
221             // check.
222             self.storage_liveness.seek_after_primary_effect(location);
223             let locals_with_storage = self.storage_liveness.get();
224             if !locals_with_storage.contains(local) {
225                 self.fail(location, format!("use of local {:?}, which has no storage here", local));
226             }
227         }
228     }
229
230     fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
231         // This check is somewhat expensive, so only run it when -Zvalidate-mir is passed.
232         if self.tcx.sess.opts.unstable_opts.validate_mir
233             && self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial)
234         {
235             // `Operand::Copy` is only supposed to be used with `Copy` types.
236             if let Operand::Copy(place) = operand {
237                 let ty = place.ty(&self.body.local_decls, self.tcx).ty;
238                 let span = self.body.source_info(location).span;
239
240                 if !ty.is_copy_modulo_regions(self.tcx.at(span), self.param_env) {
241                     self.fail(location, format!("`Operand::Copy` with non-`Copy` type {}", ty));
242                 }
243             }
244         }
245
246         self.super_operand(operand, location);
247     }
248
249     fn visit_projection_elem(
250         &mut self,
251         local: Local,
252         proj_base: &[PlaceElem<'tcx>],
253         elem: PlaceElem<'tcx>,
254         context: PlaceContext,
255         location: Location,
256     ) {
257         match elem {
258             ProjectionElem::Index(index) => {
259                 let index_ty = self.body.local_decls[index].ty;
260                 if index_ty != self.tcx.types.usize {
261                     self.fail(location, format!("bad index ({:?} != usize)", index_ty))
262                 }
263             }
264             ProjectionElem::Deref
265                 if self.mir_phase >= MirPhase::Runtime(RuntimePhase::PostCleanup) =>
266             {
267                 let base_ty = Place::ty_from(local, proj_base, &self.body.local_decls, self.tcx).ty;
268
269                 if base_ty.is_box() {
270                     self.fail(
271                         location,
272                         format!("{:?} dereferenced after ElaborateBoxDerefs", base_ty),
273                     )
274                 }
275             }
276             ProjectionElem::Field(f, ty) => {
277                 let parent = Place { local, projection: self.tcx.intern_place_elems(proj_base) };
278                 let parent_ty = parent.ty(&self.body.local_decls, self.tcx);
279                 let fail_out_of_bounds = |this: &Self, location| {
280                     this.fail(location, format!("Out of bounds field {:?} for {:?}", f, parent_ty));
281                 };
282                 let check_equal = |this: &Self, location, f_ty| {
283                     if !this.mir_assign_valid_types(ty, f_ty) {
284                         this.fail(
285                         location,
286                         format!(
287                             "Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is `{:?}`",
288                             parent, f, ty, f_ty
289                         )
290                     )
291                     }
292                 };
293
294                 let kind = match parent_ty.ty.kind() {
295                     &ty::Opaque(def_id, substs) => {
296                         self.tcx.bound_type_of(def_id).subst(self.tcx, substs).kind()
297                     }
298                     kind => kind,
299                 };
300
301                 match kind {
302                     ty::Tuple(fields) => {
303                         let Some(f_ty) = fields.get(f.as_usize()) else {
304                             fail_out_of_bounds(self, location);
305                             return;
306                         };
307                         check_equal(self, location, *f_ty);
308                     }
309                     ty::Adt(adt_def, substs) => {
310                         let var = parent_ty.variant_index.unwrap_or(VariantIdx::from_u32(0));
311                         let Some(field) = adt_def.variant(var).fields.get(f.as_usize()) else {
312                             fail_out_of_bounds(self, location);
313                             return;
314                         };
315                         check_equal(self, location, field.ty(self.tcx, substs));
316                     }
317                     ty::Closure(_, substs) => {
318                         let substs = substs.as_closure();
319                         let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else {
320                             fail_out_of_bounds(self, location);
321                             return;
322                         };
323                         check_equal(self, location, f_ty);
324                     }
325                     &ty::Generator(def_id, substs, _) => {
326                         let f_ty = if let Some(var) = parent_ty.variant_index {
327                             let gen_body = if def_id == self.body.source.def_id() {
328                                 self.body
329                             } else {
330                                 self.tcx.optimized_mir(def_id)
331                             };
332
333                             let Some(layout) = gen_body.generator_layout() else {
334                                 self.fail(location, format!("No generator layout for {:?}", parent_ty));
335                                 return;
336                             };
337
338                             let Some(&local) = layout.variant_fields[var].get(f) else {
339                                 fail_out_of_bounds(self, location);
340                                 return;
341                             };
342
343                             let Some(&f_ty) = layout.field_tys.get(local) else {
344                                 self.fail(location, format!("Out of bounds local {:?} for {:?}", local, parent_ty));
345                                 return;
346                             };
347
348                             f_ty
349                         } else {
350                             let Some(f_ty) = substs.as_generator().prefix_tys().nth(f.index()) else {
351                                 fail_out_of_bounds(self, location);
352                                 return;
353                             };
354
355                             f_ty
356                         };
357
358                         check_equal(self, location, f_ty);
359                     }
360                     _ => {
361                         self.fail(location, format!("{:?} does not have fields", parent_ty.ty));
362                     }
363                 }
364             }
365             _ => {}
366         }
367         self.super_projection_elem(local, proj_base, elem, context, location);
368     }
369
370     fn visit_place(&mut self, place: &Place<'tcx>, cntxt: PlaceContext, location: Location) {
371         // Set off any `bug!`s in the type computation code
372         let _ = place.ty(&self.body.local_decls, self.tcx);
373
374         if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial)
375             && place.projection.len() > 1
376             && cntxt != PlaceContext::NonUse(VarDebugInfo)
377             && place.projection[1..].contains(&ProjectionElem::Deref)
378         {
379             self.fail(location, format!("{:?}, has deref at the wrong place", place));
380         }
381
382         self.super_place(place, cntxt, location);
383     }
384
385     fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
386         macro_rules! check_kinds {
387             ($t:expr, $text:literal, $($patterns:tt)*) => {
388                 if !matches!(($t).kind(), $($patterns)*) {
389                     self.fail(location, format!($text, $t));
390                 }
391             };
392         }
393         match rvalue {
394             Rvalue::Use(_) | Rvalue::CopyForDeref(_) => {}
395             Rvalue::Aggregate(agg_kind, _) => {
396                 let disallowed = match **agg_kind {
397                     AggregateKind::Array(..) => false,
398                     _ => self.mir_phase >= MirPhase::Runtime(RuntimePhase::PostCleanup),
399                 };
400                 if disallowed {
401                     self.fail(
402                         location,
403                         format!("{:?} have been lowered to field assignments", rvalue),
404                     )
405                 }
406             }
407             Rvalue::Ref(_, BorrowKind::Shallow, _) => {
408                 if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
409                     self.fail(
410                         location,
411                         "`Assign` statement with a `Shallow` borrow should have been removed in runtime MIR",
412                     );
413                 }
414             }
415             Rvalue::Ref(..) => {}
416             Rvalue::Len(p) => {
417                 let pty = p.ty(&self.body.local_decls, self.tcx).ty;
418                 check_kinds!(
419                     pty,
420                     "Cannot compute length of non-array type {:?}",
421                     ty::Array(..) | ty::Slice(..)
422                 );
423             }
424             Rvalue::BinaryOp(op, vals) => {
425                 use BinOp::*;
426                 let a = vals.0.ty(&self.body.local_decls, self.tcx);
427                 let b = vals.1.ty(&self.body.local_decls, self.tcx);
428                 match op {
429                     Offset => {
430                         check_kinds!(a, "Cannot offset non-pointer type {:?}", ty::RawPtr(..));
431                         if b != self.tcx.types.isize && b != self.tcx.types.usize {
432                             self.fail(location, format!("Cannot offset by non-isize type {:?}", b));
433                         }
434                     }
435                     Eq | Lt | Le | Ne | Ge | Gt => {
436                         for x in [a, b] {
437                             check_kinds!(
438                                 x,
439                                 "Cannot compare type {:?}",
440                                 ty::Bool
441                                     | ty::Char
442                                     | ty::Int(..)
443                                     | ty::Uint(..)
444                                     | ty::Float(..)
445                                     | ty::RawPtr(..)
446                                     | ty::FnPtr(..)
447                             )
448                         }
449                         // The function pointer types can have lifetimes
450                         if !self.mir_assign_valid_types(a, b) {
451                             self.fail(
452                                 location,
453                                 format!("Cannot compare unequal types {:?} and {:?}", a, b),
454                             );
455                         }
456                     }
457                     Shl | Shr => {
458                         for x in [a, b] {
459                             check_kinds!(
460                                 x,
461                                 "Cannot shift non-integer type {:?}",
462                                 ty::Uint(..) | ty::Int(..)
463                             )
464                         }
465                     }
466                     BitAnd | BitOr | BitXor => {
467                         for x in [a, b] {
468                             check_kinds!(
469                                 x,
470                                 "Cannot perform bitwise op on type {:?}",
471                                 ty::Uint(..) | ty::Int(..) | ty::Bool
472                             )
473                         }
474                         if a != b {
475                             self.fail(
476                                 location,
477                                 format!(
478                                     "Cannot perform bitwise op on unequal types {:?} and {:?}",
479                                     a, b
480                                 ),
481                             );
482                         }
483                     }
484                     Add | Sub | Mul | Div | Rem => {
485                         for x in [a, b] {
486                             check_kinds!(
487                                 x,
488                                 "Cannot perform arithmetic on type {:?}",
489                                 ty::Uint(..) | ty::Int(..) | ty::Float(..)
490                             )
491                         }
492                         if a != b {
493                             self.fail(
494                                 location,
495                                 format!(
496                                     "Cannot perform arithmetic on unequal types {:?} and {:?}",
497                                     a, b
498                                 ),
499                             );
500                         }
501                     }
502                 }
503             }
504             Rvalue::CheckedBinaryOp(op, vals) => {
505                 use BinOp::*;
506                 let a = vals.0.ty(&self.body.local_decls, self.tcx);
507                 let b = vals.1.ty(&self.body.local_decls, self.tcx);
508                 match op {
509                     Add | Sub | Mul => {
510                         for x in [a, b] {
511                             check_kinds!(
512                                 x,
513                                 "Cannot perform checked arithmetic on type {:?}",
514                                 ty::Uint(..) | ty::Int(..)
515                             )
516                         }
517                         if a != b {
518                             self.fail(
519                                 location,
520                                 format!(
521                                     "Cannot perform checked arithmetic on unequal types {:?} and {:?}",
522                                     a, b
523                                 ),
524                             );
525                         }
526                     }
527                     Shl | Shr => {
528                         for x in [a, b] {
529                             check_kinds!(
530                                 x,
531                                 "Cannot perform checked shift on non-integer type {:?}",
532                                 ty::Uint(..) | ty::Int(..)
533                             )
534                         }
535                     }
536                     _ => self.fail(location, format!("There is no checked version of {:?}", op)),
537                 }
538             }
539             Rvalue::UnaryOp(op, operand) => {
540                 let a = operand.ty(&self.body.local_decls, self.tcx);
541                 match op {
542                     UnOp::Neg => {
543                         check_kinds!(a, "Cannot negate type {:?}", ty::Int(..) | ty::Float(..))
544                     }
545                     UnOp::Not => {
546                         check_kinds!(
547                             a,
548                             "Cannot binary not type {:?}",
549                             ty::Int(..) | ty::Uint(..) | ty::Bool
550                         );
551                     }
552                 }
553             }
554             Rvalue::ShallowInitBox(operand, _) => {
555                 let a = operand.ty(&self.body.local_decls, self.tcx);
556                 check_kinds!(a, "Cannot shallow init type {:?}", ty::RawPtr(..));
557             }
558             Rvalue::Cast(kind, operand, target_type) => {
559                 let op_ty = operand.ty(self.body, self.tcx);
560                 match kind {
561                     CastKind::DynStar => {
562                         // FIXME(dyn-star): make sure nothing needs to be done here.
563                     }
564                     // FIXME: Add Checks for these
565                     CastKind::PointerFromExposedAddress
566                     | CastKind::PointerExposeAddress
567                     | CastKind::Pointer(_) => {}
568                     CastKind::IntToInt | CastKind::IntToFloat => {
569                         let input_valid = op_ty.is_integral() || op_ty.is_char() || op_ty.is_bool();
570                         let target_valid = target_type.is_numeric() || target_type.is_char();
571                         if !input_valid || !target_valid {
572                             self.fail(
573                                 location,
574                                 format!("Wrong cast kind {kind:?} for the type {op_ty}",),
575                             );
576                         }
577                     }
578                     CastKind::FnPtrToPtr | CastKind::PtrToPtr => {
579                         if !(op_ty.is_any_ptr() && target_type.is_unsafe_ptr()) {
580                             self.fail(location, "Can't cast {op_ty} into 'Ptr'");
581                         }
582                     }
583                     CastKind::FloatToFloat | CastKind::FloatToInt => {
584                         if !op_ty.is_floating_point() || !target_type.is_numeric() {
585                             self.fail(
586                                 location,
587                                 format!(
588                                     "Trying to cast non 'Float' as {kind:?} into {target_type:?}"
589                                 ),
590                             );
591                         }
592                     }
593                 }
594             }
595             Rvalue::Repeat(_, _)
596             | Rvalue::ThreadLocalRef(_)
597             | Rvalue::AddressOf(_, _)
598             | Rvalue::NullaryOp(_, _)
599             | Rvalue::Discriminant(_) => {}
600         }
601         self.super_rvalue(rvalue, location);
602     }
603
604     fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
605         match &statement.kind {
606             StatementKind::Assign(box (dest, rvalue)) => {
607                 // LHS and RHS of the assignment must have the same type.
608                 let left_ty = dest.ty(&self.body.local_decls, self.tcx).ty;
609                 let right_ty = rvalue.ty(&self.body.local_decls, self.tcx);
610                 if !self.mir_assign_valid_types(right_ty, left_ty) {
611                     self.fail(
612                         location,
613                         format!(
614                             "encountered `{:?}` with incompatible types:\n\
615                             left-hand side has type: {}\n\
616                             right-hand side has type: {}",
617                             statement.kind, left_ty, right_ty,
618                         ),
619                     );
620                 }
621                 if let Rvalue::CopyForDeref(place) = rvalue {
622                     if !place.ty(&self.body.local_decls, self.tcx).ty.builtin_deref(true).is_some()
623                     {
624                         self.fail(
625                             location,
626                             "`CopyForDeref` should only be used for dereferenceable types",
627                         )
628                     }
629                 }
630                 // FIXME(JakobDegen): Check this for all rvalues, not just this one.
631                 if let Rvalue::Use(Operand::Copy(src) | Operand::Move(src)) = rvalue {
632                     // The sides of an assignment must not alias. Currently this just checks whether
633                     // the places are identical.
634                     if dest == src {
635                         self.fail(
636                             location,
637                             "encountered `Assign` statement with overlapping memory",
638                         );
639                     }
640                 }
641             }
642             StatementKind::AscribeUserType(..) => {
643                 if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
644                     self.fail(
645                         location,
646                         "`AscribeUserType` should have been removed after drop lowering phase",
647                     );
648                 }
649             }
650             StatementKind::FakeRead(..) => {
651                 if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
652                     self.fail(
653                         location,
654                         "`FakeRead` should have been removed after drop lowering phase",
655                     );
656                 }
657             }
658             StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => {
659                 let ty = op.ty(&self.body.local_decls, self.tcx);
660                 if !ty.is_bool() {
661                     self.fail(
662                         location,
663                         format!("`assume` argument must be `bool`, but got: `{}`", ty),
664                     );
665                 }
666             }
667             StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(
668                 CopyNonOverlapping { src, dst, count },
669             )) => {
670                 let src_ty = src.ty(&self.body.local_decls, self.tcx);
671                 let op_src_ty = if let Some(src_deref) = src_ty.builtin_deref(true) {
672                     src_deref.ty
673                 } else {
674                     self.fail(
675                         location,
676                         format!("Expected src to be ptr in copy_nonoverlapping, got: {}", src_ty),
677                     );
678                     return;
679                 };
680                 let dst_ty = dst.ty(&self.body.local_decls, self.tcx);
681                 let op_dst_ty = if let Some(dst_deref) = dst_ty.builtin_deref(true) {
682                     dst_deref.ty
683                 } else {
684                     self.fail(
685                         location,
686                         format!("Expected dst to be ptr in copy_nonoverlapping, got: {}", dst_ty),
687                     );
688                     return;
689                 };
690                 // since CopyNonOverlapping is parametrized by 1 type,
691                 // we only need to check that they are equal and not keep an extra parameter.
692                 if !self.mir_assign_valid_types(op_src_ty, op_dst_ty) {
693                     self.fail(location, format!("bad arg ({:?} != {:?})", op_src_ty, op_dst_ty));
694                 }
695
696                 let op_cnt_ty = count.ty(&self.body.local_decls, self.tcx);
697                 if op_cnt_ty != self.tcx.types.usize {
698                     self.fail(location, format!("bad arg ({:?} != usize)", op_cnt_ty))
699                 }
700             }
701             StatementKind::SetDiscriminant { place, .. } => {
702                 if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
703                     self.fail(location, "`SetDiscriminant`is not allowed until deaggregation");
704                 }
705                 let pty = place.ty(&self.body.local_decls, self.tcx).ty.kind();
706                 if !matches!(pty, ty::Adt(..) | ty::Generator(..) | ty::Opaque(..)) {
707                     self.fail(
708                         location,
709                         format!(
710                             "`SetDiscriminant` is only allowed on ADTs and generators, not {:?}",
711                             pty
712                         ),
713                     );
714                 }
715             }
716             StatementKind::Deinit(..) => {
717                 if self.mir_phase < MirPhase::Runtime(RuntimePhase::Initial) {
718                     self.fail(location, "`Deinit`is not allowed until deaggregation");
719                 }
720             }
721             StatementKind::Retag(_, _) => {
722                 // FIXME(JakobDegen) The validator should check that `self.mir_phase <
723                 // DropsLowered`. However, this causes ICEs with generation of drop shims, which
724                 // seem to fail to set their `MirPhase` correctly.
725             }
726             StatementKind::StorageLive(..)
727             | StatementKind::StorageDead(..)
728             | StatementKind::Coverage(_)
729             | StatementKind::Nop => {}
730         }
731
732         self.super_statement(statement, location);
733     }
734
735     fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
736         match &terminator.kind {
737             TerminatorKind::Goto { target } => {
738                 self.check_edge(location, *target, EdgeKind::Normal);
739             }
740             TerminatorKind::SwitchInt { targets, switch_ty, discr } => {
741                 let ty = discr.ty(&self.body.local_decls, self.tcx);
742                 if ty != *switch_ty {
743                     self.fail(
744                         location,
745                         format!(
746                             "encountered `SwitchInt` terminator with type mismatch: {:?} != {:?}",
747                             ty, switch_ty,
748                         ),
749                     );
750                 }
751
752                 let target_width = self.tcx.sess.target.pointer_width;
753
754                 let size = Size::from_bits(match switch_ty.kind() {
755                     ty::Uint(uint) => uint.normalize(target_width).bit_width().unwrap(),
756                     ty::Int(int) => int.normalize(target_width).bit_width().unwrap(),
757                     ty::Char => 32,
758                     ty::Bool => 1,
759                     other => bug!("unhandled type: {:?}", other),
760                 });
761
762                 for (value, target) in targets.iter() {
763                     if Scalar::<()>::try_from_uint(value, size).is_none() {
764                         self.fail(
765                             location,
766                             format!("the value {:#x} is not a proper {:?}", value, switch_ty),
767                         )
768                     }
769
770                     self.check_edge(location, target, EdgeKind::Normal);
771                 }
772                 self.check_edge(location, targets.otherwise(), EdgeKind::Normal);
773
774                 self.value_cache.clear();
775                 self.value_cache.extend(targets.iter().map(|(value, _)| value));
776                 let all_len = self.value_cache.len();
777                 self.value_cache.sort_unstable();
778                 self.value_cache.dedup();
779                 let has_duplicates = all_len != self.value_cache.len();
780                 if has_duplicates {
781                     self.fail(
782                         location,
783                         format!(
784                             "duplicated values in `SwitchInt` terminator: {:?}",
785                             terminator.kind,
786                         ),
787                     );
788                 }
789             }
790             TerminatorKind::Drop { target, unwind, .. } => {
791                 self.check_edge(location, *target, EdgeKind::Normal);
792                 if let Some(unwind) = unwind {
793                     self.check_edge(location, *unwind, EdgeKind::Unwind);
794                 }
795             }
796             TerminatorKind::DropAndReplace { target, unwind, .. } => {
797                 if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
798                     self.fail(
799                         location,
800                         "`DropAndReplace` should have been removed during drop elaboration",
801                     );
802                 }
803                 self.check_edge(location, *target, EdgeKind::Normal);
804                 if let Some(unwind) = unwind {
805                     self.check_edge(location, *unwind, EdgeKind::Unwind);
806                 }
807             }
808             TerminatorKind::Call { func, args, destination, target, cleanup, .. } => {
809                 let func_ty = func.ty(&self.body.local_decls, self.tcx);
810                 match func_ty.kind() {
811                     ty::FnPtr(..) | ty::FnDef(..) => {}
812                     _ => self.fail(
813                         location,
814                         format!("encountered non-callable type {} in `Call` terminator", func_ty),
815                     ),
816                 }
817                 if let Some(target) = target {
818                     self.check_edge(location, *target, EdgeKind::Normal);
819                 }
820                 if let Some(cleanup) = cleanup {
821                     self.check_edge(location, *cleanup, EdgeKind::Unwind);
822                 }
823
824                 // The call destination place and Operand::Move place used as an argument might be
825                 // passed by a reference to the callee. Consequently they must be non-overlapping.
826                 // Currently this simply checks for duplicate places.
827                 self.place_cache.clear();
828                 self.place_cache.push(destination.as_ref());
829                 for arg in args {
830                     if let Operand::Move(place) = arg {
831                         self.place_cache.push(place.as_ref());
832                     }
833                 }
834                 let all_len = self.place_cache.len();
835                 let mut dedup = FxHashSet::default();
836                 self.place_cache.retain(|p| dedup.insert(*p));
837                 let has_duplicates = all_len != self.place_cache.len();
838                 if has_duplicates {
839                     self.fail(
840                         location,
841                         format!(
842                             "encountered overlapping memory in `Call` terminator: {:?}",
843                             terminator.kind,
844                         ),
845                     );
846                 }
847             }
848             TerminatorKind::Assert { cond, target, cleanup, .. } => {
849                 let cond_ty = cond.ty(&self.body.local_decls, self.tcx);
850                 if cond_ty != self.tcx.types.bool {
851                     self.fail(
852                         location,
853                         format!(
854                             "encountered non-boolean condition of type {} in `Assert` terminator",
855                             cond_ty
856                         ),
857                     );
858                 }
859                 self.check_edge(location, *target, EdgeKind::Normal);
860                 if let Some(cleanup) = cleanup {
861                     self.check_edge(location, *cleanup, EdgeKind::Unwind);
862                 }
863             }
864             TerminatorKind::Yield { resume, drop, .. } => {
865                 if self.body.generator.is_none() {
866                     self.fail(location, "`Yield` cannot appear outside generator bodies");
867                 }
868                 if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
869                     self.fail(location, "`Yield` should have been replaced by generator lowering");
870                 }
871                 self.check_edge(location, *resume, EdgeKind::Normal);
872                 if let Some(drop) = drop {
873                     self.check_edge(location, *drop, EdgeKind::Normal);
874                 }
875             }
876             TerminatorKind::FalseEdge { real_target, imaginary_target } => {
877                 if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
878                     self.fail(
879                         location,
880                         "`FalseEdge` should have been removed after drop elaboration",
881                     );
882                 }
883                 self.check_edge(location, *real_target, EdgeKind::Normal);
884                 self.check_edge(location, *imaginary_target, EdgeKind::Normal);
885             }
886             TerminatorKind::FalseUnwind { real_target, unwind } => {
887                 if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
888                     self.fail(
889                         location,
890                         "`FalseUnwind` should have been removed after drop elaboration",
891                     );
892                 }
893                 self.check_edge(location, *real_target, EdgeKind::Normal);
894                 if let Some(unwind) = unwind {
895                     self.check_edge(location, *unwind, EdgeKind::Unwind);
896                 }
897             }
898             TerminatorKind::InlineAsm { destination, cleanup, .. } => {
899                 if let Some(destination) = destination {
900                     self.check_edge(location, *destination, EdgeKind::Normal);
901                 }
902                 if let Some(cleanup) = cleanup {
903                     self.check_edge(location, *cleanup, EdgeKind::Unwind);
904                 }
905             }
906             TerminatorKind::GeneratorDrop => {
907                 if self.body.generator.is_none() {
908                     self.fail(location, "`GeneratorDrop` cannot appear outside generator bodies");
909                 }
910                 if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
911                     self.fail(
912                         location,
913                         "`GeneratorDrop` should have been replaced by generator lowering",
914                     );
915                 }
916             }
917             TerminatorKind::Resume | TerminatorKind::Abort => {
918                 let bb = location.block;
919                 if !self.body.basic_blocks[bb].is_cleanup {
920                     self.fail(location, "Cannot `Resume` or `Abort` from non-cleanup basic block")
921                 }
922             }
923             TerminatorKind::Return => {
924                 let bb = location.block;
925                 if self.body.basic_blocks[bb].is_cleanup {
926                     self.fail(location, "Cannot `Return` from cleanup basic block")
927                 }
928             }
929             TerminatorKind::Unreachable => {}
930         }
931
932         self.super_terminator(terminator, location);
933     }
934
935     fn visit_source_scope(&mut self, scope: SourceScope) {
936         if self.body.source_scopes.get(scope).is_none() {
937             self.tcx.sess.diagnostic().delay_span_bug(
938                 self.body.span,
939                 &format!(
940                     "broken MIR in {:?} ({}):\ninvalid source scope {:?}",
941                     self.body.source.instance, self.when, scope,
942                 ),
943             );
944         }
945     }
946 }