about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJakob Degen <jakob.e.degen@gmail.com>2022-05-06 16:09:59 -0400
committerJakob Degen <jakob.e.degen@gmail.com>2022-05-06 16:43:09 -0400
commitc4168fdb50bd3d50a1729ae9af3ca4921841c35a (patch)
treebefb189a8658e5e614de9d12ac3d22f1e4cfe735
parent5289bbece312fb6704febea1a95a601f0dd27b02 (diff)
downloadrust-c4168fdb50bd3d50a1729ae9af3ca4921841c35a.tar.gz
rust-c4168fdb50bd3d50a1729ae9af3ca4921841c35a.zip
Check that field projections have the correct type
-rw-r--r--compiler/rustc_const_eval/src/transform/validate.rs56
1 files changed, 55 insertions, 1 deletions
diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs
index a790a723305..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);
     }