about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume1.gomez@gmail.com>2022-05-07 15:23:47 +0200
committerGitHub <noreply@github.com>2022-05-07 15:23:47 +0200
commit27a2bae89ced19bee662c5caccd20f9e111a7d54 (patch)
treecb1409837bf07c31580cb61ebbf64f167f23b29f
parenta6441973914de920db875f6968cd9e080ae87a6f (diff)
parentc4168fdb50bd3d50a1729ae9af3ca4921841c35a (diff)
downloadrust-27a2bae89ced19bee662c5caccd20f9e111a7d54.tar.gz
rust-27a2bae89ced19bee662c5caccd20f9e111a7d54.zip
Rollup merge of #96788 - JakobDegen:checked-binop, r=oli-obk
Improve validator around field projections and checked bin ops

The two commits are unrelated. In both cases, these rules were already documented in MIR docs.
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs100
1 files changed, 96 insertions, 4 deletions
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index f71bc586b48..25209e20e99 100644
--- a/compiler/rustc_const_eval/src/transform/validate.rs
+++ b/compiler/rustc_const_eval/src/transform/validate.rs
@@ -14,7 +14,7 @@ use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt, TypeFoldable};
 use rustc_mir_dataflow::impls::MaybeStorageLive;
 use rustc_mir_dataflow::storage::AlwaysLiveLocals;
 use rustc_mir_dataflow::{Analysis, ResultsCursor};
-use rustc_target::abi::Size;
+use rustc_target::abi::{Size, VariantIdx};
 
 #[derive(Copy, Clone, Debug)]
 enum EdgeKind {
@@ -244,6 +244,60 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                 self.fail(location, format!("bad index ({:?} != usize)", index_ty))
             }
         }
+        if let ProjectionElem::Field(f, ty) = elem {
+            let parent = Place { local, projection: self.tcx.intern_place_elems(proj_base) };
+            let parent_ty = parent.ty(&self.body.local_decls, self.tcx);
+            let fail_out_of_bounds = |this: &Self, location| {
+                this.fail(location, format!("Out of bounds field {:?} for {:?}", f, parent_ty));
+            };
+            let check_equal = |this: &Self, location, f_ty| {
+                if !this.mir_assign_valid_types(ty, f_ty) {
+                    this.fail(
+                        location,
+                        format!(
+                            "Field projection `{:?}.{:?}` specified type `{:?}`, but actual type is {:?}",
+                            parent, f, ty, f_ty
+                        )
+                    )
+                }
+            };
+            match parent_ty.ty.kind() {
+                ty::Tuple(fields) => {
+                    let Some(f_ty) = fields.get(f.as_usize()) else {
+                        fail_out_of_bounds(self, location);
+                        return;
+                    };
+                    check_equal(self, location, *f_ty);
+                }
+                ty::Adt(adt_def, substs) => {
+                    let var = parent_ty.variant_index.unwrap_or(VariantIdx::from_u32(0));
+                    let Some(field) = adt_def.variant(var).fields.get(f.as_usize()) else {
+                        fail_out_of_bounds(self, location);
+                        return;
+                    };
+                    check_equal(self, location, field.ty(self.tcx, substs));
+                }
+                ty::Closure(_, substs) => {
+                    let substs = substs.as_closure();
+                    let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else {
+                        fail_out_of_bounds(self, location);
+                        return;
+                    };
+                    check_equal(self, location, f_ty);
+                }
+                ty::Generator(_, substs, _) => {
+                    let substs = substs.as_generator();
+                    let Some(f_ty) = substs.upvar_tys().nth(f.as_usize()) else {
+                        fail_out_of_bounds(self, location);
+                        return;
+                    };
+                    check_equal(self, location, f_ty);
+                }
+                _ => {
+                    self.fail(location, format!("{:?} does not have fields", parent_ty.ty));
+                }
+            }
+        }
         self.super_projection_elem(local, proj_base, elem, context, location);
     }
 
@@ -291,7 +345,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                     ty::Array(..) | ty::Slice(..)
                 );
             }
-            Rvalue::BinaryOp(op, vals) | Rvalue::CheckedBinaryOp(op, vals) => {
+            Rvalue::BinaryOp(op, vals) => {
                 use BinOp::*;
                 let a = vals.0.ty(&self.body.local_decls, self.tcx);
                 let b = vals.1.ty(&self.body.local_decls, self.tcx);
@@ -355,17 +409,55 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
                         for x in [a, b] {
                             check_kinds!(
                                 x,
-                                "Cannot perform op on type {:?}",
+                                "Cannot perform arithmetic on type {:?}",
                                 ty::Uint(..) | ty::Int(..) | ty::Float(..)
                             )
                         }
                         if a != b {
                             self.fail(
                                 location,
-                                format!("Cannot perform op on unequal types {:?} and {:?}", a, b),
+                                format!(
+                                    "Cannot perform arithmetic on unequal types {:?} and {:?}",
+                                    a, b
+                                ),
+                            );
+                        }
+                    }
+                }
+            }
+            Rvalue::CheckedBinaryOp(op, vals) => {
+                use BinOp::*;
+                let a = vals.0.ty(&self.body.local_decls, self.tcx);
+                let b = vals.1.ty(&self.body.local_decls, self.tcx);
+                match op {
+                    Add | Sub | Mul => {
+                        for x in [a, b] {
+                            check_kinds!(
+                                x,
+                                "Cannot perform checked arithmetic on type {:?}",
+                                ty::Uint(..) | ty::Int(..)
+                            )
+                        }
+                        if a != b {
+                            self.fail(
+                                location,
+                                format!(
+                                    "Cannot perform checked arithmetic on unequal types {:?} and {:?}",
+                                    a, b
+                                ),
                             );
                         }
                     }
+                    Shl | Shr => {
+                        for x in [a, b] {
+                            check_kinds!(
+                                x,
+                                "Cannot perform checked shift on non-integer type {:?}",
+                                ty::Uint(..) | ty::Int(..)
+                            )
+                        }
+                    }
+                    _ => self.fail(location, format!("There is no checked version of {:?}", op)),
                 }
             }
             Rvalue::UnaryOp(op, operand) => {