begin implementing mir-typeck

This commit is contained in:
Ariel Ben-Yehuda 2016-02-07 21:13:00 +02:00 committed by Ariel Ben-Yehuda
parent 15611f75ca
commit addc653da5
6 changed files with 423 additions and 8 deletions

View file

@ -45,6 +45,9 @@ pub struct Mir<'tcx> {
/// values in that it is possible to borrow them and mutate them
/// through the resulting reference.
pub temp_decls: Vec<TempDecl<'tcx>>,
/// A span representing this MIR, for error reporting
pub span: Span,
}
/// where execution begins
@ -145,7 +148,7 @@ pub enum BorrowKind {
/// A "variable" is a binding declared by the user as part of the fn
/// decl, a let, etc.
#[derive(Clone, RustcEncodable, RustcDecodable)]
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct VarDecl<'tcx> {
pub mutability: Mutability,
pub name: Name,
@ -154,7 +157,7 @@ pub struct VarDecl<'tcx> {
/// A "temp" is a temporary that we place on the stack. They are
/// anonymous, always mutable, and have only a type.
#[derive(Clone, RustcEncodable, RustcDecodable)]
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct TempDecl<'tcx> {
pub ty: Ty<'tcx>,
}
@ -170,7 +173,7 @@ pub struct TempDecl<'tcx> {
///
/// there is only one argument, of type `(i32, u32)`, but two bindings
/// (`x` and `y`).
#[derive(Clone, RustcEncodable, RustcDecodable)]
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct ArgDecl<'tcx> {
pub ty: Ty<'tcx>,
}

View file

@ -14,7 +14,8 @@
*/
use mir::repr::*;
use middle::subst::Substs;
use middle::const_eval::ConstVal;
use middle::subst::{Subst, Substs};
use middle::ty::{self, AdtDef, Ty};
use rustc_front::hir;
@ -150,6 +151,73 @@ impl<'tcx> Mir<'tcx> {
self.lvalue_ty(tcx, &proj.base).projection_ty(tcx, &proj.elem)
}
}
pub fn rvalue_ty(&self,
tcx: &ty::ctxt<'tcx>,
rvalue: &Rvalue<'tcx>)
-> Option<Ty<'tcx>>
{
match *rvalue {
Rvalue::Use(ref operand) => Some(self.operand_ty(tcx, operand)),
Rvalue::Repeat(ref operand, ref count) => {
if let ConstVal::Uint(u) = count.value {
let op_ty = self.operand_ty(tcx, operand);
Some(tcx.mk_array(op_ty, u as usize))
} else {
None
}
}
Rvalue::Ref(reg, bk, ref lv) => {
let lv_ty = self.lvalue_ty(tcx, lv).to_ty(tcx);
Some(tcx.mk_ref(
tcx.mk_region(reg),
ty::TypeAndMut {
ty: lv_ty,
mutbl: bk.to_mutbl_lossy()
}
))
}
Rvalue::Len(..) => Some(tcx.types.usize),
Rvalue::Cast(_, _, ty) => Some(ty),
Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
let lhs_ty = self.operand_ty(tcx, lhs);
let rhs_ty = self.operand_ty(tcx, rhs);
Some(self.binop_ty(tcx, op, lhs_ty, rhs_ty))
}
Rvalue::UnaryOp(_, ref operand) => {
Some(self.operand_ty(tcx, operand))
}
Rvalue::Box(t) => {
Some(tcx.mk_box(t))
}
Rvalue::Aggregate(ref ak, ref ops) => {
match *ak {
AggregateKind::Vec => {
if let Some(operand) = ops.get(0) {
let ty = self.operand_ty(tcx, operand);
Some(tcx.mk_array(ty, ops.len()))
} else {
None
}
}
AggregateKind::Tuple => {
Some(tcx.mk_tup(
ops.iter().map(|op| self.operand_ty(tcx, op)).collect()
))
}
AggregateKind::Adt(def, _, substs) => {
Some(def.type_scheme(tcx).ty.subst(tcx, substs))
}
AggregateKind::Closure(did, substs) => {
Some(tcx.mk_closure_from_closure_substs(
did, Box::new(substs.clone())))
}
}
}
Rvalue::Slice { .. } => None,
Rvalue::InlineAsm(..) => None
}
}
}
impl BorrowKind {

View file

@ -80,7 +80,7 @@ macro_rules! unpack {
/// the main entry point for building MIR for a function
pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
_span: Span,
span: Span,
implicit_arguments: Vec<Ty<'tcx>>,
explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
argument_extent: CodeExtent,
@ -97,7 +97,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
temp_decls: vec![],
var_decls: vec![],
var_indices: FnvHashMap(),
unit_temp: None
unit_temp: None,
};
assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
@ -119,6 +119,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
arg_decls: arg_decls,
temp_decls: builder.temp_decls,
return_ty: return_ty,
span: span
}
}

