From 6f633ef128335e11e9903f647c6a6150f3a714d1 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 11 Feb 2016 18:30:34 +0200 Subject: tuple arguments to overloaded calls also fix translation of "rust-call" functions, although that could use more optimizations --- src/test/run-pass/mir_trans_calls.rs | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) (limited to 'src/test') diff --git a/src/test/run-pass/mir_trans_calls.rs b/src/test/run-pass/mir_trans_calls.rs index fc45fbf7278..b8b7ecbf03c 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); } -- cgit 1.4.1-3-g733a5 From 350b50df0035b5bb0520d5a84adfc117c5ce9752 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 11 Feb 2016 18:31:31 +0200 Subject: 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 (limited to 'src/test') diff --git a/src/librustc_mir/hair/cx/expr.rs b/src/librustc_mir/hair/cx/expr.rs index 324c1e92efc..4a91f2b66a1 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 00000000000..81712e4569f --- /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 _); +} -- cgit 1.4.1-3-g733a5 From 3c6f41026bea2a99ab09d15f3dbed838af1ebcac Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 11 Feb 2016 18:31:42 +0200 Subject: 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 (limited to 'src/test') diff --git a/src/librustc/middle/ty/context.rs b/src/librustc/middle/ty/context.rs index 1d071cd604d..ea6b4df8104 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 2f24a1a8962..ce7b1ceb355 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 e8b83962b52..20e083f840f 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 4e03ed489eb..b2c7507ed7b 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 dea2d750b98..53b106d6d86 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 ec67429379f..02f32da2b83 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 dbb00a13cd3..c295ed168ba 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 4a91f2b66a1..facc2541652 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 c5b34d92466..6f4375d53ec 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 0891c2845b0..e8edd4067e2 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 f4d83cd05e0..8f4d452df51 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 002584f51c6..826fb025bc1 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 3768a2e3033..7ab4975c8b8 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 51353239790..9b8b6dedb63 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 00000000000..1f75369b94a --- /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); +} -- cgit 1.4.1-3-g733a5 From 880b6c260afc13df012d81c52a5fc1cc4348976e Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Thu, 11 Feb 2016 15:38:52 +0200 Subject: 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 ++ src/test/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 (limited to 'src/test') diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 6eede3070b2..b446dec96fb 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 facc2541652..ac1cff527fe 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 8f4d452df51..b2c7478ead5 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 00000000000..cadfce367a4 --- /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; + } + } +} -- cgit 1.4.1-3-g733a5 From 881249aa463db43e41e4d5f98d6f71f2970f8965 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 13 Feb 2016 00:23:16 +0200 Subject: 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 +- src/librustc_mir/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(-) (limited to 'src/test') diff --git a/src/librustc/mir/mir_map.rs b/src/librustc/mir/mir_map.rs index aa24f6d8979..82bd2caaac1 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 0f18d8d2821..cc417f5a99e 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 68d85dc8394..70c33540121 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 88c0116f26d..2c08b6b0b81 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 b927ab489f1..a82d1fc5399 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 d0ea9f10f2e..e2c93bd4e87 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 17c5b5f7c3c..16d12324202 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 010e3274253..4e94c76c7e8 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 df9ea4ab012..16ef965e0db 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) } } -- cgit 1.4.1-3-g733a5 From d84658e317ac5b881a46e27204a1cb9f4ac1b691 Mon Sep 17 00:00:00 2001 From: Ariel Ben-Yehuda Date: Sat, 13 Feb 2016 01:01:08 +0200 Subject: address review comments --- src/librustc/mir/mir_map.rs | 4 ++++ src/librustc_mir/build/expr/as_rvalue.rs | 4 ++++ src/librustc_mir/transform/clear_dead_blocks.rs | 30 ++++++++++++++++++++----- src/librustc_mir/transform/type_check.rs | 22 ++++++++++-------- src/test/run-pass/mir_augmented_assignments.rs | 6 ++++- 5 files changed, 50 insertions(+), 16 deletions(-) (limited to 'src/test') diff --git a/src/librustc/mir/mir_map.rs b/src/librustc/mir/mir_map.rs index 82bd2caaac1..32e78b04676 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 53b106d6d86..2338d7df01a 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 2c08b6b0b81..b35d8c08f5d 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 4e94c76c7e8..0e97e362906 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 cadfce367a4..c85ac458edd 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 { -- cgit 1.4.1-3-g733a5