From addc653da5fa6d0541c445f32350d95d84f3de57 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 7 Feb 2016 21:13:00 +0200 Subject: [PATCH 01/13] begin implementing mir-typeck --- src/librustc/mir/repr.rs | 9 +- src/librustc/mir/tcx.rs | 70 ++++- src/librustc_mir/build/mod.rs | 5 +- src/librustc_mir/mir_map.rs | 5 +- src/librustc_mir/transform/mod.rs | 1 + src/librustc_mir/transform/type_check.rs | 341 +++++++++++++++++++++++ 6 files changed, 423 insertions(+), 8 deletions(-) create mode 100644 src/librustc_mir/transform/type_check.rs diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 783c58469a1b3..2f24a1a8962de 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -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>, + + /// 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>, } diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index 45cc0b8b413ea..e8b83962b521b 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -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> + { + 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 { diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 86b348581892b..b83d0f6a9803a 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -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>, 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 } } diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 5b887db7ec08e..7a2b90fbbb31d 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -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() diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 017cf3607161a..64e188a3b558e 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -11,4 +11,5 @@ pub mod simplify_cfg; pub mod erase_regions; pub mod no_landing_pads; +pub mod type_check; mod util; diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs new file mode 100644 index 0000000000000..3a891543e8013 --- /dev/null +++ b/src/librustc_mir/transform/type_check.rs @@ -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 or the MIT license +// , 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); + } +} From 999f1767caeb62a60278ae5afd9191dfbbd6ef18 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 7 Feb 2016 22:46:39 +0200 Subject: [PATCH 02/13] add -Z mir-opt-level to disable MIR optimizations setting -Z mir-opt-level=0 will disable all MIR optimizations for easier debugging --- src/librustc/session/config.rs | 6 ++++++ src/librustc_mir/mir_map.rs | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 864ff40fe1074..f835613cfcbb9 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -138,6 +138,7 @@ pub struct Options { pub no_trans: bool, pub error_format: ErrorOutputType, pub treat_err_as_bug: bool, + pub mir_opt_level: usize, /// if true, build up the dep-graph pub build_dep_graph: bool, @@ -254,6 +255,7 @@ pub fn basic_options() -> Options { parse_only: false, no_trans: false, treat_err_as_bug: false, + mir_opt_level: 1, build_dep_graph: false, dump_dep_graph: false, no_analysis: false, @@ -655,6 +657,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "show spans for compiler debugging (expr|pat|ty)"), print_trans_items: Option = (None, parse_opt_string, "print the result of the translation item collection pass"), + mir_opt_level: Option = (None, parse_opt_uint, + "set the MIR optimization level (0-3)"), } pub fn default_lib_output() -> CrateType { @@ -988,6 +992,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { let parse_only = debugging_opts.parse_only; let no_trans = debugging_opts.no_trans; let treat_err_as_bug = debugging_opts.treat_err_as_bug; + let mir_opt_level = debugging_opts.mir_opt_level.unwrap_or(1); let incremental_compilation = debugging_opts.incr_comp; let dump_dep_graph = debugging_opts.dump_dep_graph; let no_analysis = debugging_opts.no_analysis; @@ -1166,6 +1171,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options { parse_only: parse_only, no_trans: no_trans, treat_err_as_bug: treat_err_as_bug, + mir_opt_level: mir_opt_level, build_dep_graph: incremental_compilation || dump_dep_graph, dump_dep_graph: dump_dep_graph, no_analysis: no_analysis, diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 7a2b90fbbb31d..58b6d69ba5c1a 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -148,10 +148,11 @@ 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) => { - 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); - + if self.tcx.sess.opts.mir_opt_level > 0 { + simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, self.tcx); + } let meta_item_list = self.attr .iter() .flat_map(|a| a.meta_item_list()) From 67d1cf112218257025edf75b9cb7ebc8ab8bfa64 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sun, 7 Feb 2016 23:09:15 +0200 Subject: [PATCH 03/13] introduce an early pass to clear dead blocks this makes the the MIR assignment pass complete successfully --- src/librustc_mir/mir_map.rs | 4 +- .../transform/clear_dead_blocks.rs | 62 +++++++++++++++++++ src/librustc_mir/transform/mod.rs | 1 + src/librustc_mir/transform/type_check.rs | 8 --- 4 files changed, 66 insertions(+), 9 deletions(-) create mode 100644 src/librustc_mir/transform/clear_dead_blocks.rs diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 58b6d69ba5c1a..68d85dc8394b9 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -22,7 +22,8 @@ extern crate rustc_front; use build; use graphviz; use pretty; -use transform::{simplify_cfg, type_check, no_landing_pads}; +use transform::{clear_dead_blocks, simplify_cfg, type_check}; +use transform::{no_landing_pads}; use rustc::dep_graph::DepNode; use rustc::mir::repr::Mir; use hair::cx::Cx; @@ -148,6 +149,7 @@ 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) => { + clear_dead_blocks::ClearDeadBlocks::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); if self.tcx.sess.opts.mir_opt_level > 0 { diff --git a/src/librustc_mir/transform/clear_dead_blocks.rs b/src/librustc_mir/transform/clear_dead_blocks.rs new file mode 100644 index 0000000000000..88c0116f26d24 --- /dev/null +++ b/src/librustc_mir/transform/clear_dead_blocks.rs @@ -0,0 +1,62 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A pass that erases the contents of dead blocks. This is required +//! because rustc allows for ill-typed block terminators in dead +//! blocks. +//! +//! This pass does not renumber or remove the blocks, to have the +//! MIR better match the source. + +use rustc::middle::ty; +use rustc::mir::repr::*; +use rustc::mir::transform::MirPass; + +pub struct ClearDeadBlocks; + +impl ClearDeadBlocks { + pub fn new() -> ClearDeadBlocks { + ClearDeadBlocks + } + + fn clear_dead_blocks(&self, mir: &mut Mir) { + let mut seen = vec![false; mir.basic_blocks.len()]; + + // These blocks are always required. + seen[START_BLOCK.index()] = true; + seen[END_BLOCK.index()] = true; + + let mut worklist = vec![START_BLOCK]; + while let Some(bb) = worklist.pop() { + for succ in mir.basic_block_data(bb).terminator().successors().iter() { + if !seen[succ.index()] { + seen[succ.index()] = true; + worklist.push(*succ); + } + } + } + + for (block, seen) in mir.basic_blocks.iter_mut().zip(seen) { + if !seen { + *block = BasicBlockData { + statements: vec![], + terminator: Some(Terminator::Return), + is_cleanup: false + }; + } + } + } +} + +impl MirPass for ClearDeadBlocks { + fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, _tcx: &ty::ctxt<'tcx>) { + self.clear_dead_blocks(mir); + } +} diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 64e188a3b558e..d27c208041faf 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +pub mod clear_dead_blocks; pub mod simplify_cfg; pub mod erase_regions; pub mod no_landing_pads; diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 3a891543e8013..f4d83cd05e08f 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -125,14 +125,6 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { 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 { From b7cbbc374c8549620221ad6c4e6e3c300efa5b1c Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 11 Feb 2016 18:29:44 +0200 Subject: [PATCH 04/13] be more type-safe in panic/panic_bounds_check TODO: find a correct borrow region Fixes #31482 --- src/librustc_mir/build/scope.rs | 40 +++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 167317f426b71..314f75beea149 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -418,25 +418,28 @@ impl<'a,'tcx> Builder<'a,'tcx> { len: Operand<'tcx>, span: Span) { // fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> ! + let region = ty::ReStatic; // TODO(mir-borrowck): use a better region? let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem); - let args = func.ty.fn_args(); - let ref_ty = args.skip_binder()[0]; - let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty { - (region, tyandmut.ty) + let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0; + + let ref_ty = args[0]; + let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty { + tyandmut.ty } else { self.hir.span_bug(span, &format!("unexpected panic_bound_check type: {:?}", func.ty)); }; + let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty)); let (file, line) = self.span_to_fileline_args(span); let elems = vec![Operand::Constant(file), Operand::Constant(line)]; // FIXME: We should have this as a constant, rather than a stack variable (to not pollute // icache with cold branch code), however to achieve that we either have to rely on rvalue // promotion or have some way, in MIR, to create constants. - self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (file_arg, line_arg); + self.cfg.push_assign(block, span, &tuple, // tuple = (file_arg, line_arg); Rvalue::Aggregate(AggregateKind::Tuple, elems)); // FIXME: is this region really correct here? - self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple; - Rvalue::Ref(*region, BorrowKind::Unique, tuple)); + self.cfg.push_assign(block, span, &tuple_ref, // tuple_ref = &tuple; + Rvalue::Ref(region, BorrowKind::Shared, tuple)); let cleanup = self.diverge_cleanup(); self.cfg.terminate(block, Terminator::Call { func: Operand::Constant(func), @@ -449,18 +452,21 @@ impl<'a,'tcx> Builder<'a,'tcx> { /// Create diverge cleanup and branch to it from `block`. pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) { // fn(&(msg: &'static str filename: &'static str, line: u32)) -> ! + let region = ty::ReStatic; // TODO(mir-borrowck): use a better region? let func = self.lang_function(lang_items::PanicFnLangItem); - let args = func.ty.fn_args(); - let ref_ty = args.skip_binder()[0]; - let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty { - (region, tyandmut.ty) + let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0; + + let ref_ty = args[0]; + let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty { + tyandmut.ty } else { self.hir.span_bug(span, &format!("unexpected panic type: {:?}", func.ty)); }; + let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty)); let (file, line) = self.span_to_fileline_args(span); let message = Constant { - span: DUMMY_SP, + span: span, ty: self.hir.tcx().mk_static_str(), literal: self.hir.str_literal(intern_and_get_ident(msg)) }; @@ -470,11 +476,11 @@ impl<'a,'tcx> Builder<'a,'tcx> { // FIXME: We should have this as a constant, rather than a stack variable (to not pollute // icache with cold branch code), however to achieve that we either have to rely on rvalue // promotion or have some way, in MIR, to create constants. - self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (message_arg, file_arg, line_arg); + self.cfg.push_assign(block, span, &tuple, // tuple = (message_arg, file_arg, line_arg); Rvalue::Aggregate(AggregateKind::Tuple, elems)); // FIXME: is this region really correct here? - self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple; - Rvalue::Ref(*region, BorrowKind::Unique, tuple)); + self.cfg.push_assign(block, span, &tuple_ref, // tuple_ref = &tuple; + Rvalue::Ref(region, BorrowKind::Shared, tuple)); let cleanup = self.diverge_cleanup(); self.cfg.terminate(block, Terminator::Call { func: Operand::Constant(func), @@ -505,11 +511,11 @@ impl<'a,'tcx> Builder<'a,'tcx> { fn span_to_fileline_args(&mut self, span: Span) -> (Constant<'tcx>, Constant<'tcx>) { let span_lines = self.hir.tcx().sess.codemap().lookup_char_pos(span.lo); (Constant { - span: DUMMY_SP, + span: span, ty: self.hir.tcx().mk_static_str(), literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name)) }, Constant { - span: DUMMY_SP, + span: span, ty: self.hir.tcx().types.u32, literal: self.hir.usize_literal(span_lines.line) }) From 6f633ef128335e11e9903f647c6a6150f3a714d1 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 11 Feb 2016 18:30:34 +0200 Subject: [PATCH 05/13] tuple arguments to overloaded calls also fix translation of "rust-call" functions, although that could use more optimizations --- src/librustc_mir/hair/cx/expr.rs | 27 ++++++- src/librustc_trans/trans/mir/block.rs | 61 ++++++++++++--- src/librustc_trans/trans/mir/operand.rs | 98 ++++++++++++++++++------- src/test/run-pass/mir_trans_calls.rs | 36 +++++++-- 4 files changed, 179 insertions(+), 43 deletions(-) diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 90d049cdc731a..769dbe0ab5162 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -54,14 +54,35 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { // Find the actual method implementation being called and // build the appropriate UFCS call expression with the // callee-object as self parameter. + + // rewrite f(u, v) into FnOnce::call_once(f, (u, v)) + let method = method_callee(cx, self, ty::MethodCall::expr(self.id)); - let mut argrefs = vec![fun.to_ref()]; - argrefs.extend(args.iter().map(|a| a.to_ref())); + + let sig = match method.ty.sty { + ty::TyBareFn(_, fn_ty) => &fn_ty.sig, + _ => cx.tcx.sess.span_bug(self.span, "type of method is not an fn") + }; + + let sig = cx.tcx.no_late_bound_regions(sig).unwrap_or_else(|| { + cx.tcx.sess.span_bug(self.span, "method call has late-bound regions") + }); + + assert_eq!(sig.inputs.len(), 2); + + let tupled_args = Expr { + ty: sig.inputs[1], + temp_lifetime: cx.tcx.region_maps.temporary_scope(self.id), + span: self.span, + kind: ExprKind::Tuple { + fields: args.iter().map(ToRef::to_ref).collect() + } + }; ExprKind::Call { ty: method.ty, fun: method.to_ref(), - args: argrefs, + args: vec![fun.to_ref(), tupled_args.to_ref()] } } else { let adt_data = if let hir::ExprPath(..) = fun.node { diff --git a/src/librustc_trans/trans/mir/block.rs b/src/librustc_trans/trans/mir/block.rs index 44ff974dc7fc2..609f1dee98aad 100644 --- a/src/librustc_trans/trans/mir/block.rs +++ b/src/librustc_trans/trans/mir/block.rs @@ -9,7 +9,7 @@ // except according to those terms. use llvm::{BasicBlockRef, ValueRef, OperandBundleDef}; -use rustc::middle::ty; +use rustc::middle::ty::{self, Ty}; use rustc::mir::repr as mir; use syntax::abi::Abi; use trans::adt; @@ -26,8 +26,55 @@ use trans::type_::Type; use super::MirContext; use super::operand::OperandValue::{FatPtr, Immediate, Ref}; +use super::operand::OperandRef; + +#[derive(PartialEq, Eq)] +enum AbiStyle { + Foreign, + RustCall, + Rust +} impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + fn abi_style(&self, fn_ty: Ty<'tcx>) -> AbiStyle { + if let ty::TyBareFn(_, ref f) = fn_ty.sty { + // We do not translate intrinsics here (they shouldn’t be functions) + assert!(f.abi != Abi::RustIntrinsic && f.abi != Abi::PlatformIntrinsic); + + match f.abi { + Abi::Rust => AbiStyle::Rust, + Abi::RustCall => AbiStyle::RustCall, + _ => AbiStyle::Foreign + } + } else { + unreachable!() + } + } + + fn arg_operands(&mut self, + bcx: &BlockAndBuilder<'bcx, 'tcx>, + abi_style: AbiStyle, + args: &[mir::Operand<'tcx>]) + -> Vec> + { + match abi_style { + AbiStyle::Foreign | AbiStyle::Rust => { + args.iter().map(|arg| self.trans_operand(bcx, arg)).collect() + } + AbiStyle::RustCall => match args.split_last() { + None => vec![], + Some((tup, self_ty)) => { + // we can reorder safely because of MIR + let untupled_args = self.trans_operand_untupled(bcx, tup); + self_ty + .iter().map(|arg| self.trans_operand(bcx, arg)) + .chain(untupled_args.into_iter()) + .collect() + } + } + } + } + pub fn trans_block(&mut self, bb: mir::BasicBlock) { debug!("trans_block({:?})", bb); @@ -159,13 +206,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { let mut arg_tys = Vec::new(); // Foreign-ABI functions are translated differently - let is_foreign = if let ty::TyBareFn(_, ref f) = callee.ty.sty { - // We do not translate intrinsics here (they shouldn’t be functions) - assert!(f.abi != Abi::RustIntrinsic && f.abi != Abi::PlatformIntrinsic); - f.abi != Abi::Rust && f.abi != Abi::RustCall - } else { - false - }; + let abi_style = self.abi_style(callee.ty); + let is_foreign = abi_style == AbiStyle::Foreign; // Prepare the return value destination let (ret_dest_ty, must_copy_dest) = if let Some((ref d, _)) = *destination { @@ -182,8 +224,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { }; // Process the rest of the args. - for arg in args { - let operand = self.trans_operand(&bcx, arg); + for operand in self.arg_operands(&bcx, abi_style, args) { match operand.val { Ref(llval) | Immediate(llval) => llargs.push(llval), FatPtr(b, e) => { diff --git a/src/librustc_trans/trans/mir/operand.rs b/src/librustc_trans/trans/mir/operand.rs index d0eaaeef05771..2183348c8b5ea 100644 --- a/src/librustc_trans/trans/mir/operand.rs +++ b/src/librustc_trans/trans/mir/operand.rs @@ -9,13 +9,16 @@ // except according to those terms. use llvm::ValueRef; -use rustc::middle::ty::{Ty, TypeFoldable}; +use rustc::middle::ty::{self, Ty}; use rustc::mir::repr as mir; +use trans::adt; use trans::base; use trans::common::{self, Block, BlockAndBuilder}; use trans::datum; +use trans::Disr; use super::{MirContext, TempRef}; +use super::lvalue::LvalueRef; /// The representation of a Rust value. The enum variant is in fact /// uniquely determined by the value's type, but is kept as a @@ -90,6 +93,32 @@ impl<'tcx> OperandRef<'tcx> { } impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { + pub fn trans_load(&mut self, + bcx: &BlockAndBuilder<'bcx, 'tcx>, + llval: ValueRef, + ty: Ty<'tcx>) + -> OperandRef<'tcx> + { + debug!("trans_load: {} @ {:?}", bcx.val_to_string(llval), ty); + + let val = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) { + datum::ByValue => { + bcx.with_block(|bcx| { + OperandValue::Immediate(base::load_ty(bcx, llval, ty)) + }) + } + datum::ByRef if common::type_is_fat_ptr(bcx.tcx(), ty) => { + let (lldata, llextra) = bcx.with_block(|bcx| { + base::load_fat_ptr(bcx, llval, ty) + }); + OperandValue::FatPtr(lldata, llextra) + } + datum::ByRef => OperandValue::Ref(llval) + }; + + OperandRef { val: val, ty: ty } + } + pub fn trans_operand(&mut self, bcx: &BlockAndBuilder<'bcx, 'tcx>, operand: &mir::Operand<'tcx>) @@ -120,30 +149,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { // out from their home let tr_lvalue = self.trans_lvalue(bcx, lvalue); let ty = tr_lvalue.ty.to_ty(bcx.tcx()); - debug!("trans_operand: tr_lvalue={} @ {:?}", - bcx.val_to_string(tr_lvalue.llval), - ty); - let val = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) { - datum::ByValue => { - bcx.with_block(|bcx| { - OperandValue::Immediate(base::load_ty(bcx, tr_lvalue.llval, ty)) - }) - } - datum::ByRef if common::type_is_fat_ptr(bcx.tcx(), ty) => { - let (lldata, llextra) = bcx.with_block(|bcx| { - base::load_fat_ptr(bcx, tr_lvalue.llval, ty) - }); - OperandValue::FatPtr(lldata, llextra) - } - datum::ByRef => OperandValue::Ref(tr_lvalue.llval) - }; - - assert!(!ty.has_erasable_regions()); - - OperandRef { - val: val, - ty: ty - } + self.trans_load(bcx, tr_lvalue.llval, ty) } mir::Operand::Constant(ref constant) => { @@ -197,4 +203,46 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } } } + + pub fn trans_operand_untupled(&mut self, + bcx: &BlockAndBuilder<'bcx, 'tcx>, + operand: &mir::Operand<'tcx>) + -> Vec> + { + // FIXME: consider having some optimization to avoid tupling/untupling + // (and storing/loading in the case of immediates) + + // avoid trans_operand for pointless copying + let lv = match *operand { + mir::Operand::Consume(ref lvalue) => self.trans_lvalue(bcx, lvalue), + mir::Operand::Constant(ref constant) => { + // FIXME: consider being less pessimized + if constant.ty.is_nil() { + return vec![]; + } + + let ty = bcx.monomorphize(&constant.ty); + let lv = LvalueRef::alloca(bcx, ty, "__untuple_alloca"); + let constant = self.trans_constant(bcx, constant); + self.store_operand(bcx, lv.llval, constant); + lv + } + }; + + let lv_ty = lv.ty.to_ty(bcx.tcx()); + let result_types = match lv_ty.sty { + ty::TyTuple(ref tys) => tys, + _ => bcx.tcx().sess.span_bug( + self.mir.span, + &format!("bad final argument to \"rust-call\" fn {:?}", lv_ty)) + }; + + let base_repr = adt::represent_type(bcx.ccx(), lv_ty); + let base = adt::MaybeSizedValue::sized(lv.llval); + result_types.iter().enumerate().map(|(n, &ty)| { + self.trans_load(bcx, bcx.with_block(|bcx| { + adt::trans_field_ptr(bcx, &base_repr, base, Disr(0), n) + }), ty) + }).collect() + } } diff --git a/src/test/run-pass/mir_trans_calls.rs b/src/test/run-pass/mir_trans_calls.rs index fc45fbf727840..b8b7ecbf03cd6 100644 --- a/src/test/run-pass/mir_trans_calls.rs +++ b/src/test/run-pass/mir_trans_calls.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(rustc_attrs)] +#![feature(rustc_attrs, unboxed_closures, fn_traits)] #[rustc_mir] fn test1(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) { @@ -117,6 +117,27 @@ fn test_fn_impl(f: &&Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 { f(x, y) } +#[rustc_mir] +fn test_fn_direct_call(f: &F, x: i32, y: i32) -> i32 + where F: Fn(i32, i32) -> i32 +{ + f.call((x, y)) +} + +#[rustc_mir] +fn test_fn_const_call(f: &F) -> i32 + where F: Fn(i32, i32) -> i32 +{ + f.call((100, -1)) +} + +#[rustc_mir] +fn test_fn_nil_call(f: &F) -> i32 + where F: Fn() -> i32 +{ + f() +} + fn main() { assert_eq!(test1(1, (2, 3), &[4, 5, 6]), (1, (2, 3), &[4, 5, 6][..])); assert_eq!(test2(98), 98); @@ -128,9 +149,14 @@ fn main() { assert_eq!(test8(), 2); assert_eq!(test9(), 41 + 42 * 43); - let closure = |x: i32, y: i32| { x + y }; - assert_eq!(test_closure(&closure, 100, 1), 101); + let r = 3; + let closure = |x: i32, y: i32| { r*(x + (y*2)) }; + assert_eq!(test_fn_const_call(&closure), 294); + assert_eq!(test_closure(&closure, 100, 1), 306); let function_object = &closure as &Fn(i32, i32) -> i32; - assert_eq!(test_fn_object(function_object, 100, 2), 102); - assert_eq!(test_fn_impl(&function_object, 100, 3), 103); + assert_eq!(test_fn_object(function_object, 100, 2), 312); + assert_eq!(test_fn_impl(&function_object, 100, 3), 318); + assert_eq!(test_fn_direct_call(&closure, 100, 4), 324); + + assert_eq!(test_fn_nil_call(&(|| 42)), 42); } From 76608c8e0a09687e485b8a1eb0b47156ac54a7f3 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 11 Feb 2016 18:31:20 +0200 Subject: [PATCH 06/13] make *mut T -> *const T a coercion rather than being implicit quasi-subtyping. Nothing good can come out of quasi-subtyping. --- src/librustc/middle/expr_use_visitor.rs | 5 ++-- src/librustc/middle/mem_categorization.rs | 1 + src/librustc/middle/ty/adjustment.rs | 31 ++++++++++++++++++----- src/librustc/util/ppaux.rs | 3 +++ src/librustc_metadata/astencode.rs | 16 +++++++++--- src/librustc_mir/hair/cx/expr.rs | 12 +++++++++ src/librustc_passes/consts.rs | 3 ++- src/librustc_trans/trans/consts.rs | 4 +-- src/librustc_trans/trans/expr.rs | 7 ++--- src/librustc_typeck/check/coercion.rs | 4 ++- src/librustc_typeck/check/writeback.rs | 4 +++ 11 files changed, 70 insertions(+), 20 deletions(-) diff --git a/src/librustc/middle/expr_use_visitor.rs b/src/librustc/middle/expr_use_visitor.rs index 9b315aa46e5d3..93cc158cd12f2 100644 --- a/src/librustc/middle/expr_use_visitor.rs +++ b/src/librustc/middle/expr_use_visitor.rs @@ -721,10 +721,11 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> { if let Some(adjustment) = adj { match adjustment { adjustment::AdjustReifyFnPointer | - adjustment::AdjustUnsafeFnPointer => { + adjustment::AdjustUnsafeFnPointer | + adjustment::AdjustMutToConstPointer => { // Creating a closure/fn-pointer or unsizing consumes // the input and stores it into the resulting rvalue. - debug!("walk_adjustment(AdjustReifyFnPointer|AdjustUnsafeFnPointer)"); + debug!("walk_adjustment: trivial adjustment"); let cmt_unadjusted = return_if_err!(self.mc.cat_expr_unadjusted(expr)); self.delegate_consume(expr.id, expr.span, cmt_unadjusted); diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index c16997157bd9f..fef35764e1cc1 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -430,6 +430,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> { adjustment::AdjustReifyFnPointer | adjustment::AdjustUnsafeFnPointer | + adjustment::AdjustMutToConstPointer | adjustment::AdjustDerefRef(_) => { debug!("cat_expr({:?}): {:?}", adjustment, diff --git a/src/librustc/middle/ty/adjustment.rs b/src/librustc/middle/ty/adjustment.rs index 6cab0baa55325..afe177fbdcdcb 100644 --- a/src/librustc/middle/ty/adjustment.rs +++ b/src/librustc/middle/ty/adjustment.rs @@ -21,8 +21,9 @@ use rustc_front::hir; #[derive(Copy, Clone)] pub enum AutoAdjustment<'tcx> { - AdjustReifyFnPointer, // go from a fn-item type to a fn-pointer type - AdjustUnsafeFnPointer, // go from a safe fn pointer to an unsafe fn pointer + AdjustReifyFnPointer, // go from a fn-item type to a fn-pointer type + AdjustUnsafeFnPointer, // go from a safe fn pointer to an unsafe fn pointer + AdjustMutToConstPointer, // go from a mut raw pointer to a const raw pointer AdjustDerefRef(AutoDerefRef<'tcx>), } @@ -106,7 +107,8 @@ impl<'tcx> AutoAdjustment<'tcx> { pub fn is_identity(&self) -> bool { match *self { AdjustReifyFnPointer | - AdjustUnsafeFnPointer => false, + AdjustUnsafeFnPointer | + AdjustMutToConstPointer => false, AdjustDerefRef(ref r) => r.is_identity(), } } @@ -151,7 +153,7 @@ impl<'tcx> ty::TyS<'tcx> { return match adjustment { Some(adjustment) => { match *adjustment { - AdjustReifyFnPointer => { + AdjustReifyFnPointer => { match self.sty { ty::TyBareFn(Some(_), b) => { cx.mk_fn(None, b) @@ -164,17 +166,32 @@ impl<'tcx> ty::TyS<'tcx> { } } - AdjustUnsafeFnPointer => { + AdjustUnsafeFnPointer => { match self.sty { ty::TyBareFn(None, b) => cx.safe_to_unsafe_fn_ty(b), ref b => { cx.sess.bug( - &format!("AdjustReifyFnPointer adjustment on non-fn-item: \ + &format!("AdjustUnsafeFnPointer adjustment on non-fn-ptr: \ {:?}", b)); } } - } + } + + AdjustMutToConstPointer => { + match self.sty { + ty::TyRawPtr(mt) => cx.mk_ptr(ty::TypeAndMut { + ty: mt.ty, + mutbl: hir::MutImmutable + }), + ref b => { + cx.sess.bug( + &format!("AdjustMutToConstPointer on non-raw-ptr: \ + {:?}", + b)); + } + } + } AdjustDerefRef(ref adj) => { let mut adjusted_ty = self; diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index a0939dc53dfcf..5868f233776c2 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -396,6 +396,9 @@ impl<'tcx> fmt::Debug for ty::adjustment::AutoAdjustment<'tcx> { ty::adjustment::AdjustUnsafeFnPointer => { write!(f, "AdjustUnsafeFnPointer") } + ty::adjustment::AdjustMutToConstPointer => { + write!(f, "AdjustMutToConstPointer") + } ty::adjustment::AdjustDerefRef(ref data) => { write!(f, "{:?}", data) } diff --git a/src/librustc_metadata/astencode.rs b/src/librustc_metadata/astencode.rs index 3a0bd3aa2059d..fe4df865a0e61 100644 --- a/src/librustc_metadata/astencode.rs +++ b/src/librustc_metadata/astencode.rs @@ -610,7 +610,7 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> { self.emit_enum("AutoAdjustment", |this| { match *adj { - adjustment::AdjustReifyFnPointer=> { + adjustment::AdjustReifyFnPointer => { this.emit_enum_variant("AdjustReifyFnPointer", 1, 0, |_| Ok(())) } @@ -620,8 +620,14 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> { }) } + adjustment::AdjustMutToConstPointer => { + this.emit_enum_variant("AdjustMutToConstPointer", 3, 0, |_| { + Ok(()) + }) + } + adjustment::AdjustDerefRef(ref auto_deref_ref) => { - this.emit_enum_variant("AdjustDerefRef", 3, 2, |this| { + this.emit_enum_variant("AdjustDerefRef", 4, 2, |this| { this.emit_enum_variant_arg(0, |this| Ok(this.emit_auto_deref_ref(ecx, auto_deref_ref))) }) @@ -1002,12 +1008,14 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> { fn read_auto_adjustment<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>) -> adjustment::AutoAdjustment<'tcx> { self.read_enum("AutoAdjustment", |this| { - let variants = ["AdjustReifyFnPointer", "AdjustUnsafeFnPointer", "AdjustDerefRef"]; + let variants = ["AdjustReifyFnPointer", "AdjustUnsafeFnPointer", + "AdjustMutToConstPointer", "AdjustDerefRef"]; this.read_enum_variant(&variants, |this, i| { Ok(match i { 1 => adjustment::AdjustReifyFnPointer, 2 => adjustment::AdjustUnsafeFnPointer, - 3 => { + 3 => adjustment::AdjustMutToConstPointer, + 4 => { let auto_deref_ref: adjustment::AutoDerefRef = this.read_enum_variant_arg(0, |this| Ok(this.read_auto_deref_ref(dcx))).unwrap(); diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 769dbe0ab5162..324c1e92efc2e 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -416,6 +416,9 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { kind: kind, }; + debug!("unadjusted-expr={:?} applying adjustments={:?}", + expr, cx.tcx.tables.borrow().adjustments.get(&self.id)); + // Now apply adjustments, if any. match cx.tcx.tables.borrow().adjustments.get(&self.id) { None => {} @@ -437,6 +440,15 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { kind: ExprKind::UnsafeFnPointer { source: expr.to_ref() }, }; } + Some(&ty::adjustment::AdjustMutToConstPointer) => { + let adjusted_ty = cx.tcx.expr_ty_adjusted(self); + expr = Expr { + temp_lifetime: temp_lifetime, + ty: adjusted_ty, + span: self.span, + kind: ExprKind::Cast { source: expr.to_ref() }, + }; + } Some(&ty::adjustment::AdjustDerefRef(ref adj)) => { for i in 0..adj.autoderefs { let i = i as u32; diff --git a/src/librustc_passes/consts.rs b/src/librustc_passes/consts.rs index ee6003f713e7b..b0d459063ef29 100644 --- a/src/librustc_passes/consts.rs +++ b/src/librustc_passes/consts.rs @@ -768,7 +768,8 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp match v.tcx.tables.borrow().adjustments.get(&e.id) { None | Some(&ty::adjustment::AdjustReifyFnPointer) | - Some(&ty::adjustment::AdjustUnsafeFnPointer) => {} + Some(&ty::adjustment::AdjustUnsafeFnPointer) | + Some(&ty::adjustment::AdjustMutToConstPointer) => {} Some(&ty::adjustment::AdjustDerefRef( ty::adjustment::AutoDerefRef { autoderefs, .. } diff --git a/src/librustc_trans/trans/consts.rs b/src/librustc_trans/trans/consts.rs index e8c90fa31eac2..79c73a83ee7e0 100644 --- a/src/librustc_trans/trans/consts.rs +++ b/src/librustc_trans/trans/consts.rs @@ -40,7 +40,7 @@ use trans::type_of; use trans::Disr; use middle::subst::Substs; use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer}; -use middle::ty::adjustment::AdjustUnsafeFnPointer; +use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer}; use middle::ty::{self, Ty}; use middle::ty::cast::{CastTy,IntTy}; use util::nodemap::NodeMap; @@ -354,7 +354,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>, // FIXME(#19925) once fn item types are // zero-sized, we'll need to do something here } - Some(AdjustUnsafeFnPointer) => { + Some(AdjustUnsafeFnPointer) | Some(AdjustMutToConstPointer) => { // purely a type-level thing } Some(AdjustDerefRef(adj)) => { diff --git a/src/librustc_trans/trans/expr.rs b/src/librustc_trans/trans/expr.rs index fac956c1ddd20..782e38d3af2d4 100644 --- a/src/librustc_trans/trans/expr.rs +++ b/src/librustc_trans/trans/expr.rs @@ -71,7 +71,8 @@ use trans::tvec; use trans::type_of; use trans::Disr; use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer}; -use middle::ty::adjustment::{AdjustUnsafeFnPointer, CustomCoerceUnsized}; +use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer}; +use middle::ty::adjustment::CustomCoerceUnsized; use middle::ty::{self, Ty}; use middle::ty::MethodCall; use middle::ty::cast::{CastKind, CastTy}; @@ -354,7 +355,7 @@ fn adjustment_required<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // zero-sized, we'll need to return true here false } - AdjustUnsafeFnPointer => { + AdjustUnsafeFnPointer | AdjustMutToConstPointer => { // purely a type-level thing false } @@ -391,7 +392,7 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>, // FIXME(#19925) once fn item types are // zero-sized, we'll need to do something here } - AdjustUnsafeFnPointer => { + AdjustUnsafeFnPointer | AdjustMutToConstPointer => { // purely a type-level thing } AdjustDerefRef(ref adj) => { diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index 8f64e85de4b0f..f07464592faac 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -67,7 +67,7 @@ use middle::traits::{self, ObligationCause}; use middle::traits::{predicate_for_trait_def, report_selection_error}; use middle::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef}; use middle::ty::adjustment::{AutoPtr, AutoUnsafe, AdjustReifyFnPointer}; -use middle::ty::adjustment::{AdjustUnsafeFnPointer}; +use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer}; use middle::ty::{self, LvaluePreference, TypeAndMut, Ty}; use middle::ty::fold::TypeFoldable; use middle::ty::error::TypeError; @@ -427,6 +427,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { autoref: Some(AutoUnsafe(mutbl_b)), unsize: None }))) + } else if mt_a.mutbl != mutbl_b { + Ok(Some(AdjustMutToConstPointer)) } else { Ok(None) } diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index e663e449cfc4a..5135323979034 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -305,6 +305,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { adjustment::AdjustReifyFnPointer } + adjustment::AdjustMutToConstPointer => { + adjustment::AdjustMutToConstPointer + } + adjustment::AdjustUnsafeFnPointer => { adjustment::AdjustUnsafeFnPointer } From 350b50df0035b5bb0520d5a84adfc117c5ce9752 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 11 Feb 2016 18:31:31 +0200 Subject: [PATCH 07/13] deref the argument of overloaded MIR autoderef Fixes #31466 --- src/librustc_mir/hair/cx/expr.rs | 36 +++++++++++++++++++++++--- src/test/run-pass/mir_autoderef.rs | 41 ++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 src/test/run-pass/mir_autoderef.rs diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 324c1e92efc2e..4a91f2b66a1eb 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -416,7 +416,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { kind: kind, }; - debug!("unadjusted-expr={:?} applying adjustments={:?}", + debug!("make_mirror: unadjusted-expr={:?} applying adjustments={:?}", expr, cx.tcx.tables.borrow().adjustments.get(&self.id)); // Now apply adjustments, if any. @@ -459,10 +459,38 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { self.span, i, |mc| cx.tcx.tables.borrow().method_map.get(&mc).map(|m| m.ty)); - let kind = if cx.tcx.is_overloaded_autoderef(self.id, i) { - overloaded_lvalue(cx, self, ty::MethodCall::autoderef(self.id, i), - PassArgs::ByValue, expr.to_ref(), vec![]) + debug!("make_mirror: autoderef #{}, adjusted_ty={:?}", i, adjusted_ty); + let method_key = ty::MethodCall::autoderef(self.id, i); + let meth_ty = + cx.tcx.tables.borrow().method_map.get(&method_key).map(|m| m.ty); + let kind = if let Some(meth_ty) = meth_ty { + debug!("make_mirror: overloaded autoderef (meth_ty={:?})", meth_ty); + + let ref_ty = cx.tcx.no_late_bound_regions(&meth_ty.fn_ret()); + let (region, mutbl) = match ref_ty { + Some(ty::FnConverging(&ty::TyS { + sty: ty::TyRef(region, mt), .. + })) => (region, mt.mutbl), + _ => cx.tcx.sess.span_bug( + expr.span, "autoderef returned bad type") + }; + + expr = Expr { + temp_lifetime: temp_lifetime, + ty: cx.tcx.mk_ref( + region, ty::TypeAndMut { ty: expr.ty, mutbl: mutbl }), + span: expr.span, + kind: ExprKind::Borrow { + region: *region, + borrow_kind: to_borrow_kind(mutbl), + arg: expr.to_ref() + } + }; + + overloaded_lvalue(cx, self, method_key, + PassArgs::ByRef, expr.to_ref(), vec![]) } else { + debug!("make_mirror: built-in autoderef"); ExprKind::Deref { arg: expr.to_ref() } }; expr = Expr { diff --git a/src/test/run-pass/mir_autoderef.rs b/src/test/run-pass/mir_autoderef.rs new file mode 100644 index 0000000000000..81712e4569f06 --- /dev/null +++ b/src/test/run-pass/mir_autoderef.rs @@ -0,0 +1,41 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +use std::ops::{Deref, DerefMut}; + +pub struct MyRef(u32); + +impl Deref for MyRef { + type Target = u32; + fn deref(&self) -> &u32 { &self.0 } +} + +impl DerefMut for MyRef { + fn deref_mut(&mut self) -> &mut u32 { &mut self.0 } +} + + +#[rustc_mir] +fn deref(x: &MyRef) -> &u32 { + x +} + +#[rustc_mir] +fn deref_mut(x: &mut MyRef) -> &mut u32 { + x +} + +fn main() { + let mut r = MyRef(2); + assert_eq!(deref(&r) as *const _, &r.0 as *const _); + assert_eq!(deref_mut(&mut r) as *mut _, &mut r.0 as *mut _); +} From 3c6f41026bea2a99ab09d15f3dbed838af1ebcac Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 11 Feb 2016 18:31:42 +0200 Subject: [PATCH 08/13] store the normalized types of field accesses Fixes #31504 --- src/librustc/middle/ty/context.rs | 7 ++++ src/librustc/mir/repr.rs | 10 ++--- src/librustc/mir/tcx.rs | 18 +------- src/librustc_mir/build/expr/as_lvalue.rs | 2 +- src/librustc_mir/build/expr/as_rvalue.rs | 31 +++++++------- src/librustc_mir/build/matches/test.rs | 3 +- src/librustc_mir/build/matches/util.rs | 3 +- src/librustc_mir/hair/cx/expr.rs | 23 +++++++++-- src/librustc_mir/hair/cx/pattern.rs | 17 ++++++++ src/librustc_mir/hair/mod.rs | 8 +++- src/librustc_mir/transform/type_check.rs | 1 + src/librustc_trans/trans/mir/lvalue.rs | 2 +- src/librustc_typeck/check/mod.rs | 23 ++++++++--- src/librustc_typeck/check/writeback.rs | 17 +++++++- src/test/run-pass/mir_struct_with_assoc_ty.rs | 41 +++++++++++++++++++ 15 files changed, 150 insertions(+), 56 deletions(-) create mode 100644 src/test/run-pass/mir_struct_with_assoc_ty.rs diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs index 1d071cd604db4..ea6b4df8104d5 100644 --- a/src/librustc/middle/ty/context.rs +++ b/src/librustc/middle/ty/context.rs @@ -131,6 +131,12 @@ pub struct Tables<'tcx> { /// equivalents. This table is not used in trans (since regions /// are erased there) and hence is not serialized to metadata. pub liberated_fn_sigs: NodeMap>, + + /// For each FRU expression, record the normalized types of the fields + /// of the struct - this is needed because it is non-trivial to + /// normalize while preserving regions. This table is used only in + /// MIR construction and hence is not serialized to metadata. + pub fru_field_types: NodeMap>> } impl<'tcx> Tables<'tcx> { @@ -144,6 +150,7 @@ impl<'tcx> Tables<'tcx> { closure_tys: DefIdMap(), closure_kinds: DefIdMap(), liberated_fn_sigs: NodeMap(), + fru_field_types: NodeMap() } } diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 2f24a1a8962de..ce7b1ceb35540 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -502,7 +502,7 @@ pub struct Projection<'tcx, B, V> { #[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] pub enum ProjectionElem<'tcx, V> { Deref, - Field(Field), + Field(Field, Ty<'tcx>), Index(V), /// These indices are generated by slice patterns. Easiest to explain @@ -553,8 +553,8 @@ impl Field { } impl<'tcx> Lvalue<'tcx> { - pub fn field(self, f: Field) -> Lvalue<'tcx> { - self.elem(ProjectionElem::Field(f)) + pub fn field(self, f: Field, ty: Ty<'tcx>) -> Lvalue<'tcx> { + self.elem(ProjectionElem::Field(f, ty)) } pub fn deref(self) -> Lvalue<'tcx> { @@ -594,8 +594,8 @@ impl<'tcx> Debug for Lvalue<'tcx> { write!(fmt, "({:?} as {})", data.base, adt_def.variants[index].name), ProjectionElem::Deref => write!(fmt, "(*{:?})", data.base), - ProjectionElem::Field(field) => - write!(fmt, "{:?}.{:?}", data.base, field.index()), + ProjectionElem::Field(field, ty) => + write!(fmt, "({:?}.{:?}: {:?})", data.base, field.index(), ty), ProjectionElem::Index(ref index) => write!(fmt, "{:?}[{:?}]", data.base, index), ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => diff --git a/src/librustc/mir/tcx.rs b/src/librustc/mir/tcx.rs index e8b83962b521b..20e083f840f8c 100644 --- a/src/librustc/mir/tcx.rs +++ b/src/librustc/mir/tcx.rs @@ -73,23 +73,7 @@ impl<'tcx> LvalueTy<'tcx> { tcx.sess.bug(&format!("cannot downcast non-enum type: `{:?}`", self)) } }, - ProjectionElem::Field(field) => { - let field_ty = match self { - LvalueTy::Ty { ty } => match ty.sty { - ty::TyStruct(adt_def, substs) => - adt_def.struct_variant().fields[field.index()].ty(tcx, substs), - ty::TyTuple(ref tys) => - tys[field.index()], - ty::TyClosure(_, ref closure_substs) => - closure_substs.upvar_tys[field.index()], - _ => - tcx.sess.bug(&format!("cannot get field of type: `{:?}`", ty)), - }, - LvalueTy::Downcast { adt_def, substs, variant_index } => - adt_def.variants[variant_index].fields[field.index()].ty(tcx, substs), - }; - LvalueTy::Ty { ty: field_ty } - } + ProjectionElem::Field(_, fty) => LvalueTy::Ty { ty: fty } } } } diff --git a/src/librustc_mir/build/expr/as_lvalue.rs b/src/librustc_mir/build/expr/as_lvalue.rs index 4e03ed489eb9f..b2c7507ed7b28 100644 --- a/src/librustc_mir/build/expr/as_lvalue.rs +++ b/src/librustc_mir/build/expr/as_lvalue.rs @@ -41,7 +41,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { } ExprKind::Field { lhs, name } => { let lvalue = unpack!(block = this.as_lvalue(block, lhs)); - let lvalue = lvalue.field(name); + let lvalue = lvalue.field(name, expr.ty); block.and(lvalue) } ExprKind::Deref { arg } => { diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index dea2d750b981b..53b106d6d8679 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -139,7 +139,9 @@ impl<'a,'tcx> Builder<'a,'tcx> { .collect(); block.and(Rvalue::Aggregate(AggregateKind::Closure(closure_id, substs), upvars)) } - ExprKind::Adt { adt_def, variant_index, substs, fields, base } => { // see (*) above + ExprKind::Adt { + adt_def, variant_index, substs, fields, base + } => { // see (*) above // first process the set of fields that were provided // (evaluating them in order given by user) let fields_map: FnvHashMap<_, _> = @@ -147,25 +149,20 @@ impl<'a,'tcx> Builder<'a,'tcx> { .map(|f| (f.name, unpack!(block = this.as_operand(block, f.expr)))) .collect(); - // if base expression is given, evaluate it now - let base = base.map(|base| unpack!(block = this.as_lvalue(block, base))); - - // get list of all fields that we will need let field_names = this.hir.all_fields(adt_def, variant_index); - // for the actual values we use, take either the - // expr the user specified or, if they didn't - // specify something for this field name, create a - // path relative to the base (which must have been - // supplied, or the IR is internally - // inconsistent). - let fields: Vec<_> = + let fields = if let Some(FruInfo { base, field_types }) = base { + let base = unpack!(block = this.as_lvalue(block, base)); field_names.into_iter() - .map(|n| match fields_map.get(&n) { - Some(v) => v.clone(), - None => Operand::Consume(base.clone().unwrap().field(n)), - }) - .collect(); + .zip(field_types.into_iter()) + .map(|(n, ty)| match fields_map.get(&n) { + Some(v) => v.clone(), + None => Operand::Consume(base.clone().field(n, ty)) + }) + .collect() + } else { + field_names.iter().map(|n| fields_map[n].clone()).collect() + }; block.and(Rvalue::Aggregate(AggregateKind::Adt(adt_def, variant_index, substs), fields)) diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index ec67429379f95..02f32da2b83fc 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -404,7 +404,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { subpatterns.iter() .map(|subpattern| { // e.g., `(x as Variant).0` - let lvalue = downcast_lvalue.clone().field(subpattern.field); + let lvalue = downcast_lvalue.clone().field(subpattern.field, + subpattern.field_ty()); // e.g., `(x as Variant).0 @ P1` MatchPair::new(lvalue, &subpattern.pattern) }); diff --git a/src/librustc_mir/build/matches/util.rs b/src/librustc_mir/build/matches/util.rs index dbb00a13cd3b4..c295ed168badb 100644 --- a/src/librustc_mir/build/matches/util.rs +++ b/src/librustc_mir/build/matches/util.rs @@ -21,7 +21,8 @@ impl<'a,'tcx> Builder<'a,'tcx> { -> Vec> { subpatterns.iter() .map(|fieldpat| { - let lvalue = lvalue.clone().field(fieldpat.field); + let lvalue = lvalue.clone().field(fieldpat.field, + fieldpat.field_ty()); MatchPair::new(lvalue, &fieldpat.pattern) }) .collect() diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 4a91f2b66a1eb..facc2541652e8 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -248,13 +248,23 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { variant_index: 0, substs: substs, fields: field_refs, - base: base.to_ref(), + base: base.as_ref().map(|base| { + FruInfo { + base: base.to_ref(), + field_types: cx.tcx.tables + .borrow() + .fru_field_types[&self.id] + .clone() + } + }) } } ty::TyEnum(adt, substs) => { match cx.tcx.def_map.borrow()[&self.id].full_def() { Def::Variant(enum_id, variant_id) => { debug_assert!(adt.did == enum_id); + assert!(base.is_none()); + let index = adt.variant_index_with_id(variant_id); let field_refs = field_refs(&adt.variants[index], fields); ExprKind::Adt { @@ -262,7 +272,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { variant_index: index, substs: substs, fields: field_refs, - base: base.to_ref(), + base: None } } ref def => { @@ -810,11 +820,16 @@ fn convert_var<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>, }; match upvar_capture { ty::UpvarCapture::ByValue => field_kind, - ty::UpvarCapture::ByRef(_) => { + ty::UpvarCapture::ByRef(borrow) => { ExprKind::Deref { arg: Expr { temp_lifetime: temp_lifetime, - ty: var_ty, + ty: cx.tcx.mk_ref( + cx.tcx.mk_region(borrow.region), + ty::TypeAndMut { + ty: var_ty, + mutbl: borrow.kind.to_mutbl_lossy() + }), span: expr.span, kind: field_kind, }.to_ref() diff --git a/src/librustc_mir/hair/cx/pattern.rs b/src/librustc_mir/hair/cx/pattern.rs index c5b34d9246694..6f4375d53ec4b 100644 --- a/src/librustc_mir/hair/cx/pattern.rs +++ b/src/librustc_mir/hair/cx/pattern.rs @@ -314,3 +314,20 @@ impl<'patcx, 'cx, 'tcx> PatCx<'patcx, 'cx, 'tcx> { } } } + +impl<'tcx> FieldPattern<'tcx> { + pub fn field_ty(&self) -> Ty<'tcx> { + debug!("field_ty({:?},ty={:?})", self, self.pattern.ty); + let r = match *self.pattern.kind { + PatternKind::Binding { mode: BindingMode::ByRef(..), ..} => { + match self.pattern.ty.sty { + ty::TyRef(_, mt) => mt.ty, + _ => unreachable!() + } + } + _ => self.pattern.ty + }; + debug!("field_ty -> {:?}", r); + r + } +} diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index 0891c2845b0ba..e8edd4067e2f8 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -229,7 +229,7 @@ pub enum ExprKind<'tcx> { variant_index: usize, substs: &'tcx Substs<'tcx>, fields: Vec>, - base: Option>, + base: Option> }, Closure { closure_id: DefId, @@ -256,6 +256,12 @@ pub struct FieldExprRef<'tcx> { pub expr: ExprRef<'tcx>, } +#[derive(Clone, Debug)] +pub struct FruInfo<'tcx> { + pub base: ExprRef<'tcx>, + pub field_types: Vec> +} + #[derive(Clone, Debug)] pub struct Arm<'tcx> { pub patterns: Vec>, diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index f4d83cd05e08f..8f4d452df51fa 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -47,6 +47,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> { fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: visit::LvalueContext) { self.super_lvalue(lvalue, context); + debug!("visiting lvalue {:?}", lvalue); let lv_ty = self.mir.lvalue_ty(self.tcx(), lvalue).to_ty(self.tcx()); self.sanitize_type(lvalue, lv_ty); } diff --git a/src/librustc_trans/trans/mir/lvalue.rs b/src/librustc_trans/trans/mir/lvalue.rs index 002584f51c6d7..826fb025bc1f7 100644 --- a/src/librustc_trans/trans/mir/lvalue.rs +++ b/src/librustc_trans/trans/mir/lvalue.rs @@ -126,7 +126,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> { } }) } - mir::ProjectionElem::Field(ref field) => { + mir::ProjectionElem::Field(ref field, _) => { let base_ty = tr_base.ty.to_ty(tcx); let base_repr = adt::represent_type(ccx, base_ty); let discr = match tr_base.ty { diff --git a/src/librustc_typeck/check/mod.rs b/src/librustc_typeck/check/mod.rs index 3768a2e30337e..7ab4975c8b8ae 100644 --- a/src/librustc_typeck/check/mod.rs +++ b/src/librustc_typeck/check/mod.rs @@ -3179,8 +3179,8 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, check_struct_fields_on_error(fcx, expr.id, fields, base_expr); return; } - let (adt, variant) = match fcx.def_struct_variant(def, path.span) { - Some((adt, variant)) => (adt, variant), + let variant = match fcx.def_struct_variant(def, path.span) { + Some((_, variant)) => variant, None => { span_err!(fcx.tcx().sess, path.span, E0071, "`{}` does not name a structure", @@ -3195,12 +3195,23 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>, check_expr_struct_fields(fcx, expr_ty, expr.span, variant, fields, base_expr.is_none()); - if let &Some(ref base_expr) = base_expr { check_expr_has_type(fcx, base_expr, expr_ty); - if adt.adt_kind() == ty::AdtKind::Enum { - span_err!(tcx.sess, base_expr.span, E0436, - "functional record update syntax requires a struct"); + match expr_ty.sty { + ty::TyStruct(adt, substs) => { + fcx.inh.tables.borrow_mut().fru_field_types.insert( + expr.id, + adt.struct_variant().fields.iter().map(|f| { + fcx.normalize_associated_types_in( + expr.span, &f.ty(tcx, substs) + ) + }).collect() + ); + } + _ => { + span_err!(tcx.sess, base_expr.span, E0436, + "functional record update syntax requires a struct"); + } } } } diff --git a/src/librustc_typeck/check/writeback.rs b/src/librustc_typeck/check/writeback.rs index 5135323979034..9b8b6dedb6345 100644 --- a/src/librustc_typeck/check/writeback.rs +++ b/src/librustc_typeck/check/writeback.rs @@ -43,6 +43,7 @@ pub fn resolve_type_vars_in_expr(fcx: &FnCtxt, e: &hir::Expr) { wbcx.visit_upvar_borrow_map(); wbcx.visit_closures(); wbcx.visit_liberated_fn_sigs(); + wbcx.visit_fru_field_types(); } pub fn resolve_type_vars_in_fn(fcx: &FnCtxt, @@ -64,6 +65,7 @@ pub fn resolve_type_vars_in_fn(fcx: &FnCtxt, wbcx.visit_upvar_borrow_map(); wbcx.visit_closures(); wbcx.visit_liberated_fn_sigs(); + wbcx.visit_fru_field_types(); } /////////////////////////////////////////////////////////////////////////// @@ -371,6 +373,13 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { } } + fn visit_fru_field_types(&self) { + for (&node_id, ftys) in self.fcx.inh.tables.borrow().fru_field_types.iter() { + let ftys = self.resolve(ftys, ResolvingFieldTypes(node_id)); + self.tcx().tables.borrow_mut().fru_field_types.insert(node_id, ftys); + } + } + fn resolve>(&self, t: &T, reason: ResolveReason) -> T { t.fold_with(&mut Resolver::new(self.fcx, reason)) } @@ -387,6 +396,7 @@ enum ResolveReason { ResolvingUpvar(ty::UpvarId), ResolvingClosure(DefId), ResolvingFnSig(ast::NodeId), + ResolvingFieldTypes(ast::NodeId) } impl ResolveReason { @@ -401,6 +411,9 @@ impl ResolveReason { ResolvingFnSig(id) => { tcx.map.span(id) } + ResolvingFieldTypes(id) => { + tcx.map.span(id) + } ResolvingClosure(did) => { if let Some(node_id) = tcx.map.as_local_node_id(did) { tcx.expr_span(node_id) @@ -478,14 +491,14 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { "cannot determine a type for this closure") } - ResolvingFnSig(id) => { + ResolvingFnSig(id) | ResolvingFieldTypes(id) => { // any failures here should also fail when // resolving the patterns, closure types, or // something else. let span = self.reason.span(self.tcx); self.tcx.sess.delay_span_bug( span, - &format!("cannot resolve some aspect of fn sig for {:?}", id)); + &format!("cannot resolve some aspect of data for {:?}", id)); } } } diff --git a/src/test/run-pass/mir_struct_with_assoc_ty.rs b/src/test/run-pass/mir_struct_with_assoc_ty.rs new file mode 100644 index 0000000000000..1f75369b94a86 --- /dev/null +++ b/src/test/run-pass/mir_struct_with_assoc_ty.rs @@ -0,0 +1,41 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(rustc_attrs)] + +use std::marker::PhantomData; + +pub trait DataBind { + type Data; +} + +impl DataBind for Global { + type Data = T; +} + +pub struct Global(PhantomData); + +pub struct Data { + pub offsets: as DataBind>::Data, +} + +#[rustc_mir] +fn create_data() -> Data { + let mut d = Data { offsets: [1, 2] }; + d.offsets[0] = 3; + d +} + + +fn main() { + let d = create_data(); + assert_eq!(d.offsets[0], 3); + assert_eq!(d.offsets[1], 2); +} From 880b6c260afc13df012d81c52a5fc1cc4348976e Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 11 Feb 2016 15:38:52 +0200 Subject: [PATCH 09/13] fix a few remaining bugs - make check runs! --- src/librustc_driver/driver.rs | 20 +- src/librustc_mir/hair/cx/expr.rs | 26 ++- src/librustc_mir/transform/type_check.rs | 8 + .../run-pass/mir_augmented_assignments.rs | 183 ++++++++++++++++++ 4 files changed, 220 insertions(+), 17 deletions(-) create mode 100644 src/test/run-pass/mir_augmented_assignments.rs diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 6eede3070b27a..b446dec96fbfb 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -844,6 +844,18 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, "match checking", || middle::check_match::check_crate(tcx)); + // this must run before MIR dump, because + // "not all control paths return a value" is reported here. + // + // maybe move the check to a MIR pass? + time(time_passes, + "liveness checking", + || middle::liveness::check_crate(tcx)); + + time(time_passes, + "rvalue checking", + || rvalues::check_crate(tcx)); + let mut mir_map = time(time_passes, "MIR dump", @@ -853,18 +865,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session, "MIR passes", || mir_map.run_passes(&mut sess.plugin_mir_passes.borrow_mut(), tcx)); - time(time_passes, - "liveness checking", - || middle::liveness::check_crate(tcx)); - time(time_passes, "borrow checking", || borrowck::check_crate(tcx)); - time(time_passes, - "rvalue checking", - || rvalues::check_crate(tcx)); - // Avoid overwhelming user with errors if type checking failed. // I'm not sure how helpful this is, to be honest, but it avoids // a diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index facc2541652e8..ac1cff527fe91 100644 --- a/src/librustc_mir/hair/cx/expr.rs +++ b/src/librustc_mir/hair/cx/expr.rs @@ -32,6 +32,8 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { debug!("Expr::make_mirror(): id={}, span={:?}", self.id, self.span); let expr_ty = cx.tcx.expr_ty(self); // note: no adjustments (yet)! + let temp_lifetime = cx.tcx.region_maps.temporary_scope(self.id); + let expr_extent = cx.tcx.region_maps.node_extent(self.id); let kind = match self.node { // Here comes the interesting stuff: @@ -72,7 +74,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { let tupled_args = Expr { ty: sig.inputs[1], - temp_lifetime: cx.tcx.region_maps.temporary_scope(self.id), + temp_lifetime: temp_lifetime, span: self.span, kind: ExprKind::Tuple { fields: args.iter().map(ToRef::to_ref).collect() @@ -146,11 +148,20 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { } hir::ExprAssignOp(op, ref lhs, ref rhs) => { - let op = bin_op(op.node); - ExprKind::AssignOp { - op: op, - lhs: lhs.to_ref(), - rhs: rhs.to_ref(), + if cx.tcx.is_method_call(self.id) { + let pass_args = if hir_util::is_by_value_binop(op.node) { + PassArgs::ByValue + } else { + PassArgs::ByRef + }; + overloaded_operator(cx, self, ty::MethodCall::expr(self.id), + pass_args, lhs.to_ref(), vec![rhs]) + } else { + ExprKind::AssignOp { + op: bin_op(op.node), + lhs: lhs.to_ref(), + rhs: rhs.to_ref(), + } } } @@ -416,9 +427,6 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr { ExprKind::Tuple { fields: fields.to_ref() }, }; - let temp_lifetime = cx.tcx.region_maps.temporary_scope(self.id); - let expr_extent = cx.tcx.region_maps.node_extent(self.id); - let mut expr = Expr { temp_lifetime: temp_lifetime, ty: expr_ty, diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 8f4d452df51fa..b2c7478ead5fe 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -9,6 +9,7 @@ // except according to those terms. //! This pass type-checks the MIR to ensure it is not broken. +#![allow(unreachable_code)] use rustc::middle::infer; use rustc::middle::ty::{self, Ty}; @@ -322,6 +323,13 @@ impl<'a, 'tcx> MirPass for TypeckMir<'a, 'tcx> { _tcx: &ty::ctxt<'tcx_>) { // FIXME: pass param_env to run_on_mir let mir: &mut Mir<'tcx> = unsafe { ::std::mem::transmute(mir) }; + + if self.tcx().sess.err_count() > 0 { + // compiling a broken program can obviously result in a + // broken MIR, so try not to report duplicate errors. + return; + } + let mut type_verifier = TypeVerifier::new(self.infcx, mir); type_verifier.visit_mir(mir); diff --git a/src/test/run-pass/mir_augmented_assignments.rs b/src/test/run-pass/mir_augmented_assignments.rs new file mode 100644 index 0000000000000..cadfce367a426 --- /dev/null +++ b/src/test/run-pass/mir_augmented_assignments.rs @@ -0,0 +1,183 @@ +// Copyright 2015 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. + +#![feature(augmented_assignments)] +#![feature(op_assign_traits)] +#![feature(rustc_attrs)] + +use std::mem; +use std::ops::{ + AddAssign, BitAndAssign, BitOrAssign, BitXorAssign, DivAssign, MulAssign, RemAssign, + ShlAssign, ShrAssign, SubAssign, +}; + +#[derive(Debug, PartialEq)] +struct Int(i32); + +struct Slice([i32]); + +impl Slice { + fn new(slice: &mut [i32]) -> &mut Slice { + unsafe { + mem::transmute(slice) + } + } +} + +fn main() { + main_mir(); +} + +#[rustc_mir] +fn main_mir() { + let mut x = Int(1); + + x += Int(2); + assert_eq!(x, Int(0b11)); + + x &= Int(0b01); + assert_eq!(x, Int(0b01)); + + x |= Int(0b10); + assert_eq!(x, Int(0b11)); + + x ^= Int(0b01); + assert_eq!(x, Int(0b10)); + + x /= Int(2); + assert_eq!(x, Int(1)); + + x *= Int(3); + assert_eq!(x, Int(3)); + + x %= Int(2); + assert_eq!(x, Int(1)); + + // overloaded RHS + x <<= 1u8; + assert_eq!(x, Int(2)); + + x <<= 1u16; + assert_eq!(x, Int(4)); + + x >>= 1u8; + assert_eq!(x, Int(2)); + + x >>= 1u16; + assert_eq!(x, Int(1)); + + x -= Int(1); + assert_eq!(x, Int(0)); + + // indexed LHS + let mut v = vec![Int(1), Int(2)]; + v[0] += Int(2); + assert_eq!(v[0], Int(3)); + + // unsized RHS + let mut array = [0, 1, 2]; + *Slice::new(&mut array) += 1; + assert_eq!(array[0], 1); + assert_eq!(array[1], 2); + assert_eq!(array[2], 3); +} + +impl AddAssign for Int { + #[rustc_mir] + fn add_assign(&mut self, rhs: Int) { + self.0 += rhs.0; + } +} + +impl BitAndAssign for Int { + #[rustc_mir] + fn bitand_assign(&mut self, rhs: Int) { + self.0 &= rhs.0; + } +} + +impl BitOrAssign for Int { + #[rustc_mir] + fn bitor_assign(&mut self, rhs: Int) { + self.0 |= rhs.0; + } +} + +impl BitXorAssign for Int { + #[rustc_mir] + fn bitxor_assign(&mut self, rhs: Int) { + self.0 ^= rhs.0; + } +} + +impl DivAssign for Int { + #[rustc_mir] + fn div_assign(&mut self, rhs: Int) { + self.0 /= rhs.0; + } +} + +impl MulAssign for Int { + #[rustc_mir] + fn mul_assign(&mut self, rhs: Int) { + self.0 *= rhs.0; + } +} + +impl RemAssign for Int { + #[rustc_mir] + fn rem_assign(&mut self, rhs: Int) { + self.0 %= rhs.0; + } +} + +impl ShlAssign for Int { + #[rustc_mir] + fn shl_assign(&mut self, rhs: u8) { + self.0 <<= rhs; + } +} + +impl ShlAssign for Int { + #[rustc_mir] + fn shl_assign(&mut self, rhs: u16) { + self.0 <<= rhs; + } +} + +impl ShrAssign for Int { + #[rustc_mir] + fn shr_assign(&mut self, rhs: u8) { + self.0 >>= rhs; + } +} + +impl ShrAssign for Int { + #[rustc_mir] + fn shr_assign(&mut self, rhs: u16) { + self.0 >>= rhs; + } +} + +impl SubAssign for Int { + #[rustc_mir] + fn sub_assign(&mut self, rhs: Int) { + self.0 -= rhs.0; + } +} + +impl AddAssign for Slice { + #[rustc_mir] + fn add_assign(&mut self, rhs: i32) { + for lhs in &mut self.0 { + *lhs += rhs; + } + } +} From ae919d0f4bf4a3bff387de6a33467cb0f198436b Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 11 Feb 2016 18:05:28 +0200 Subject: [PATCH 10/13] type-check lvalues --- src/librustc_mir/lib.rs | 1 + src/librustc_mir/transform/type_check.rs | 227 ++++++++++++++++++++++- 2 files changed, 218 insertions(+), 10 deletions(-) diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 6f4128fc24de4..5d915f37f6ebf 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -20,6 +20,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![cfg_attr(not(stage0), deny(warnings))] #![unstable(feature = "rustc_private", issue = "27812")] +#![feature(box_patterns)] #![feature(rustc_private)] #![feature(staged_api)] diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index b2c7478ead5fe..010e327425384 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -12,9 +12,11 @@ #![allow(unreachable_code)] use rustc::middle::infer; +use rustc::middle::traits; use rustc::middle::ty::{self, Ty}; use rustc::middle::ty::fold::TypeFoldable; use rustc::mir::repr::*; +use rustc::mir::tcx::LvalueTy; use rustc::mir::transform::MirPass; use rustc::mir::visit::{self, Visitor}; @@ -25,11 +27,27 @@ macro_rules! span_mirbug { ($context:expr, $elem:expr, $($message:tt)*) => ({ $context.tcx().sess.span_warn( $context.last_span, - &format!("broken MIR ({:?}): {:?}", $elem, format!($($message)*)) + &format!("broken MIR ({:?}): {}", $elem, format!($($message)*)) ) }) } +macro_rules! span_mirbug_and_err { + ($context:expr, $elem:expr, $($message:tt)*) => ({ + { + $context.tcx().sess.span_bug( + $context.last_span, + &format!("broken MIR ({:?}): {:?}", $elem, format!($($message)*)) + ); + $context.error() + } + }) +} + +enum FieldAccessError { + OutOfRange { field_count: usize } +} + /// Verifies that MIR types are sane to not crash further /// checks. struct TypeVerifier<'a, 'tcx: 'a> { @@ -46,11 +64,8 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> { } } - fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, context: visit::LvalueContext) { - self.super_lvalue(lvalue, context); - debug!("visiting lvalue {:?}", lvalue); - let lv_ty = self.mir.lvalue_ty(self.tcx(), lvalue).to_ty(self.tcx()); - self.sanitize_type(lvalue, lv_ty); + fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, _context: visit::LvalueContext) { + self.sanitize_lvalue(lvalue); } fn visit_constant(&mut self, constant: &Constant<'tcx>) { @@ -78,6 +93,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> { for (n, tmp_decl) in mir.temp_decls.iter().enumerate() { self.sanitize_type(&(n, tmp_decl), tmp_decl.ty); } + if self.errors_reported { + return; + } self.super_mir(mir); } } @@ -96,12 +114,201 @@ impl<'a, 'tcx> TypeVerifier<'a, 'tcx> { self.infcx.tcx } - fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) { - if !(ty.needs_infer() || ty.has_escaping_regions()) { - return; + fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { + if !(ty.needs_infer() || ty.has_escaping_regions() || + ty.references_error()) { + return ty; + } + span_mirbug_and_err!(self, parent, "bad type {:?}", ty) + } + + fn sanitize_lvalue(&mut self, lvalue: &Lvalue<'tcx>) -> LvalueTy<'tcx> { + debug!("sanitize_lvalue: {:?}", lvalue); + match *lvalue { + Lvalue::Var(index) => LvalueTy::Ty { ty: self.mir.var_decls[index as usize].ty }, + Lvalue::Temp(index) => + LvalueTy::Ty { ty: self.mir.temp_decls[index as usize].ty }, + Lvalue::Arg(index) => + LvalueTy::Ty { ty: self.mir.arg_decls[index as usize].ty }, + Lvalue::Static(def_id) => + LvalueTy::Ty { ty: self.tcx().lookup_item_type(def_id).ty }, + Lvalue::ReturnPointer => { + if let ty::FnConverging(return_ty) = self.mir.return_ty { + LvalueTy::Ty { ty: return_ty } + } else { + LvalueTy::Ty { + ty: span_mirbug_and_err!( + self, lvalue, "return in diverging function") + } + } + } + Lvalue::Projection(ref proj) => { + let base_ty = self.sanitize_lvalue(&proj.base); + if let LvalueTy::Ty { ty } = base_ty { + if ty.references_error() { + assert!(self.errors_reported); + return LvalueTy::Ty { ty: self.tcx().types.err }; + } + } + self.sanitize_projection(base_ty, &proj.elem, lvalue) + } + } + } + + fn sanitize_projection(&mut self, + base: LvalueTy<'tcx>, + pi: &LvalueElem<'tcx>, + lvalue: &Lvalue<'tcx>) + -> LvalueTy<'tcx> { + debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, lvalue); + let tcx = self.tcx(); + let base_ty = base.to_ty(tcx); + match *pi { + ProjectionElem::Deref => { + let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference); + LvalueTy::Ty { + ty: deref_ty.map(|t| t.ty).unwrap_or_else(|| { + span_mirbug_and_err!( + self, lvalue, "deref of non-pointer {:?}", base_ty) + }) + } + } + ProjectionElem::Index(ref i) => { + self.visit_operand(i); + let index_ty = self.mir.operand_ty(tcx, i); + if index_ty != tcx.types.usize { + LvalueTy::Ty { + ty: span_mirbug_and_err!(self, i, "index by non-usize {:?}", i) + } + } else { + LvalueTy::Ty { + ty: base_ty.builtin_index().unwrap_or_else(|| { + span_mirbug_and_err!( + self, lvalue, "index of non-array {:?}", base_ty) + }) + } + } + } + ProjectionElem::ConstantIndex { .. } => { + // consider verifying in-bounds + LvalueTy::Ty { + ty: base_ty.builtin_index().unwrap_or_else(|| { + span_mirbug_and_err!( + self, lvalue, "index of non-array {:?}", base_ty) + }) + } + } + ProjectionElem::Downcast(adt_def1, index) => + match base_ty.sty { + ty::TyEnum(adt_def, substs) if adt_def == adt_def1 => { + if index >= adt_def.variants.len() { + LvalueTy::Ty { + ty: span_mirbug_and_err!( + self, + lvalue, + "cast to variant #{:?} but enum only has {:?}", + index, + adt_def.variants.len()) + } + } else { + LvalueTy::Downcast { + adt_def: adt_def, + substs: substs, + variant_index: index + } + } + } + _ => LvalueTy::Ty { + ty: span_mirbug_and_err!( + self, lvalue, "can't downcast {:?}", base_ty) + } + }, + ProjectionElem::Field(field, fty) => { + let fty = self.sanitize_type(lvalue, fty); + match self.field_ty(lvalue, base, field) { + Ok(ty) => { + if let Err(terr) = infer::can_mk_subty(self.infcx, ty, fty) { + span_mirbug!( + self, lvalue, "bad field access ({:?}: {:?}): {:?}", + ty, fty, terr); + } + } + Err(FieldAccessError::OutOfRange { field_count }) => { + span_mirbug!( + self, lvalue, "accessed field #{} but variant only has {}", + field.index(), field_count) + } + } + LvalueTy::Ty { ty: fty } + } } - span_mirbug!(self, parent, "bad type {:?}", ty); + } + + fn error(&mut self) -> Ty<'tcx> { self.errors_reported = true; + self.tcx().types.err + } + + fn field_ty(&mut self, + parent: &fmt::Debug, + base_ty: LvalueTy<'tcx>, + field: Field) + -> Result, FieldAccessError> + { + let tcx = self.tcx(); + + let (variant, substs) = match base_ty { + LvalueTy::Downcast { adt_def, substs, variant_index } => { + (&adt_def.variants[variant_index], substs) + } + LvalueTy::Ty { ty } => match ty.sty { + ty::TyStruct(adt_def, substs) | ty::TyEnum(adt_def, substs) + if adt_def.is_univariant() => { + (&adt_def.variants[0], substs) + } + ty::TyTuple(ref tys) | ty::TyClosure(_, box ty::ClosureSubsts { + upvar_tys: ref tys, .. + }) => { + return match tys.get(field.index()) { + Some(&ty) => Ok(ty), + None => Err(FieldAccessError::OutOfRange { + field_count: tys.len() + }) + } + } + _ => return Ok(span_mirbug_and_err!( + self, parent, "can't project out of {:?}", base_ty)) + } + }; + + if let Some(field) = variant.fields.get(field.index()) { + Ok(self.normalize(parent, field.ty(tcx, substs))) + } else { + Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) + } + } + + fn normalize(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { + let mut selcx = traits::SelectionContext::new(&self.infcx); + let cause = traits::ObligationCause::misc(self.last_span, 0); + let traits::Normalized { value: ty, obligations } = + traits::normalize(&mut selcx, cause, &ty); + + debug!("normalize: ty={:?} obligations={:?}", + ty, + obligations); + + let mut fulfill_cx = self.infcx.fulfillment_cx.borrow_mut(); + for obligation in obligations { + fulfill_cx.register_predicate_obligation(&self.infcx, obligation); + } + + match infer::drain_fulfillment_cx(&self.infcx, &mut fulfill_cx, &ty) { + Ok(ty) => ty, + Err(e) => { + span_mirbug_and_err!(self, parent, "trait fulfillment failed: {:?}", e) + } + } } } From a61963ab08a447e62ea3c8f4da35da541f1885b6 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 11 Feb 2016 18:41:01 +0200 Subject: [PATCH 11/13] TODO -> FIXME --- src/librustc_mir/build/scope.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index 314f75beea149..7be70867796ff 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -418,7 +418,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { len: Operand<'tcx>, span: Span) { // fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> ! - let region = ty::ReStatic; // TODO(mir-borrowck): use a better region? + let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region? let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem); let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0; @@ -452,7 +452,7 @@ impl<'a,'tcx> Builder<'a,'tcx> { /// Create diverge cleanup and branch to it from `block`. pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) { // fn(&(msg: &'static str filename: &'static str, line: u32)) -> ! - let region = ty::ReStatic; // TODO(mir-borrowck): use a better region? + let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region? let func = self.lang_function(lang_items::PanicFnLangItem); let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0; From 881249aa463db43e41e4d5f98d6f71f2970f8965 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 13 Feb 2016 00:23:16 +0200 Subject: [PATCH 12/13] use the FulfillmentContext and InferCtxt more correctly --- src/librustc/mir/mir_map.rs | 10 +- src/librustc/mir/transform.rs | 4 +- src/librustc_mir/mir_map.rs | 8 +- .../transform/clear_dead_blocks.rs | 5 +- src/librustc_mir/transform/erase_regions.rs | 13 +- src/librustc_mir/transform/no_landing_pads.rs | 7 +- src/librustc_mir/transform/simplify_cfg.rs | 3 +- src/librustc_mir/transform/type_check.rs | 137 ++++++++++++------ src/test/auxiliary/dummy_mir_pass.rs | 7 +- 9 files changed, 117 insertions(+), 77 deletions(-) diff --git a/src/librustc/mir/mir_map.rs b/src/librustc/mir/mir_map.rs index aa24f6d897969..82bd2caaac196 100644 --- a/src/librustc/mir/mir_map.rs +++ b/src/librustc/mir/mir_map.rs @@ -12,6 +12,7 @@ use util::nodemap::NodeMap; use mir::repr::Mir; use mir::transform::MirPass; use middle::ty; +use middle::infer; pub struct MirMap<'tcx> { pub map: NodeMap>, @@ -19,9 +20,14 @@ pub struct MirMap<'tcx> { impl<'tcx> MirMap<'tcx> { pub fn run_passes(&mut self, passes: &mut [Box], tcx: &ty::ctxt<'tcx>) { - for (_, ref mut mir) in &mut self.map { + if passes.is_empty() { return; } + + for (&id, mir) in &mut self.map { + let param_env = ty::ParameterEnvironment::for_item(tcx, id); + let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env)); + for pass in &mut *passes { - pass.run_on_mir(mir, tcx) + pass.run_on_mir(mir, &infcx) } } } diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index 0f18d8d282144..cc417f5a99ea6 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -9,8 +9,8 @@ // except according to those terms. use mir::repr::Mir; -use middle::ty::ctxt; +use middle::infer::InferCtxt; pub trait MirPass { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ctxt<'tcx>); + fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, infcx: &InferCtxt<'a, 'tcx>); } diff --git a/src/librustc_mir/mir_map.rs b/src/librustc_mir/mir_map.rs index 68d85dc8394b9..70c3354012135 100644 --- a/src/librustc_mir/mir_map.rs +++ b/src/librustc_mir/mir_map.rs @@ -149,11 +149,11 @@ 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) => { - clear_dead_blocks::ClearDeadBlocks::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); + clear_dead_blocks::ClearDeadBlocks::new().run_on_mir(&mut mir, &infcx); + type_check::TypeckMir::new().run_on_mir(&mut mir, &infcx); + no_landing_pads::NoLandingPads.run_on_mir(&mut mir, &infcx); if self.tcx.sess.opts.mir_opt_level > 0 { - simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, self.tcx); + simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, &infcx); } let meta_item_list = self.attr .iter() diff --git a/src/librustc_mir/transform/clear_dead_blocks.rs b/src/librustc_mir/transform/clear_dead_blocks.rs index 88c0116f26d24..2c08b6b0b81b8 100644 --- a/src/librustc_mir/transform/clear_dead_blocks.rs +++ b/src/librustc_mir/transform/clear_dead_blocks.rs @@ -15,7 +15,7 @@ //! This pass does not renumber or remove the blocks, to have the //! MIR better match the source. -use rustc::middle::ty; +use rustc::middle::infer; use rustc::mir::repr::*; use rustc::mir::transform::MirPass; @@ -56,7 +56,8 @@ impl ClearDeadBlocks { } impl MirPass for ClearDeadBlocks { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, _tcx: &ty::ctxt<'tcx>) { + fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &infer::InferCtxt<'a, 'tcx>) + { self.clear_dead_blocks(mir); } } diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index b927ab489f100..a82d1fc53991f 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -16,18 +16,13 @@ use rustc::middle::ty; use rustc::mir::repr::*; use rustc::mir::visit::MutVisitor; use rustc::mir::mir_map::MirMap; -use rustc::mir::transform::MirPass; pub fn erase_regions<'tcx>(tcx: &ty::ctxt<'tcx>, mir_map: &mut MirMap<'tcx>) { - let mut eraser = EraseRegions; - for (_, mir) in &mut mir_map.map { - eraser.run_on_mir(mir, tcx); + EraseRegionsVisitor::new(tcx).visit_mir(mir); } } -pub struct EraseRegions; - struct EraseRegionsVisitor<'a, 'tcx: 'a> { tcx: &'a ty::ctxt<'tcx>, } @@ -58,12 +53,6 @@ impl<'a, 'tcx> EraseRegionsVisitor<'a, 'tcx> { } } -impl MirPass for EraseRegions { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ty::ctxt<'tcx>) { - EraseRegionsVisitor::new(tcx).visit_mir(mir); - } -} - impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> { fn visit_mir(&mut self, mir: &mut Mir<'tcx>) { self.erase_regions_return_ty(&mut mir.return_ty); diff --git a/src/librustc_mir/transform/no_landing_pads.rs b/src/librustc_mir/transform/no_landing_pads.rs index d0ea9f10f2e29..e2c93bd4e8751 100644 --- a/src/librustc_mir/transform/no_landing_pads.rs +++ b/src/librustc_mir/transform/no_landing_pads.rs @@ -11,7 +11,7 @@ //! This pass removes the unwind branch of all the terminators when the no-landing-pads option is //! specified. -use rustc::middle::ty; +use rustc::middle::infer; use rustc::mir::repr::*; use rustc::mir::visit::MutVisitor; use rustc::mir::transform::MirPass; @@ -41,8 +41,9 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads { } impl MirPass for NoLandingPads { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ty::ctxt<'tcx>) { - if tcx.sess.no_landing_pads() { + fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, + infcx: &infer::InferCtxt<'a, 'tcx>) { + if infcx.tcx.sess.no_landing_pads() { self.visit_mir(mir); } } diff --git a/src/librustc_mir/transform/simplify_cfg.rs b/src/librustc_mir/transform/simplify_cfg.rs index 17c5b5f7c3cff..16d12324202f3 100644 --- a/src/librustc_mir/transform/simplify_cfg.rs +++ b/src/librustc_mir/transform/simplify_cfg.rs @@ -9,6 +9,7 @@ // except according to those terms. use rustc::middle::const_eval::ConstVal; +use rustc::middle::infer; use rustc::mir::repr::*; use transform::util; use rustc::mir::transform::MirPass; @@ -119,7 +120,7 @@ impl SimplifyCfg { } impl MirPass for SimplifyCfg { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &::rustc::middle::ty::ctxt<'tcx>) { + fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &infer::InferCtxt<'a, 'tcx>) { let mut changed = true; while changed { changed = self.simplify_branches(mir); diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 010e327425384..4e94c76c7e85d 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -11,7 +11,7 @@ //! This pass type-checks the MIR to ensure it is not broken. #![allow(unreachable_code)] -use rustc::middle::infer; +use rustc::middle::infer::{self, InferCtxt}; use rustc::middle::traits; use rustc::middle::ty::{self, Ty}; use rustc::middle::ty::fold::TypeFoldable; @@ -35,7 +35,7 @@ macro_rules! span_mirbug { macro_rules! span_mirbug_and_err { ($context:expr, $elem:expr, $($message:tt)*) => ({ { - $context.tcx().sess.span_bug( + $context.tcx().sess.span_warn( $context.last_span, &format!("broken MIR ({:?}): {:?}", $elem, format!($($message)*)) ); @@ -50,14 +50,14 @@ enum FieldAccessError { /// Verifies that MIR types are sane to not crash further /// checks. -struct TypeVerifier<'a, 'tcx: 'a> { - infcx: &'a infer::InferCtxt<'a, 'tcx>, +struct TypeVerifier<'a, 'b: 'a, 'tcx: 'b> { + cx: &'a mut TypeChecker<'b, 'tcx>, mir: &'a Mir<'tcx>, last_span: Span, errors_reported: bool } -impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> { +impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> { fn visit_span(&mut self, span: &Span) { if *span != DUMMY_SP { self.last_span = *span; @@ -100,10 +100,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'tcx> { } } -impl<'a, 'tcx> TypeVerifier<'a, 'tcx> { - fn new(infcx: &'a infer::InferCtxt<'a, 'tcx>, mir: &'a Mir<'tcx>) -> Self { +impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { + fn new(cx: &'a mut TypeChecker<'b, 'tcx>, mir: &'a Mir<'tcx>) -> Self { TypeVerifier { - infcx: infcx, + cx: cx, mir: mir, last_span: mir.span, errors_reported: false @@ -111,7 +111,11 @@ impl<'a, 'tcx> TypeVerifier<'a, 'tcx> { } fn tcx(&self) -> &'a ty::ctxt<'tcx> { - self.infcx.tcx + self.cx.infcx.tcx + } + + fn infcx(&self) -> &'a InferCtxt<'a, 'tcx> { + self.cx.infcx } fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { @@ -163,6 +167,7 @@ impl<'a, 'tcx> TypeVerifier<'a, 'tcx> { debug!("sanitize_projection: {:?} {:?} {:?}", base, pi, lvalue); let tcx = self.tcx(); let base_ty = base.to_ty(tcx); + let span = self.last_span; match *pi { ProjectionElem::Deref => { let deref_ty = base_ty.builtin_deref(true, ty::LvaluePreference::NoPreference); @@ -227,7 +232,7 @@ impl<'a, 'tcx> TypeVerifier<'a, 'tcx> { let fty = self.sanitize_type(lvalue, fty); match self.field_ty(lvalue, base, field) { Ok(ty) => { - if let Err(terr) = infer::can_mk_subty(self.infcx, ty, fty) { + if let Err(terr) = self.cx.mk_eqty(span, ty, fty) { span_mirbug!( self, lvalue, "bad field access ({:?}: {:?}): {:?}", ty, fty, terr); @@ -282,14 +287,15 @@ impl<'a, 'tcx> TypeVerifier<'a, 'tcx> { }; if let Some(field) = variant.fields.get(field.index()) { - Ok(self.normalize(parent, field.ty(tcx, substs))) + Ok(self.normalize(field.ty(tcx, substs))) } else { Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() }) } } - fn normalize(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { - let mut selcx = traits::SelectionContext::new(&self.infcx); + fn normalize(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { + let infcx = self.infcx(); + let mut selcx = traits::SelectionContext::new(infcx); let cause = traits::ObligationCause::misc(self.last_span, 0); let traits::Normalized { value: ty, obligations } = traits::normalize(&mut selcx, cause, &ty); @@ -298,33 +304,44 @@ impl<'a, 'tcx> TypeVerifier<'a, 'tcx> { ty, obligations); - let mut fulfill_cx = self.infcx.fulfillment_cx.borrow_mut(); + let mut fulfill_cx = &mut self.cx.fulfillment_cx; for obligation in obligations { - fulfill_cx.register_predicate_obligation(&self.infcx, obligation); + fulfill_cx.register_predicate_obligation(infcx, obligation); } - match infer::drain_fulfillment_cx(&self.infcx, &mut fulfill_cx, &ty) { - Ok(ty) => ty, - Err(e) => { - span_mirbug_and_err!(self, parent, "trait fulfillment failed: {:?}", e) - } - } + ty } } -pub struct TypeckMir<'a, 'tcx: 'a> { - infcx: &'a infer::InferCtxt<'a, 'tcx>, +pub struct TypeChecker<'a, 'tcx: 'a> { + infcx: &'a InferCtxt<'a, 'tcx>, + fulfillment_cx: traits::FulfillmentContext<'tcx>, last_span: Span } -impl<'a, 'tcx> TypeckMir<'a, 'tcx> { - pub fn new(infcx: &'a infer::InferCtxt<'a, 'tcx>) -> Self { - TypeckMir { +impl<'a, 'tcx> TypeChecker<'a, 'tcx> { + fn new(infcx: &'a InferCtxt<'a, 'tcx>) -> Self { + TypeChecker { infcx: infcx, + fulfillment_cx: traits::FulfillmentContext::new(), last_span: DUMMY_SP } } + fn mk_subty(&self, span: Span, sup: Ty<'tcx>, sub: Ty<'tcx>) + -> infer::UnitResult<'tcx> + { + infer::mk_subty(self.infcx, false, infer::TypeOrigin::Misc(span), + sup, sub) + } + + fn mk_eqty(&self, span: Span, a: Ty<'tcx>, b: Ty<'tcx>) + -> infer::UnitResult<'tcx> + { + infer::mk_eqty(self.infcx, false, infer::TypeOrigin::Misc(span), + a, b) + } + fn tcx(&self) -> &'a ty::ctxt<'tcx> { self.infcx.tcx } @@ -337,7 +354,7 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { 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) { + if let Err(terr) = self.mk_subty(self.last_span, rv_ty, lv_ty) { span_mirbug!(self, stmt, "bad assignment ({:?} = {:?}): {:?}", lv_ty, rv_ty, terr); } @@ -358,7 +375,10 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { Terminator::Goto { .. } | Terminator::Resume | Terminator::Return | - Terminator::Drop { .. } => {} + Terminator::Drop { .. } => { + // no checks needed for these + } + Terminator::If { ref cond, .. } => { let cond_ty = mir.operand_ty(tcx, cond); match cond_ty.sty { @@ -370,15 +390,23 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { } 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) { + if let Err(terr) = self.mk_subty(self.last_span, discr_ty, switch_ty) { span_mirbug!(self, term, "bad SwitchInt ({:?} on {:?}): {:?}", switch_ty, discr_ty, terr); } + if !switch_ty.is_integral() && !switch_ty.is_char() && + !switch_ty.is_bool() + { + span_mirbug!(self, term, "bad SwitchInt discr ty {:?}",switch_ty); + } + // FIXME: check the values } - Terminator::Switch { ref discr, adt_def, .. } => { + Terminator::Switch { ref discr, adt_def, ref targets } => { let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx); match discr_ty.sty { - ty::TyEnum(def, _) if def == adt_def => {}, + ty::TyEnum(def, _) + if def == adt_def && adt_def.variants.len() == targets.len() + => {}, _ => { span_mirbug!(self, term, "bad Switch ({:?} on {:?})", adt_def, discr_ty); @@ -419,7 +447,7 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { } (&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) { + if let Err(terr) = self.mk_subty(self.last_span, ty, dest_ty) { span_mirbug!(self, term, "call dest mismatch ({:?} <- {:?}): {:?}", dest_ty, ty, terr); @@ -445,7 +473,7 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { } 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) { + if let Err(terr) = self.mk_subty(self.last_span, op_arg_ty, fn_arg) { span_mirbug!(self, term, "bad arg #{:?} ({:?} <- {:?}): {:?}", n, fn_arg, op_arg_ty, terr); } @@ -500,7 +528,7 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { } }; - if let Err(terr) = infer::can_mk_subty(self.infcx, arg_ty, pointee_ty) { + if let Err(terr) = self.mk_subty(self.last_span, arg_ty, pointee_ty) { span_mirbug!(self, term, "bad box_free arg ({:?} <- {:?}): {:?}", pointee_ty, arg_ty, terr); } @@ -522,28 +550,45 @@ impl<'a, 'tcx> TypeckMir<'a, 'tcx> { } } } + + fn verify_obligations(&mut self, mir: &Mir<'tcx>) { + self.last_span = mir.span; + if let Err(e) = self.fulfillment_cx.select_all_or_error(self.infcx) { + span_mirbug!(self, "", "errors selecting obligation: {:?}", + e); + } + } } -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) }; +pub struct TypeckMir; - if self.tcx().sess.err_count() > 0 { +impl TypeckMir { + pub fn new() -> Self { + TypeckMir + } +} + +impl MirPass for TypeckMir { + fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, infcx: &InferCtxt<'a, 'tcx>) + { + if infcx.tcx.sess.err_count() > 0 { // compiling a broken program can obviously result in a // broken MIR, so try not to report duplicate errors. return; } - let mut type_verifier = TypeVerifier::new(self.infcx, mir); - type_verifier.visit_mir(mir); + let mut checker = TypeChecker::new(infcx); - if type_verifier.errors_reported { - return; + { + let mut verifier = TypeVerifier::new(&mut checker, mir); + verifier.visit_mir(mir); + if verifier.errors_reported { + // don't do further checks to avoid ICEs + return; + } } - self.typeck_mir(mir); + checker.typeck_mir(mir); + checker.verify_obligations(mir); } } diff --git a/src/test/auxiliary/dummy_mir_pass.rs b/src/test/auxiliary/dummy_mir_pass.rs index df9ea4ab012e7..16ef965e0dbd5 100644 --- a/src/test/auxiliary/dummy_mir_pass.rs +++ b/src/test/auxiliary/dummy_mir_pass.rs @@ -21,17 +21,14 @@ extern crate syntax; use rustc::mir::transform::MirPass; use rustc::mir::repr::{Mir, Literal}; use rustc::mir::visit::MutVisitor; -use rustc::middle::ty; +use rustc::middle::infer::InferCtxt; use rustc::middle::const_eval::ConstVal; -use rustc::lint::{LateContext, LintContext, LintPass, LateLintPass, LateLintPassObject, LintArray}; use rustc_plugin::Registry; -use rustc_front::hir; -use syntax::attr; struct Pass; impl MirPass for Pass { - fn run_on_mir<'tcx>(&mut self, mir: &mut Mir<'tcx>, tcx: &ty::ctxt<'tcx>) { + fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &InferCtxt<'a, 'tcx>) { Visitor.visit_mir(mir) } } From d84658e317ac5b881a46e27204a1cb9f4ac1b691 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 13 Feb 2016 01:01:08 +0200 Subject: [PATCH 13/13] address review comments --- src/librustc/mir/mir_map.rs | 4 +++ src/librustc_mir/build/expr/as_rvalue.rs | 4 +++ .../transform/clear_dead_blocks.rs | 30 +++++++++++++++---- src/librustc_mir/transform/type_check.rs | 22 ++++++++------ .../run-pass/mir_augmented_assignments.rs | 6 +++- 5 files changed, 50 insertions(+), 16 deletions(-) diff --git a/src/librustc/mir/mir_map.rs b/src/librustc/mir/mir_map.rs index 82bd2caaac196..32e78b0467639 100644 --- a/src/librustc/mir/mir_map.rs +++ b/src/librustc/mir/mir_map.rs @@ -8,6 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use dep_graph::DepNode; use util::nodemap::NodeMap; use mir::repr::Mir; use mir::transform::MirPass; @@ -23,6 +24,9 @@ impl<'tcx> MirMap<'tcx> { if passes.is_empty() { return; } for (&id, mir) in &mut self.map { + let did = tcx.map.local_def_id(id); + let _task = tcx.dep_graph.in_task(DepNode::MirMapConstruction(did)); + let param_env = ty::ParameterEnvironment::for_item(tcx, id); let infcx = infer::new_infer_ctxt(tcx, &tcx.tables, Some(param_env)); diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 53b106d6d8679..2338d7df01a85 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -153,6 +153,10 @@ impl<'a,'tcx> Builder<'a,'tcx> { let fields = if let Some(FruInfo { base, field_types }) = base { let base = unpack!(block = this.as_lvalue(block, base)); + + // MIR does not natively support FRU, so for each + // base-supplied field, generate an operand that + // reads it from the base. field_names.into_iter() .zip(field_types.into_iter()) .map(|(n, ty)| match fields_map.get(&n) { diff --git a/src/librustc_mir/transform/clear_dead_blocks.rs b/src/librustc_mir/transform/clear_dead_blocks.rs index 2c08b6b0b81b8..b35d8c08f5dc1 100644 --- a/src/librustc_mir/transform/clear_dead_blocks.rs +++ b/src/librustc_mir/transform/clear_dead_blocks.rs @@ -8,12 +8,29 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! A pass that erases the contents of dead blocks. This is required -//! because rustc allows for ill-typed block terminators in dead -//! blocks. +//! A pass that erases the contents of dead blocks. This pass must +//! run before any analysis passes because some of the dead blocks +//! can be ill-typed. //! -//! This pass does not renumber or remove the blocks, to have the -//! MIR better match the source. +//! The main problem is that typeck lets most blocks whose end is not +//! reachable have an arbitrary return type, rather than having the +//! usual () return type (as a note, typeck's notion of reachability +//! is in fact slightly weaker than MIR CFG reachability - see #31617). +//! +//! A standard example of the situation is: +//! ```rust +//! fn example() { +//! let _a: char = { return; }; +//! } +//! ``` +//! +//! Here the block (`{ return; }`) has the return type `char`, +//! rather than `()`, but the MIR we naively generate still contains +//! the `_a = ()` write in the unreachable block "after" the return. +//! +//! As we have to run this pass even when we want to debug the MIR, +//! this pass just replaces the blocks with empty "return" blocks +//! and does not renumber anything. use rustc::middle::infer; use rustc::mir::repr::*; @@ -43,8 +60,9 @@ impl ClearDeadBlocks { } } - for (block, seen) in mir.basic_blocks.iter_mut().zip(seen) { + for (n, (block, seen)) in mir.basic_blocks.iter_mut().zip(seen).enumerate() { if !seen { + info!("clearing block #{}: {:?}", n, block); *block = BasicBlockData { statements: vec![], terminator: Some(Terminator::Return), diff --git a/src/librustc_mir/transform/type_check.rs b/src/librustc_mir/transform/type_check.rs index 4e94c76c7e85d..0e97e3629064b 100644 --- a/src/librustc_mir/transform/type_check.rs +++ b/src/librustc_mir/transform/type_check.rs @@ -48,8 +48,11 @@ enum FieldAccessError { OutOfRange { field_count: usize } } -/// Verifies that MIR types are sane to not crash further -/// checks. +/// Verifies that MIR types are sane to not crash further checks. +/// +/// The sanitize_XYZ methods here take an MIR object and compute its +/// type, calling `span_mirbug` and returning an error type if there +/// is a problem. struct TypeVerifier<'a, 'b: 'a, 'tcx: 'b> { cx: &'a mut TypeChecker<'b, 'tcx>, mir: &'a Mir<'tcx>, @@ -119,11 +122,11 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } fn sanitize_type(&mut self, parent: &fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> { - if !(ty.needs_infer() || ty.has_escaping_regions() || - ty.references_error()) { - return ty; + if ty.needs_infer() || ty.has_escaping_regions() || ty.references_error() { + span_mirbug_and_err!(self, parent, "bad type {:?}", ty) + } else { + ty } - span_mirbug_and_err!(self, parent, "bad type {:?}", ty) } fn sanitize_lvalue(&mut self, lvalue: &Lvalue<'tcx>) -> LvalueTy<'tcx> { @@ -225,7 +228,8 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> { } _ => LvalueTy::Ty { ty: span_mirbug_and_err!( - self, lvalue, "can't downcast {:?}", base_ty) + self, lvalue, "can't downcast {:?} as {:?}", + base_ty, adt_def1) } }, ProjectionElem::Field(field, fty) => { @@ -467,8 +471,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { args: &[Operand<'tcx>]) { debug!("check_call_inputs({:?}, {:?})", sig, args); - if sig.inputs.len() > args.len() || - (sig.inputs.len() < args.len() && !sig.variadic) { + if args.len() < sig.inputs.len() || + (args.len() > sig.inputs.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() { diff --git a/src/test/run-pass/mir_augmented_assignments.rs b/src/test/run-pass/mir_augmented_assignments.rs index cadfce367a426..c85ac458edd88 100644 --- a/src/test/run-pass/mir_augmented_assignments.rs +++ b/src/test/run-pass/mir_augmented_assignments.rs @@ -77,7 +77,10 @@ fn main_mir() { assert_eq!(x, Int(0)); // indexed LHS - let mut v = vec![Int(1), Int(2)]; + // FIXME(mir-drop): use the vec![..] macro + let mut v = Vec::new(); + v.push(Int(1)); + v.push(Int(2)); v[0] += Int(2); assert_eq!(v[0], Int(3)); @@ -87,6 +90,7 @@ fn main_mir() { assert_eq!(array[0], 1); assert_eq!(array[1], 2); assert_eq!(array[2], 3); + } impl AddAssign for Int {