about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPaul Daniel Faria <nashenas88@users.noreply.github.com>2017-11-10 00:07:32 -0500
committerNiko Matsakis <niko@alum.mit.edu>2017-11-22 03:51:54 -0500
commit19c17360d96820f581e29790dccfd916eebcabf2 (patch)
treea739077c3d43e8d5cc716e5d3cd15c28141b91b9
parent4fecccbd2e9760cdc082b5030ebf82911b7e3632 (diff)
downloadrust-19c17360d96820f581e29790dccfd916eebcabf2.tar.gz
rust-19c17360d96820f581e29790dccfd916eebcabf2.zip
Check rvalue aggregates during check_stmt in tycheck, add initial, (not passing) test
-rw-r--r--src/librustc_mir/transform/type_check.rs86
-rw-r--r--src/test/compile-fail/aggregate-rvalues-typeck.rs23
2 files changed, 109 insertions, 0 deletions
diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs
index cc6b7020903..7a801d887fb 100644
--- a/src/librustc_mir/transform/type_check.rs
+++ b/src/librustc_mir/transform/type_check.rs
@@ -549,6 +549,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
                         terr
                     );
                 }
+                self.check_rvalue(mir, rv, location);
             }
             StatementKind::SetDiscriminant {
                 ref lvalue,
@@ -1011,6 +1012,91 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
         }
     }
 
+    fn aggregate_field_ty(&mut self, ak: &Box<AggregateKind<'tcx>>, field: usize, location: Location)
+        -> Result<Ty<'tcx>, FieldAccessError>
+    {
+        let tcx = self.tcx();
+
+        let (variant, substs) = match **ak {
+            AggregateKind::Adt(def, variant, substs, _) => { // handle unions?
+                (&def.variants[variant], substs)
+            },
+            AggregateKind::Closure(def_id, substs) => {
+                return match substs.upvar_tys(def_id, tcx).nth(field) {
+                    Some(ty) => Ok(ty),
+                    None => Err(FieldAccessError::OutOfRange {
+                        field_count: substs.upvar_tys(def_id, tcx).count()
+                    }),
+                }
+            },
+            AggregateKind::Generator(def_id, substs, _) => {
+                if let Some(ty) = substs.upvar_tys(def_id, tcx).nth(field) {
+                    return Ok(ty);
+                }
+
+                return match substs.field_tys(def_id, tcx).nth(field) {
+                    Some(ty) => Ok(ty),
+                    None => Err(FieldAccessError::OutOfRange {
+                        field_count: substs.field_tys(def_id, tcx).count() + 1
+                    }),
+                }
+            },
+            AggregateKind::Array(ty) => {
+                return Ok(ty);
+            },
+            AggregateKind::Tuple => {
+                unreachable!("This should have been covered in check_rvalues");
+            },
+        };
+
+        if let Some(field) = variant.fields.get(field) {
+            Ok(self.normalize(&field.ty(tcx, substs), location))
+        } else {
+            Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
+        }
+    }
+
+    #[allow(dead_code)]
+    fn check_rvalue(&mut self, mir: &Mir<'tcx>, rv: &Rvalue<'tcx>, location: Location) {
+        let tcx = self.tcx();
+        match rv {
+            Rvalue::Aggregate(ref ak, ref ops) => {
+                match **ak {
+                    // tuple rvalue field type is always the type of the op. Nothing to check here.
+                    AggregateKind::Tuple => { },
+                    _ => {
+                        for (i, op) in ops.iter().enumerate() {
+                            let field_ty = if let Ok(field_ty) = self.aggregate_field_ty(ak, i, location) {
+                                field_ty
+                            } else {
+                                // TODO(nashenas88) log span_mirbug terr??
+                                continue;
+                            };
+                            let op_ty = match op {
+                                Operand::Consume(lv) => lv.ty(mir, tcx).to_ty(tcx),
+                                Operand::Constant(c) => c.ty,
+                            };
+                            if let Err(_terr) = self.sub_types(op_ty, field_ty, location.at_successor_within_block()) {
+                                // TODO(nashenas88) log span_mirbug terr??
+                            }
+                        }
+                    },
+                }
+            },
+            // FIXME: These other cases have to be implemented in future PRs
+            Rvalue::Use(..) |
+            Rvalue::Repeat(..) |
+            Rvalue::Ref(..) |
+            Rvalue::Len(..) |
+            Rvalue::Cast(..) |
+            Rvalue::BinaryOp(..) |
+            Rvalue::CheckedBinaryOp(..) |
+            Rvalue::UnaryOp(..) |
+            Rvalue::Discriminant(..) |
+            Rvalue::NullaryOp(..) => { }
+        }
+    }
+
     fn typeck_mir(&mut self, mir: &Mir<'tcx>) {
         self.last_span = mir.span;
         debug!("run_on_mir: {:?}", mir.span);
diff --git a/src/test/compile-fail/aggregate-rvalues-typeck.rs b/src/test/compile-fail/aggregate-rvalues-typeck.rs
new file mode 100644
index 00000000000..693d7f9821a
--- /dev/null
+++ b/src/test/compile-fail/aggregate-rvalues-typeck.rs
@@ -0,0 +1,23 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+//compile-flags: -Z emit-end-regions -Z borrowck-mir -Z mir
+
+#![allow(unused_assignments)]
+
+struct Wrap<'a> { w: &'a mut u32 }
+
+fn foo() {
+    let mut x = 22u64;
+    let wrapper = Wrap { w: &mut x };
+    x += 1;  //~ ERROR cannot assign to `x`
+    *wrapper.w += 1;
+}
+
+fn main() { }
\ No newline at end of file