View file

@ -22,7 +22,7 @@ extern crate rustc_front;
use build;
use graphviz;
use pretty;
use transform::{simplify_cfg, no_landing_pads};
use transform::{simplify_cfg, type_check, no_landing_pads};
use rustc::dep_graph::DepNode;
use rustc::mir::repr::Mir;
use hair::cx::Cx;
@ -148,8 +148,9 @@ impl<'a, 'm, 'tcx> Visitor<'tcx> for InnerDump<'a,'m,'tcx> {
match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
Ok(mut mir) => {
no_landing_pads::NoLandingPads.run_on_mir(&mut mir, self.tcx);
simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, self.tcx);
type_check::TypeckMir::new(&infcx).run_on_mir(&mut mir, self.tcx);
no_landing_pads::NoLandingPads.run_on_mir(&mut mir, self.tcx);
let meta_item_list = self.attr
.iter()

View file

@ -11,4 +11,5 @@
pub mod simplify_cfg;
pub mod erase_regions;
pub mod no_landing_pads;
pub mod type_check;
mod util;

View file

@ -0,0 +1,341 @@
// Copyright 2016 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.
//! This pass type-checks the MIR to ensure it is not broken.
use rustc::middle::infer;
use rustc::middle::ty::{self, Ty};
use rustc::middle::ty::fold::TypeFoldable;
use rustc::mir::repr::*;
use rustc::mir::transform::MirPass;
use rustc::mir::visit::{self, Visitor};
use syntax::codemap::{Span, DUMMY_SP};
use std::fmt;
macro_rules! span_mirbug {
($context:expr, $elem:expr, $($message:tt)*) => ({
$context.tcx().sess.span_warn(
$context.last_span,
&format!("broken MIR ({:?}): {:?}", $elem, format!($($message)*))
)
})
}
/// Verifies that MIR types are sane to not crash further
/// checks.
struct TypeVerifier<'a, 'tcx: 'a> {
infcx: &'a infer::InferCtxt<'a, 'tcx>,
mir: &'a Mir<'tcx>,
last_span: Span,
errors_reported: bool
}
impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> {
fn visit_span(&mut self, span: &Span) {
if *span != DUMMY_SP {
self.last_span = *span;
}
}
fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: visit::LvalueContext) {
self.super_lvalue(lvalue, context);
let lv_ty = self.mir.lvalue_ty(self.tcx(), lvalue).to_ty(self.tcx());
self.sanitize_type(lvalue, lv_ty);
}
fn visit_constant(&mut self, constant: &Constant<'tcx>) {
self.super_constant(constant);
self.sanitize_type(constant, constant.ty);
}
fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>) {
self.super_rvalue(rvalue);
if let Some(ty) = self.mir.rvalue_ty(self.tcx(), rvalue) {
self.sanitize_type(rvalue, ty);
}
}
fn visit_mir(&mut self, mir: &Mir<'tcx>) {
if let ty::FnConverging(t) = mir.return_ty {
self.sanitize_type(&"return type", t);
}
for var_decl in &mir.var_decls {
self.sanitize_type(var_decl, var_decl.ty);
}
for (n, arg_decl) in mir.arg_decls.iter().enumerate() {
self.sanitize_type(&(n, arg_decl), arg_decl.ty);
}
for (n, tmp_decl) in mir.temp_decls.iter().enumerate() {
self.sanitize_type(&(n, tmp_decl), tmp_decl.ty);
}
self.super_mir(mir);
}
}
impl<'a, 'tcx> TypeVerifier<'a, 'tcx> {
fn new(infcx: &'a infer::InferCtxt<'a, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
TypeVerifier {
infcx: infcx,
mir: mir,
last_span: mir.span,
errors_reported: false
}
}
fn tcx(&self) -> &'a ty::ctxt<'tcx> {
self.infcx.tcx
}
fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) {
if !(ty.needs_infer() || ty.has_escaping_regions()) {
return;
}
span_mirbug!(self, parent, "bad type {:?}", ty);
self.errors_reported = true;
}
}
pub struct TypeckMir<'a, 'tcx: 'a> {
infcx: &'a infer::InferCtxt<'a, 'tcx>,
last_span: Span
}
impl<'a, 'tcx> TypeckMir<'a, 'tcx> {
pub fn new(infcx: &'a infer::InferCtxt<'a, 'tcx>) -> Self {
TypeckMir {
infcx: infcx,
last_span: DUMMY_SP
}
}
fn tcx(&self) -> &'a ty::ctxt<'tcx> {
self.infcx.tcx
}
fn check_stmt(&mut self, mir: &Mir<'tcx>, stmt: &Statement<'tcx>) {
debug!("check_stmt: {:?}", stmt);
let tcx = self.tcx();
match stmt.kind {
StatementKind::Assign(ref lv, ref rv) => {
match lv {
&Lvalue::ReturnPointer if mir.return_ty == ty::FnDiverging => {
// HACK: buggy writes
return;
}
_ => {}
}
let lv_ty = mir.lvalue_ty(tcx, lv).to_ty(tcx);
let rv_ty = mir.rvalue_ty(tcx, rv);
if let Some(rv_ty) = rv_ty {
if let Err(terr) = infer::can_mk_subty(self.infcx, rv_ty, lv_ty) {
span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}",
lv_ty, rv_ty, terr);
}
}
// FIXME: rvalue with undeterminable type - e.g. inline
// asm.
}
}
}
fn check_terminator(&self,
mir: &Mir<'tcx>,
term: &Terminator<'tcx>) {
debug!("check_terminator: {:?}", term);
let tcx = self.tcx();
match *term {
Terminator::Goto { .. } |
Terminator::Resume |
Terminator::Return |
Terminator::Drop { .. } => {}
Terminator::If { ref cond, .. } => {
let cond_ty = mir.operand_ty(tcx, cond);
match cond_ty.sty {
ty::TyBool => {}
_ => {
span_mirbug!(self, term, "bad If ({:?}, not bool", cond_ty);
}
}
}
Terminator::SwitchInt { ref discr, switch_ty, .. } => {
let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx);
if let Err(terr) = infer::can_mk_subty(self.infcx, discr_ty, switch_ty) {
span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}",
switch_ty, discr_ty, terr);
}
}
Terminator::Switch { ref discr, adt_def, .. } => {
let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx);
match discr_ty.sty {
ty::TyEnum(def, _) if def == adt_def => {},
_ => {
span_mirbug!(self, term, "bad Switch ({:?} on {:?})",
adt_def, discr_ty);
}
}
}
Terminator::Call { ref func, ref args, ref destination, .. } => {
let func_ty = mir.operand_ty(tcx, func);
debug!("check_terminator: call, func_ty={:?}", func_ty);
let func_ty = match func_ty.sty {
ty::TyBareFn(_, func_ty) => func_ty,
_ => {
span_mirbug!(self, term, "call to non-function {:?}", func_ty);
return;
}
};
let sig = tcx.erase_late_bound_regions(&func_ty.sig);
self.check_call_dest(mir, term, &sig, destination);
if self.is_box_free(func) {
self.check_box_free_inputs(mir, term, &sig, args);
} else {
self.check_call_inputs(mir, term, &sig, args);
}
}
}
}
fn check_call_dest(&self,
mir: &Mir<'tcx>,
term: &Terminator<'tcx>,
sig: &ty::FnSig<'tcx>,
destination: &Option<(Lvalue<'tcx>, BasicBlock)>) {
let tcx = self.tcx();
match (destination, sig.output) {
(&Some(..), ty::FnDiverging) => {
span_mirbug!(self, term, "call to diverging function {:?} with dest", sig);
}
(&Some((ref dest, _)), ty::FnConverging(ty)) => {
let dest_ty = mir.lvalue_ty(tcx, dest).to_ty(tcx);
if let Err(terr) = infer::can_mk_subty(self.infcx, ty, dest_ty) {
span_mirbug!(self, term,
"call dest mismatch ({:?} <- {:?}): {:?}",
dest_ty, ty, terr);
}
}
(&None, ty::FnDiverging) => {}
(&None, ty::FnConverging(..)) => {
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
}
}
}
fn check_call_inputs(&self,
mir: &Mir<'tcx>,
term: &Terminator<'tcx>,
sig: &ty::FnSig<'tcx>,
args: &[Operand<'tcx>])
{
debug!("check_call_inputs({:?}, {:?})", sig, args);
if sig.inputs.len() > args.len() ||
(sig.inputs.len() < args.len() && !sig.variadic) {
span_mirbug!(self, term, "call to {:?} with wrong # of args", sig);
}
for (n, (fn_arg, op_arg)) in sig.inputs.iter().zip(args).enumerate() {
let op_arg_ty = mir.operand_ty(self.tcx(), op_arg);
if let Err(terr) = infer::can_mk_subty(self.infcx, op_arg_ty, fn_arg) {
span_mirbug!(self, term, "bad arg #{:?} ({:?} <- {:?}): {:?}",
n, fn_arg, op_arg_ty, terr);
}
}
}
fn is_box_free(&self, operand: &Operand<'tcx>) -> bool {
match operand {
&Operand::Constant(Constant {
literal: Literal::Item { def_id, .. }, ..
}) => {
Some(def_id) == self.tcx().lang_items.box_free_fn()
}
_ => false,
}
}
fn check_box_free_inputs(&self,
mir: &Mir<'tcx>,
term: &Terminator<'tcx>,
sig: &ty::FnSig<'tcx>,
args: &[Operand<'tcx>])
{
debug!("check_box_free_inputs");
// box_free takes a Box as a pointer. Allow for that.
if sig.inputs.len() != 1 {
span_mirbug!(self, term, "box_free should take 1 argument");
return;
}
let pointee_ty = match sig.inputs[0].sty {
ty::TyRawPtr(mt) => mt.ty,
_ => {
span_mirbug!(self, term, "box_free should take a raw ptr");
return;
}
};
if args.len() != 1 {
span_mirbug!(self, term, "box_free called with wrong # of args");
return;
}
let arg_ty = match mir.operand_ty(self.tcx(), &args[0]).sty {
ty::TyRawPtr(mt) => mt.ty,
ty::TyBox(ty) => ty,
_ => {
span_mirbug!(self, term, "box_free called with bad arg ty");
return;
}
};
if let Err(terr) = infer::can_mk_subty(self.infcx, arg_ty, pointee_ty) {
span_mirbug!(self, term, "bad box_free arg ({:?} <- {:?}): {:?}",
pointee_ty, arg_ty, terr);
}
}
fn typeck_mir(&mut self, mir: &Mir<'tcx>) {
self.last_span = mir.span;
debug!("run_on_mir: {:?}", mir.span);
for block in &mir.basic_blocks {
for stmt in &block.statements {
if stmt.span != DUMMY_SP {
self.last_span = stmt.span;
}
self.check_stmt(mir, stmt);
}
if let Some(ref terminator) = block.terminator {
self.check_terminator(mir, terminator);
}
}
}
}
impl<'a, 'tcx> MirPass for TypeckMir<'a, 'tcx> {
fn run_on_mir<'tcx_>(&mut self,
mir: &mut Mir<'tcx_>,
_tcx: &ty::ctxt<'tcx_>) {
// FIXME: pass param_env to run_on_mir
let mir: &mut Mir<'tcx> = unsafe { ::std::mem::transmute(mir) };
let mut type_verifier = TypeVerifier::new(self.infcx, mir);
type_verifier.visit_mir(mir);
if type_verifier.errors_reported {
return;
}
self.typeck_mir(mir);
}
}