From 19c17360d96820f581e29790dccfd916eebcabf2 Mon Sep 17 00:00:00 2001 From: Paul Daniel Faria Date: Fri, 10 Nov 2017 00:07:32 -0500 Subject: [PATCH] Check rvalue aggregates during check_stmt in tycheck, add initial, (not passing) test --- src/librustc_mir/transform/type_check.rs | 86 +++++++++++++++++++ .../compile-fail/aggregate-rvalues-typeck.rs | 23 +++++ 2 files changed, 109 insertions(+) create mode 100644 src/test/compile-fail/aggregate-rvalues-typeck.rs diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index cc6b70209031..7a801d887fbb 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>, field: usize, location: Location) + -> Result, 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 000000000000..693d7f9821ad --- /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 or the MIT license +// , 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