Skip to content

Mir tycheck aggregate rvalues #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/librustc/mir/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1376,10 +1376,14 @@ pub enum AggregateKind<'tcx> {
/// The type is of the element
Array(Ty<'tcx>),
Tuple,
/// The second field is variant number (discriminant), it's equal to 0
/// for struct and union expressions. The fourth field is active field
/// number and is present only for union expressions.

/// The second field is variant number (discriminant), it's equal
/// to 0 for struct and union expressions. The fourth field is
/// active field number and is present only for union expressions
/// -- e.g. for a union expression `SomeUnion { c: .. }`, the
/// active field index would identity the field `c`
Adt(&'tcx AdtDef, usize, &'tcx Substs<'tcx>, Option<usize>),

Closure(DefId, ClosureSubsts<'tcx>),
Generator(DefId, ClosureSubsts<'tcx>, GeneratorInterior<'tcx>),
}
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_mir/transform/nll/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(

// Run the MIR type-checker.
let body_id = source.item_id();
let constraint_sets = &type_check::type_check(infcx, body_id, param_env, mir);
let constraint_sets = &type_check::type_check(infcx, body_id, param_env, mir, source);

// Create the region inference context, taking ownership of the region inference
// data that was contained in `infcx`.
Expand Down
136 changes: 134 additions & 2 deletions src/librustc_mir/transform/type_check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
//! This pass type-checks the MIR to ensure it is not broken.
#![allow(unreachable_code)]

use rustc::hir::map::DefPathData;
use rustc::infer::{InferCtxt, InferOk, InferResult, LateBoundRegionConversionTime, UnitResult};
use rustc::infer::region_constraints::RegionConstraintData;
use rustc::traits::{self, FulfillmentContext};
Expand Down Expand Up @@ -41,8 +42,9 @@ pub fn type_check<'a, 'gcx, 'tcx>(
body_id: ast::NodeId,
param_env: ty::ParamEnv<'gcx>,
mir: &Mir<'tcx>,
mir_source: MirSource,
) -> MirTypeckRegionConstraints<'tcx> {
let mut checker = TypeChecker::new(infcx, body_id, param_env);
let mut checker = TypeChecker::new(infcx, body_id, param_env, mir_source);
let errors_reported = {
let mut verifier = TypeVerifier::new(&mut checker, mir);
verifier.visit_mir(mir);
Expand Down Expand Up @@ -408,6 +410,11 @@ pub struct TypeChecker<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
body_id: ast::NodeId,
reported_errors: FxHashSet<(Ty<'tcx>, Span)>,
constraints: MirTypeckRegionConstraints<'tcx>,

// FIXME(#45940) - True if this is a MIR shim or ADT constructor
// (e.g., for a tuple struct.) In that case, the internal types of
// operands and things require normalization.
is_adt_constructor: bool,
}

/// A collection of region constraints that must be satisfied for the
Expand Down Expand Up @@ -459,14 +466,23 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
body_id: ast::NodeId,
param_env: ty::ParamEnv<'gcx>,
mir_source: MirSource,
) -> Self {
let mir_def_id = infcx.tcx.hir.local_def_id(mir_source.item_id());
let def_key = infcx.tcx.def_key(mir_def_id);
let is_adt_constructor = match def_key.disambiguated_data.data {
DefPathData::StructCtor => true,
_ => false,
};

TypeChecker {
infcx,
last_span: DUMMY_SP,
body_id,
param_env,
reported_errors: FxHashSet(),
constraints: MirTypeckRegionConstraints::default(),
is_adt_constructor,
}
}

Expand Down Expand Up @@ -549,6 +565,7 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
terr
);
}
self.check_rvalue(mir, rv, location);
}
StatementKind::SetDiscriminant {
ref lvalue,
Expand Down Expand Up @@ -1008,6 +1025,121 @@ impl<'a, 'gcx, 'tcx> TypeChecker<'a, 'gcx, 'tcx> {
}
}

fn aggregate_field_ty(
&mut self,
ak: &Box<AggregateKind<'tcx>>,
field_index: usize,
location: Location,
) -> Result<Ty<'tcx>, FieldAccessError> {
let tcx = self.tcx();

match **ak {
AggregateKind::Adt(def, variant_index, substs, active_field_index) => {
let variant = &def.variants[variant_index];
let adj_field_index = active_field_index.unwrap_or(field_index);
if let Some(field) = variant.fields.get(adj_field_index) {
Ok(self.normalize(&field.ty(tcx, substs), location))
} else {
Err(FieldAccessError::OutOfRange {
field_count: variant.fields.len(),
})
}
}
AggregateKind::Closure(def_id, substs) => {
match substs.upvar_tys(def_id, tcx).nth(field_index) {
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_index) {
Ok(ty)
} else {
match substs.field_tys(def_id, tcx).nth(field_index) {
Some(ty) => Ok(ty),
None => Err(FieldAccessError::OutOfRange {
field_count: substs.field_tys(def_id, tcx).count() + 1,
}),
}
}
}
AggregateKind::Array(ty) => {
Ok(ty)
}
AggregateKind::Tuple => {
unreachable!("This should have been covered in check_rvalues");
}
}
}

fn check_rvalue(&mut self, mir: &Mir<'tcx>, rv: &Rvalue<'tcx>, location: Location) {
let tcx = self.tcx();
match rv {
Rvalue::Aggregate(ak, 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 = match self.aggregate_field_ty(ak, i, location) {
Ok(field_ty) => field_ty,
Err(FieldAccessError::OutOfRange { field_count }) => {
span_mirbug!(
self,
rv,
"accessed field #{} but variant only has {}",
i,
field_count
);
continue;
}
};
let op_ty = match op {
Operand::Consume(lv) => {
let lv_ty = lv.ty(mir, tcx).to_ty(tcx);
if self.is_adt_constructor {
self.normalize(&lv_ty, location)
} else {
lv_ty
}
}
Operand::Constant(c) => c.ty,
};
if let Err(terr) = self.sub_types(
op_ty,
field_ty,
location.at_successor_within_block(),
)
{
span_mirbug!(
self,
rv,
"{:?} is not a subtype of {:?}: {:?}",
op_ty,
field_ty,
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);
Expand Down Expand Up @@ -1063,7 +1195,7 @@ impl MirPass for TypeckMir {
}
let param_env = tcx.param_env(def_id);
tcx.infer_ctxt().enter(|infcx| {
let _region_constraint_sets = type_check(&infcx, item_id, param_env, mir);
let _region_constraint_sets = type_check(&infcx, item_id, param_env, mir, src);

// For verification purposes, we just ignore the resulting
// region constraint sets. Not our problem. =)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// 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.
//revisions: ast mir
//[mir] compile-flags: -Z emit-end-regions -Z borrowck-mir -Z nll

#![allow(unused_assignments)]

struct Wrap<'a> { w: &'a mut u32 }

fn foo() {
let mut x = 22;
let wrapper = Wrap { w: &mut x };
x += 1; //[ast]~ ERROR cannot assign to `x` because it is borrowed [E0506]
//[mir]~^ ERROR cannot assign to `x` because it is borrowed (Ast) [E0506]
//[mir]~^^ ERROR cannot assign to `x` because it is borrowed (Mir) [E0506]
//[mir]~^^^ ERROR cannot use `x` because it was mutably borrowed (Mir) [E0503]
*wrapper.w += 1;
}

fn main() { }