Check rvalue aggregates during check_stmt in tycheck, add initial, (not passing) test

This commit is contained in:
Paul Daniel Faria 2017-11-10 00:07:32 -05:00 committed by Niko Matsakis
parent 4fecccbd2e
commit 19c17360d9
2 changed files with 109 additions and 0 deletions

View file

@ -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);

View file

@ -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() { }