Auto merge of #31474 - arielb1:mir-typeck, r=nikomatsakis

This should stop broken MIR from annoying us when we try to implement things
This commit is contained in:
bors 2016-02-20 12:38:18 +00:00
commit 6c751e0456
43 changed files with 1509 additions and 190 deletions

View File

@ -721,10 +721,11 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
if let Some(adjustment) = adj { if let Some(adjustment) = adj {
match adjustment { match adjustment {
adjustment::AdjustReifyFnPointer | adjustment::AdjustReifyFnPointer |
adjustment::AdjustUnsafeFnPointer => { adjustment::AdjustUnsafeFnPointer |
adjustment::AdjustMutToConstPointer => {
// Creating a closure/fn-pointer or unsizing consumes // Creating a closure/fn-pointer or unsizing consumes
// the input and stores it into the resulting rvalue. // the input and stores it into the resulting rvalue.
debug!("walk_adjustment(AdjustReifyFnPointer|AdjustUnsafeFnPointer)"); debug!("walk_adjustment: trivial adjustment");
let cmt_unadjusted = let cmt_unadjusted =
return_if_err!(self.mc.cat_expr_unadjusted(expr)); return_if_err!(self.mc.cat_expr_unadjusted(expr));
self.delegate_consume(expr.id, expr.span, cmt_unadjusted); self.delegate_consume(expr.id, expr.span, cmt_unadjusted);

View File

@ -430,6 +430,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
adjustment::AdjustReifyFnPointer | adjustment::AdjustReifyFnPointer |
adjustment::AdjustUnsafeFnPointer | adjustment::AdjustUnsafeFnPointer |
adjustment::AdjustMutToConstPointer |
adjustment::AdjustDerefRef(_) => { adjustment::AdjustDerefRef(_) => {
debug!("cat_expr({:?}): {:?}", debug!("cat_expr({:?}): {:?}",
adjustment, adjustment,

View File

@ -23,6 +23,7 @@ use rustc_front::hir;
pub enum AutoAdjustment<'tcx> { pub enum AutoAdjustment<'tcx> {
AdjustReifyFnPointer, // go from a fn-item type to a fn-pointer type AdjustReifyFnPointer, // go from a fn-item type to a fn-pointer type
AdjustUnsafeFnPointer, // go from a safe fn pointer to an unsafe fn pointer 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>), AdjustDerefRef(AutoDerefRef<'tcx>),
} }
@ -106,7 +107,8 @@ impl<'tcx> AutoAdjustment<'tcx> {
pub fn is_identity(&self) -> bool { pub fn is_identity(&self) -> bool {
match *self { match *self {
AdjustReifyFnPointer | AdjustReifyFnPointer |
AdjustUnsafeFnPointer => false, AdjustUnsafeFnPointer |
AdjustMutToConstPointer => false,
AdjustDerefRef(ref r) => r.is_identity(), AdjustDerefRef(ref r) => r.is_identity(),
} }
} }
@ -169,7 +171,22 @@ impl<'tcx> ty::TyS<'tcx> {
ty::TyBareFn(None, b) => cx.safe_to_unsafe_fn_ty(b), ty::TyBareFn(None, b) => cx.safe_to_unsafe_fn_ty(b),
ref b => { ref b => {
cx.sess.bug( 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)); b));
} }

View File

@ -131,6 +131,12 @@ pub struct Tables<'tcx> {
/// equivalents. This table is not used in trans (since regions /// equivalents. This table is not used in trans (since regions
/// are erased there) and hence is not serialized to metadata. /// are erased there) and hence is not serialized to metadata.
pub liberated_fn_sigs: NodeMap<ty::FnSig<'tcx>>, pub liberated_fn_sigs: NodeMap<ty::FnSig<'tcx>>,
/// 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<Vec<Ty<'tcx>>>
} }
impl<'tcx> Tables<'tcx> { impl<'tcx> Tables<'tcx> {
@ -144,6 +150,7 @@ impl<'tcx> Tables<'tcx> {
closure_tys: DefIdMap(), closure_tys: DefIdMap(),
closure_kinds: DefIdMap(), closure_kinds: DefIdMap(),
liberated_fn_sigs: NodeMap(), liberated_fn_sigs: NodeMap(),
fru_field_types: NodeMap()
} }
} }

View File

@ -8,10 +8,12 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use dep_graph::DepNode;
use util::nodemap::NodeMap; use util::nodemap::NodeMap;
use mir::repr::Mir; use mir::repr::Mir;
use mir::transform::MirPass; use mir::transform::MirPass;
use middle::ty; use middle::ty;
use middle::infer;
pub struct MirMap<'tcx> { pub struct MirMap<'tcx> {
pub map: NodeMap<Mir<'tcx>>, pub map: NodeMap<Mir<'tcx>>,
@ -19,9 +21,17 @@ pub struct MirMap<'tcx> {
impl<'tcx> MirMap<'tcx> { impl<'tcx> MirMap<'tcx> {
pub fn run_passes(&mut self, passes: &mut [Box<MirPass>], tcx: &ty::ctxt<'tcx>) { pub fn run_passes(&mut self, passes: &mut [Box<MirPass>], 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 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));
for pass in &mut *passes { for pass in &mut *passes {
pass.run_on_mir(mir, tcx) pass.run_on_mir(mir, &infcx)
} }
} }
} }

View File

@ -45,6 +45,9 @@ pub struct Mir<'tcx> {
/// values in that it is possible to borrow them and mutate them /// values in that it is possible to borrow them and mutate them
/// through the resulting reference. /// through the resulting reference.
pub temp_decls: Vec<TempDecl<'tcx>>, pub temp_decls: Vec<TempDecl<'tcx>>,
/// A span representing this MIR, for error reporting
pub span: Span,
} }
/// where execution begins /// where execution begins
@ -145,7 +148,7 @@ pub enum BorrowKind {
/// A "variable" is a binding declared by the user as part of the fn /// A "variable" is a binding declared by the user as part of the fn
/// decl, a let, etc. /// decl, a let, etc.
#[derive(Clone, RustcEncodable, RustcDecodable)] #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct VarDecl<'tcx> { pub struct VarDecl<'tcx> {
pub mutability: Mutability, pub mutability: Mutability,
pub name: Name, pub name: Name,
@ -154,7 +157,7 @@ pub struct VarDecl<'tcx> {
/// A "temp" is a temporary that we place on the stack. They are /// A "temp" is a temporary that we place on the stack. They are
/// anonymous, always mutable, and have only a type. /// anonymous, always mutable, and have only a type.
#[derive(Clone, RustcEncodable, RustcDecodable)] #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct TempDecl<'tcx> { pub struct TempDecl<'tcx> {
pub ty: Ty<'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 /// there is only one argument, of type `(i32, u32)`, but two bindings
/// (`x` and `y`). /// (`x` and `y`).
#[derive(Clone, RustcEncodable, RustcDecodable)] #[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
pub struct ArgDecl<'tcx> { pub struct ArgDecl<'tcx> {
pub ty: Ty<'tcx>, pub ty: Ty<'tcx>,
} }
@ -499,7 +502,7 @@ pub struct Projection<'tcx, B, V> {
#[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)] #[derive(Clone, Debug, PartialEq, RustcEncodable, RustcDecodable)]
pub enum ProjectionElem<'tcx, V> { pub enum ProjectionElem<'tcx, V> {
Deref, Deref,
Field(Field), Field(Field, Ty<'tcx>),
Index(V), Index(V),
/// These indices are generated by slice patterns. Easiest to explain /// These indices are generated by slice patterns. Easiest to explain
@ -550,8 +553,8 @@ impl Field {
} }
impl<'tcx> Lvalue<'tcx> { impl<'tcx> Lvalue<'tcx> {
pub fn field(self, f: Field) -> Lvalue<'tcx> { pub fn field(self, f: Field, ty: Ty<'tcx>) -> Lvalue<'tcx> {
self.elem(ProjectionElem::Field(f)) self.elem(ProjectionElem::Field(f, ty))
} }
pub fn deref(self) -> Lvalue<'tcx> { pub fn deref(self) -> Lvalue<'tcx> {
@ -591,8 +594,8 @@ impl<'tcx> Debug for Lvalue<'tcx> {
write!(fmt, "({:?} as {})", data.base, adt_def.variants[index].name), write!(fmt, "({:?} as {})", data.base, adt_def.variants[index].name),
ProjectionElem::Deref => ProjectionElem::Deref =>
write!(fmt, "(*{:?})", data.base), write!(fmt, "(*{:?})", data.base),
ProjectionElem::Field(field) => ProjectionElem::Field(field, ty) =>
write!(fmt, "{:?}.{:?}", data.base, field.index()), write!(fmt, "({:?}.{:?}: {:?})", data.base, field.index(), ty),
ProjectionElem::Index(ref index) => ProjectionElem::Index(ref index) =>
write!(fmt, "{:?}[{:?}]", data.base, index), write!(fmt, "{:?}[{:?}]", data.base, index),
ProjectionElem::ConstantIndex { offset, min_length, from_end: false } => ProjectionElem::ConstantIndex { offset, min_length, from_end: false } =>

View File

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

View File

@ -9,8 +9,8 @@
// except according to those terms. // except according to those terms.
use mir::repr::Mir; use mir::repr::Mir;
use middle::ty::ctxt; use middle::infer::InferCtxt;
pub trait MirPass { 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>);
} }

View File

@ -138,6 +138,7 @@ pub struct Options {
pub no_trans: bool, pub no_trans: bool,
pub error_format: ErrorOutputType, pub error_format: ErrorOutputType,
pub treat_err_as_bug: bool, pub treat_err_as_bug: bool,
pub mir_opt_level: usize,
/// if true, build up the dep-graph /// if true, build up the dep-graph
pub build_dep_graph: bool, pub build_dep_graph: bool,
@ -254,6 +255,7 @@ pub fn basic_options() -> Options {
parse_only: false, parse_only: false,
no_trans: false, no_trans: false,
treat_err_as_bug: false, treat_err_as_bug: false,
mir_opt_level: 1,
build_dep_graph: false, build_dep_graph: false,
dump_dep_graph: false, dump_dep_graph: false,
no_analysis: false, no_analysis: false,
@ -655,6 +657,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"show spans for compiler debugging (expr|pat|ty)"), "show spans for compiler debugging (expr|pat|ty)"),
print_trans_items: Option<String> = (None, parse_opt_string, print_trans_items: Option<String> = (None, parse_opt_string,
"print the result of the translation item collection pass"), "print the result of the translation item collection pass"),
mir_opt_level: Option<usize> = (None, parse_opt_uint,
"set the MIR optimization level (0-3)"),
} }
pub fn default_lib_output() -> CrateType { 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 parse_only = debugging_opts.parse_only;
let no_trans = debugging_opts.no_trans; let no_trans = debugging_opts.no_trans;
let treat_err_as_bug = debugging_opts.treat_err_as_bug; 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 incremental_compilation = debugging_opts.incr_comp;
let dump_dep_graph = debugging_opts.dump_dep_graph; let dump_dep_graph = debugging_opts.dump_dep_graph;
let no_analysis = debugging_opts.no_analysis; let no_analysis = debugging_opts.no_analysis;
@ -1166,6 +1171,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
parse_only: parse_only, parse_only: parse_only,
no_trans: no_trans, no_trans: no_trans,
treat_err_as_bug: treat_err_as_bug, treat_err_as_bug: treat_err_as_bug,
mir_opt_level: mir_opt_level,
build_dep_graph: incremental_compilation || dump_dep_graph, build_dep_graph: incremental_compilation || dump_dep_graph,
dump_dep_graph: dump_dep_graph, dump_dep_graph: dump_dep_graph,
no_analysis: no_analysis, no_analysis: no_analysis,

View File

@ -396,6 +396,9 @@ impl<'tcx> fmt::Debug for ty::adjustment::AutoAdjustment<'tcx> {
ty::adjustment::AdjustUnsafeFnPointer => { ty::adjustment::AdjustUnsafeFnPointer => {
write!(f, "AdjustUnsafeFnPointer") write!(f, "AdjustUnsafeFnPointer")
} }
ty::adjustment::AdjustMutToConstPointer => {
write!(f, "AdjustMutToConstPointer")
}
ty::adjustment::AdjustDerefRef(ref data) => { ty::adjustment::AdjustDerefRef(ref data) => {
write!(f, "{:?}", data) write!(f, "{:?}", data)
} }

View File

@ -844,6 +844,18 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
"match checking", "match checking",
|| middle::check_match::check_crate(tcx)); || 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 = let mut mir_map =
time(time_passes, time(time_passes,
"MIR dump", "MIR dump",
@ -853,18 +865,10 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
"MIR passes", "MIR passes",
|| mir_map.run_passes(&mut sess.plugin_mir_passes.borrow_mut(), tcx)); || 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, time(time_passes,
"borrow checking", "borrow checking",
|| borrowck::check_crate(tcx)); || borrowck::check_crate(tcx));
time(time_passes,
"rvalue checking",
|| rvalues::check_crate(tcx));
// Avoid overwhelming user with errors if type checking failed. // Avoid overwhelming user with errors if type checking failed.
// I'm not sure how helpful this is, to be honest, but it avoids // I'm not sure how helpful this is, to be honest, but it avoids
// a // a

View File

@ -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) => { 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.emit_enum_variant_arg(0,
|this| Ok(this.emit_auto_deref_ref(ecx, auto_deref_ref))) |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>) fn read_auto_adjustment<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>)
-> adjustment::AutoAdjustment<'tcx> { -> adjustment::AutoAdjustment<'tcx> {
self.read_enum("AutoAdjustment", |this| { self.read_enum("AutoAdjustment", |this| {
let variants = ["AdjustReifyFnPointer", "AdjustUnsafeFnPointer", "AdjustDerefRef"]; let variants = ["AdjustReifyFnPointer", "AdjustUnsafeFnPointer",
"AdjustMutToConstPointer", "AdjustDerefRef"];
this.read_enum_variant(&variants, |this, i| { this.read_enum_variant(&variants, |this, i| {
Ok(match i { Ok(match i {
1 => adjustment::AdjustReifyFnPointer, 1 => adjustment::AdjustReifyFnPointer,
2 => adjustment::AdjustUnsafeFnPointer, 2 => adjustment::AdjustUnsafeFnPointer,
3 => { 3 => adjustment::AdjustMutToConstPointer,
4 => {
let auto_deref_ref: adjustment::AutoDerefRef = let auto_deref_ref: adjustment::AutoDerefRef =
this.read_enum_variant_arg(0, this.read_enum_variant_arg(0,
|this| Ok(this.read_auto_deref_ref(dcx))).unwrap(); |this| Ok(this.read_auto_deref_ref(dcx))).unwrap();

View File

@ -41,7 +41,7 @@ impl<'a,'tcx> Builder<'a,'tcx> {
} }
ExprKind::Field { lhs, name } => { ExprKind::Field { lhs, name } => {
let lvalue = unpack!(block = this.as_lvalue(block, lhs)); let lvalue = unpack!(block = this.as_lvalue(block, lhs));
let lvalue = lvalue.field(name); let lvalue = lvalue.field(name, expr.ty);
block.and(lvalue) block.and(lvalue)
} }
ExprKind::Deref { arg } => { ExprKind::Deref { arg } => {

View File

@ -139,7 +139,9 @@ impl<'a,'tcx> Builder<'a,'tcx> {
.collect(); .collect();
block.and(Rvalue::Aggregate(AggregateKind::Closure(closure_id, substs), upvars)) 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 // first process the set of fields that were provided
// (evaluating them in order given by user) // (evaluating them in order given by user)
let fields_map: FnvHashMap<_, _> = let fields_map: FnvHashMap<_, _> =
@ -147,25 +149,24 @@ impl<'a,'tcx> Builder<'a,'tcx> {
.map(|f| (f.name, unpack!(block = this.as_operand(block, f.expr)))) .map(|f| (f.name, unpack!(block = this.as_operand(block, f.expr))))
.collect(); .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); let field_names = this.hir.all_fields(adt_def, variant_index);
// for the actual values we use, take either the let fields = if let Some(FruInfo { base, field_types }) = base {
// expr the user specified or, if they didn't let base = unpack!(block = this.as_lvalue(block, base));
// specify something for this field name, create a
// path relative to the base (which must have been // MIR does not natively support FRU, so for each
// supplied, or the IR is internally // base-supplied field, generate an operand that
// inconsistent). // reads it from the base.
let fields: Vec<_> =
field_names.into_iter() field_names.into_iter()
.map(|n| match fields_map.get(&n) { .zip(field_types.into_iter())
.map(|(n, ty)| match fields_map.get(&n) {
Some(v) => v.clone(), Some(v) => v.clone(),
None => Operand::Consume(base.clone().unwrap().field(n)), None => Operand::Consume(base.clone().field(n, ty))
}) })
.collect(); .collect()
} else {
field_names.iter().map(|n| fields_map[n].clone()).collect()
};
block.and(Rvalue::Aggregate(AggregateKind::Adt(adt_def, variant_index, substs), block.and(Rvalue::Aggregate(AggregateKind::Adt(adt_def, variant_index, substs),
fields)) fields))

View File

@ -404,7 +404,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
subpatterns.iter() subpatterns.iter()
.map(|subpattern| { .map(|subpattern| {
// e.g., `(x as Variant).0` // 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` // e.g., `(x as Variant).0 @ P1`
MatchPair::new(lvalue, &subpattern.pattern) MatchPair::new(lvalue, &subpattern.pattern)
}); });

View File

@ -21,7 +21,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
-> Vec<MatchPair<'pat, 'tcx>> { -> Vec<MatchPair<'pat, 'tcx>> {
subpatterns.iter() subpatterns.iter()
.map(|fieldpat| { .map(|fieldpat| {
let lvalue = lvalue.clone().field(fieldpat.field); let lvalue = lvalue.clone().field(fieldpat.field,
fieldpat.field_ty());
MatchPair::new(lvalue, &fieldpat.pattern) MatchPair::new(lvalue, &fieldpat.pattern)
}) })
.collect() .collect()

View File

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

View File

@ -418,25 +418,28 @@ impl<'a,'tcx> Builder<'a,'tcx> {
len: Operand<'tcx>, len: Operand<'tcx>,
span: Span) { span: Span) {
// fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> ! // fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> !
let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem); let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem);
let args = func.ty.fn_args(); let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0;
let ref_ty = args.skip_binder()[0];
let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty { let ref_ty = args[0];
(region, tyandmut.ty) let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty {
tyandmut.ty
} else { } else {
self.hir.span_bug(span, &format!("unexpected panic_bound_check type: {:?}", func.ty)); 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 (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
let (file, line) = self.span_to_fileline_args(span); let (file, line) = self.span_to_fileline_args(span);
let elems = vec![Operand::Constant(file), Operand::Constant(line)]; 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 // 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 // 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. // 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)); Rvalue::Aggregate(AggregateKind::Tuple, elems));
// FIXME: is this region really correct here? // FIXME: is this region really correct here?
self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple; self.cfg.push_assign(block, span, &tuple_ref, // tuple_ref = &tuple;
Rvalue::Ref(*region, BorrowKind::Unique, tuple)); Rvalue::Ref(region, BorrowKind::Shared, tuple));
let cleanup = self.diverge_cleanup(); let cleanup = self.diverge_cleanup();
self.cfg.terminate(block, Terminator::Call { self.cfg.terminate(block, Terminator::Call {
func: Operand::Constant(func), func: Operand::Constant(func),
@ -449,18 +452,21 @@ impl<'a,'tcx> Builder<'a,'tcx> {
/// Create diverge cleanup and branch to it from `block`. /// Create diverge cleanup and branch to it from `block`.
pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) { pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) {
// fn(&(msg: &'static str filename: &'static str, line: u32)) -> ! // fn(&(msg: &'static str filename: &'static str, line: u32)) -> !
let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
let func = self.lang_function(lang_items::PanicFnLangItem); let func = self.lang_function(lang_items::PanicFnLangItem);
let args = func.ty.fn_args(); let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0;
let ref_ty = args.skip_binder()[0];
let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty { let ref_ty = args[0];
(region, tyandmut.ty) let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty {
tyandmut.ty
} else { } else {
self.hir.span_bug(span, &format!("unexpected panic type: {:?}", func.ty)); self.hir.span_bug(span, &format!("unexpected panic type: {:?}", func.ty));
}; };
let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty)); let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
let (file, line) = self.span_to_fileline_args(span); let (file, line) = self.span_to_fileline_args(span);
let message = Constant { let message = Constant {
span: DUMMY_SP, span: span,
ty: self.hir.tcx().mk_static_str(), ty: self.hir.tcx().mk_static_str(),
literal: self.hir.str_literal(intern_and_get_ident(msg)) 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 // 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 // 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. // 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)); Rvalue::Aggregate(AggregateKind::Tuple, elems));
// FIXME: is this region really correct here? // FIXME: is this region really correct here?
self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple; self.cfg.push_assign(block, span, &tuple_ref, // tuple_ref = &tuple;
Rvalue::Ref(*region, BorrowKind::Unique, tuple)); Rvalue::Ref(region, BorrowKind::Shared, tuple));
let cleanup = self.diverge_cleanup(); let cleanup = self.diverge_cleanup();
self.cfg.terminate(block, Terminator::Call { self.cfg.terminate(block, Terminator::Call {
func: Operand::Constant(func), 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>) { 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); let span_lines = self.hir.tcx().sess.codemap().lookup_char_pos(span.lo);
(Constant { (Constant {
span: DUMMY_SP, span: span,
ty: self.hir.tcx().mk_static_str(), ty: self.hir.tcx().mk_static_str(),
literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name)) literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name))
}, Constant { }, Constant {
span: DUMMY_SP, span: span,
ty: self.hir.tcx().types.u32, ty: self.hir.tcx().types.u32,
literal: self.hir.usize_literal(span_lines.line) literal: self.hir.usize_literal(span_lines.line)
}) })

View File

@ -32,6 +32,8 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
debug!("Expr::make_mirror(): id={}, span={:?}", self.id, self.span); debug!("Expr::make_mirror(): id={}, span={:?}", self.id, self.span);
let expr_ty = cx.tcx.expr_ty(self); // note: no adjustments (yet)! 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 { let kind = match self.node {
// Here comes the interesting stuff: // Here comes the interesting stuff:
@ -54,14 +56,35 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
// Find the actual method implementation being called and // Find the actual method implementation being called and
// build the appropriate UFCS call expression with the // build the appropriate UFCS call expression with the
// callee-object as self parameter. // 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 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: temp_lifetime,
span: self.span,
kind: ExprKind::Tuple {
fields: args.iter().map(ToRef::to_ref).collect()
}
};
ExprKind::Call { ExprKind::Call {
ty: method.ty, ty: method.ty,
fun: method.to_ref(), fun: method.to_ref(),
args: argrefs, args: vec![fun.to_ref(), tupled_args.to_ref()]
} }
} else { } else {
let adt_data = if let hir::ExprPath(..) = fun.node { let adt_data = if let hir::ExprPath(..) = fun.node {
@ -125,13 +148,22 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
} }
hir::ExprAssignOp(op, ref lhs, ref rhs) => { hir::ExprAssignOp(op, ref lhs, ref rhs) => {
let op = bin_op(op.node); 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 { ExprKind::AssignOp {
op: op, op: bin_op(op.node),
lhs: lhs.to_ref(), lhs: lhs.to_ref(),
rhs: rhs.to_ref(), rhs: rhs.to_ref(),
} }
} }
}
hir::ExprLit(..) => ExprKind::Literal { hir::ExprLit(..) => ExprKind::Literal {
literal: cx.const_eval_literal(self) literal: cx.const_eval_literal(self)
@ -227,13 +259,23 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
variant_index: 0, variant_index: 0,
substs: substs, substs: substs,
fields: field_refs, fields: field_refs,
base: base.as_ref().map(|base| {
FruInfo {
base: base.to_ref(), base: base.to_ref(),
field_types: cx.tcx.tables
.borrow()
.fru_field_types[&self.id]
.clone()
}
})
} }
} }
ty::TyEnum(adt, substs) => { ty::TyEnum(adt, substs) => {
match cx.tcx.def_map.borrow()[&self.id].full_def() { match cx.tcx.def_map.borrow()[&self.id].full_def() {
Def::Variant(enum_id, variant_id) => { Def::Variant(enum_id, variant_id) => {
debug_assert!(adt.did == enum_id); debug_assert!(adt.did == enum_id);
assert!(base.is_none());
let index = adt.variant_index_with_id(variant_id); let index = adt.variant_index_with_id(variant_id);
let field_refs = field_refs(&adt.variants[index], fields); let field_refs = field_refs(&adt.variants[index], fields);
ExprKind::Adt { ExprKind::Adt {
@ -241,7 +283,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
variant_index: index, variant_index: index,
substs: substs, substs: substs,
fields: field_refs, fields: field_refs,
base: base.to_ref(), base: None
} }
} }
ref def => { ref def => {
@ -385,9 +427,6 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
ExprKind::Tuple { fields: fields.to_ref() }, 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 { let mut expr = Expr {
temp_lifetime: temp_lifetime, temp_lifetime: temp_lifetime,
ty: expr_ty, ty: expr_ty,
@ -395,6 +434,9 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
kind: kind, kind: kind,
}; };
debug!("make_mirror: unadjusted-expr={:?} applying adjustments={:?}",
expr, cx.tcx.tables.borrow().adjustments.get(&self.id));
// Now apply adjustments, if any. // Now apply adjustments, if any.
match cx.tcx.tables.borrow().adjustments.get(&self.id) { match cx.tcx.tables.borrow().adjustments.get(&self.id) {
None => {} None => {}
@ -416,6 +458,15 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
kind: ExprKind::UnsafeFnPointer { source: expr.to_ref() }, 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)) => { Some(&ty::adjustment::AdjustDerefRef(ref adj)) => {
for i in 0..adj.autoderefs { for i in 0..adj.autoderefs {
let i = i as u32; let i = i as u32;
@ -426,10 +477,38 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
self.span, self.span,
i, i,
|mc| cx.tcx.tables.borrow().method_map.get(&mc).map(|m| m.ty)); |mc| cx.tcx.tables.borrow().method_map.get(&mc).map(|m| m.ty));
let kind = if cx.tcx.is_overloaded_autoderef(self.id, i) { debug!("make_mirror: autoderef #{}, adjusted_ty={:?}", i, adjusted_ty);
overloaded_lvalue(cx, self, ty::MethodCall::autoderef(self.id, i), let method_key = ty::MethodCall::autoderef(self.id, i);
PassArgs::ByValue, expr.to_ref(), vec![]) 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 { } else {
debug!("make_mirror: built-in autoderef");
ExprKind::Deref { arg: expr.to_ref() } ExprKind::Deref { arg: expr.to_ref() }
}; };
expr = Expr { expr = Expr {
@ -749,11 +828,16 @@ fn convert_var<'a, 'tcx: 'a>(cx: &mut Cx<'a, 'tcx>,
}; };
match upvar_capture { match upvar_capture {
ty::UpvarCapture::ByValue => field_kind, ty::UpvarCapture::ByValue => field_kind,
ty::UpvarCapture::ByRef(_) => { ty::UpvarCapture::ByRef(borrow) => {
ExprKind::Deref { ExprKind::Deref {
arg: Expr { arg: Expr {
temp_lifetime: temp_lifetime, temp_lifetime: temp_lifetime,
ty: cx.tcx.mk_ref(
cx.tcx.mk_region(borrow.region),
ty::TypeAndMut {
ty: var_ty, ty: var_ty,
mutbl: borrow.kind.to_mutbl_lossy()
}),
span: expr.span, span: expr.span,
kind: field_kind, kind: field_kind,
}.to_ref() }.to_ref()

View File

@ -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
}
}

View File

@ -229,7 +229,7 @@ pub enum ExprKind<'tcx> {
variant_index: usize, variant_index: usize,
substs: &'tcx Substs<'tcx>, substs: &'tcx Substs<'tcx>,
fields: Vec<FieldExprRef<'tcx>>, fields: Vec<FieldExprRef<'tcx>>,
base: Option<ExprRef<'tcx>>, base: Option<FruInfo<'tcx>>
}, },
Closure { Closure {
closure_id: DefId, closure_id: DefId,
@ -256,6 +256,12 @@ pub struct FieldExprRef<'tcx> {
pub expr: ExprRef<'tcx>, pub expr: ExprRef<'tcx>,
} }
#[derive(Clone, Debug)]
pub struct FruInfo<'tcx> {
pub base: ExprRef<'tcx>,
pub field_types: Vec<Ty<'tcx>>
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Arm<'tcx> { pub struct Arm<'tcx> {
pub patterns: Vec<Pattern<'tcx>>, pub patterns: Vec<Pattern<'tcx>>,

View File

@ -20,6 +20,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
#![cfg_attr(not(stage0), deny(warnings))] #![cfg_attr(not(stage0), deny(warnings))]
#![unstable(feature = "rustc_private", issue = "27812")] #![unstable(feature = "rustc_private", issue = "27812")]
#![feature(box_patterns)]
#![feature(rustc_private)] #![feature(rustc_private)]
#![feature(staged_api)] #![feature(staged_api)]

View File

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

View File

@ -0,0 +1,81 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! 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.
//!
//! 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::*;
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 (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),
is_cleanup: false
};
}
}
}
}
impl MirPass for ClearDeadBlocks {
fn run_on_mir<'a, 'tcx>(&mut self, mir: &mut Mir<'tcx>, _: &infer::InferCtxt<'a, 'tcx>)
{
self.clear_dead_blocks(mir);
}
}

View File

@ -16,18 +16,13 @@ use rustc::middle::ty;
use rustc::mir::repr::*; use rustc::mir::repr::*;
use rustc::mir::visit::MutVisitor; use rustc::mir::visit::MutVisitor;
use rustc::mir::mir_map::MirMap; 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>) { 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 { 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> { struct EraseRegionsVisitor<'a, 'tcx: 'a> {
tcx: &'a ty::ctxt<'tcx>, 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> { impl<'a, 'tcx> MutVisitor<'tcx> for EraseRegionsVisitor<'a, 'tcx> {
fn visit_mir(&mut self, mir: &mut Mir<'tcx>) { fn visit_mir(&mut self, mir: &mut Mir<'tcx>) {
self.erase_regions_return_ty(&mut mir.return_ty); self.erase_regions_return_ty(&mut mir.return_ty);

View File

@ -8,7 +8,9 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
pub mod clear_dead_blocks;
pub mod simplify_cfg; pub mod simplify_cfg;
pub mod erase_regions; pub mod erase_regions;
pub mod no_landing_pads; pub mod no_landing_pads;
pub mod type_check;
mod util; mod util;

View File

@ -11,7 +11,7 @@
//! This pass removes the unwind branch of all the terminators when the no-landing-pads option is //! This pass removes the unwind branch of all the terminators when the no-landing-pads option is
//! specified. //! specified.
use rustc::middle::ty; use rustc::middle::infer;
use rustc::mir::repr::*; use rustc::mir::repr::*;
use rustc::mir::visit::MutVisitor; use rustc::mir::visit::MutVisitor;
use rustc::mir::transform::MirPass; use rustc::mir::transform::MirPass;
@ -41,8 +41,9 @@ impl<'tcx> MutVisitor<'tcx> for NoLandingPads {
} }
impl MirPass for NoLandingPads { impl MirPass for NoLandingPads {
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>,
if tcx.sess.no_landing_pads() { infcx: &infer::InferCtxt<'a, 'tcx>) {
if infcx.tcx.sess.no_landing_pads() {
self.visit_mir(mir); self.visit_mir(mir);
} }
} }

View File

@ -9,6 +9,7 @@
// except according to those terms. // except according to those terms.
use rustc::middle::const_eval::ConstVal; use rustc::middle::const_eval::ConstVal;
use rustc::middle::infer;
use rustc::mir::repr::*; use rustc::mir::repr::*;
use transform::util; use transform::util;
use rustc::mir::transform::MirPass; use rustc::mir::transform::MirPass;
@ -119,7 +120,7 @@ impl SimplifyCfg {
} }
impl MirPass for 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; let mut changed = true;
while changed { while changed {
changed = self.simplify_branches(mir); changed = self.simplify_branches(mir);

View File

@ -0,0 +1,598 @@
// Copyright 2016 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//! This pass type-checks the MIR to ensure it is not broken.
#![allow(unreachable_code)]
use rustc::middle::infer::{self, InferCtxt};
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};
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)*))
)
})
}
macro_rules! span_mirbug_and_err {
($context:expr, $elem:expr, $($message:tt)*) => ({
{
$context.tcx().sess.span_warn(
$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.
///
/// 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>,
last_span: Span,
errors_reported: bool
}
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;
}
}
fn visit_lvalue(&mut self, lvalue: &Lvalue<'tcx>, _context: visit::LvalueContext) {
self.sanitize_lvalue(lvalue);
}
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);
}
if self.errors_reported {
return;
}
self.super_mir(mir);
}
}
impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
fn new(cx: &'a mut TypeChecker<'b, 'tcx>, mir: &'a Mir<'tcx>) -> Self {
TypeVerifier {
cx: cx,
mir: mir,
last_span: mir.span,
errors_reported: false
}
}
fn tcx(&self) -> &'a ty::ctxt<'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> {
if ty.needs_infer() || ty.has_escaping_regions() || ty.references_error() {
span_mirbug_and_err!(self, parent, "bad type {:?}", ty)
} else {
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);
let span = self.last_span;
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 {:?} as {:?}",
base_ty, adt_def1)
}
},
ProjectionElem::Field(field, fty) => {
let fty = self.sanitize_type(lvalue, fty);
match self.field_ty(lvalue, base, field) {
Ok(ty) => {
if let Err(terr) = self.cx.mk_eqty(span, 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 }
}
}
}
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<Ty<'tcx>, 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(field.ty(tcx, substs)))
} else {
Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
}
}
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);
debug!("normalize: ty={:?} obligations={:?}",
ty,
obligations);
let mut fulfill_cx = &mut self.cx.fulfillment_cx;
for obligation in obligations {
fulfill_cx.register_predicate_obligation(infcx, obligation);
}
ty
}
}
pub struct TypeChecker<'a, 'tcx: 'a> {
infcx: &'a InferCtxt<'a, 'tcx>,
fulfillment_cx: traits::FulfillmentContext<'tcx>,
last_span: Span
}
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
}
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) => {
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) = self.mk_subty(self.last_span, 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 { .. } => {
// no checks needed for these
}
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) = 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, ref targets } => {
let discr_ty = mir.lvalue_ty(tcx, discr).to_ty(tcx);
match discr_ty.sty {
ty::TyEnum(def, _)
if def == adt_def && adt_def.variants.len() == targets.len()
=> {},
_ => {
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) = self.mk_subty(self.last_span, 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 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() {
let op_arg_ty = mir.operand_ty(self.tcx(), op_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);
}
}
}
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) = self.mk_subty(self.last_span, 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);
}
}
}
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);
}
}
}
pub struct TypeckMir;
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 checker = TypeChecker::new(infcx);
{
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;
}
}
checker.typeck_mir(mir);
checker.verify_obligations(mir);
}
}

View File

@ -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) { match v.tcx.tables.borrow().adjustments.get(&e.id) {
None | None |
Some(&ty::adjustment::AdjustReifyFnPointer) | Some(&ty::adjustment::AdjustReifyFnPointer) |
Some(&ty::adjustment::AdjustUnsafeFnPointer) => {} Some(&ty::adjustment::AdjustUnsafeFnPointer) |
Some(&ty::adjustment::AdjustMutToConstPointer) => {}
Some(&ty::adjustment::AdjustDerefRef( Some(&ty::adjustment::AdjustDerefRef(
ty::adjustment::AutoDerefRef { autoderefs, .. } ty::adjustment::AutoDerefRef { autoderefs, .. }

View File

@ -40,7 +40,7 @@ use trans::type_of;
use trans::Disr; use trans::Disr;
use middle::subst::Substs; use middle::subst::Substs;
use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer}; 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::{self, Ty};
use middle::ty::cast::{CastTy,IntTy}; use middle::ty::cast::{CastTy,IntTy};
use util::nodemap::NodeMap; 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 // FIXME(#19925) once fn item types are
// zero-sized, we'll need to do something here // zero-sized, we'll need to do something here
} }
Some(AdjustUnsafeFnPointer) => { Some(AdjustUnsafeFnPointer) | Some(AdjustMutToConstPointer) => {
// purely a type-level thing // purely a type-level thing
} }
Some(AdjustDerefRef(adj)) => { Some(AdjustDerefRef(adj)) => {

View File

@ -71,7 +71,8 @@ use trans::tvec;
use trans::type_of; use trans::type_of;
use trans::Disr; use trans::Disr;
use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer}; 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::{self, Ty};
use middle::ty::MethodCall; use middle::ty::MethodCall;
use middle::ty::cast::{CastKind, CastTy}; 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 // zero-sized, we'll need to return true here
false false
} }
AdjustUnsafeFnPointer => { AdjustUnsafeFnPointer | AdjustMutToConstPointer => {
// purely a type-level thing // purely a type-level thing
false false
} }
@ -391,7 +392,7 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
// FIXME(#19925) once fn item types are // FIXME(#19925) once fn item types are
// zero-sized, we'll need to do something here // zero-sized, we'll need to do something here
} }
AdjustUnsafeFnPointer => { AdjustUnsafeFnPointer | AdjustMutToConstPointer => {
// purely a type-level thing // purely a type-level thing
} }
AdjustDerefRef(ref adj) => { AdjustDerefRef(ref adj) => {

View File

@ -9,7 +9,7 @@
// except according to those terms. // except according to those terms.
use llvm::{BasicBlockRef, ValueRef, OperandBundleDef}; use llvm::{BasicBlockRef, ValueRef, OperandBundleDef};
use rustc::middle::ty; use rustc::middle::ty::{self, Ty};
use rustc::mir::repr as mir; use rustc::mir::repr as mir;
use syntax::abi::Abi; use syntax::abi::Abi;
use trans::adt; use trans::adt;
@ -26,8 +26,55 @@ use trans::type_::Type;
use super::MirContext; use super::MirContext;
use super::operand::OperandValue::{FatPtr, Immediate, Ref}; 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> { 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 shouldnt 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<OperandRef<'tcx>>
{
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) { pub fn trans_block(&mut self, bb: mir::BasicBlock) {
debug!("trans_block({:?})", bb); debug!("trans_block({:?})", bb);
@ -159,13 +206,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
let mut arg_tys = Vec::new(); let mut arg_tys = Vec::new();
// Foreign-ABI functions are translated differently // Foreign-ABI functions are translated differently
let is_foreign = if let ty::TyBareFn(_, ref f) = callee.ty.sty { let abi_style = self.abi_style(callee.ty);
// We do not translate intrinsics here (they shouldnt be functions) let is_foreign = abi_style == AbiStyle::Foreign;
assert!(f.abi != Abi::RustIntrinsic && f.abi != Abi::PlatformIntrinsic);
f.abi != Abi::Rust && f.abi != Abi::RustCall
} else {
false
};
// Prepare the return value destination // Prepare the return value destination
let (ret_dest_ty, must_copy_dest) = if let Some((ref d, _)) = *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. // Process the rest of the args.
for arg in args { for operand in self.arg_operands(&bcx, abi_style, args) {
let operand = self.trans_operand(&bcx, arg);
match operand.val { match operand.val {
Ref(llval) | Immediate(llval) => llargs.push(llval), Ref(llval) | Immediate(llval) => llargs.push(llval),
FatPtr(b, e) => { FatPtr(b, e) => {

View File

@ -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_ty = tr_base.ty.to_ty(tcx);
let base_repr = adt::represent_type(ccx, base_ty); let base_repr = adt::represent_type(ccx, base_ty);
let discr = match tr_base.ty { let discr = match tr_base.ty {

View File

@ -9,13 +9,16 @@
// except according to those terms. // except according to those terms.
use llvm::ValueRef; use llvm::ValueRef;
use rustc::middle::ty::{Ty, TypeFoldable}; use rustc::middle::ty::{self, Ty};
use rustc::mir::repr as mir; use rustc::mir::repr as mir;
use trans::adt;
use trans::base; use trans::base;
use trans::common::{self, Block, BlockAndBuilder}; use trans::common::{self, Block, BlockAndBuilder};
use trans::datum; use trans::datum;
use trans::Disr;
use super::{MirContext, TempRef}; use super::{MirContext, TempRef};
use super::lvalue::LvalueRef;
/// The representation of a Rust value. The enum variant is in fact /// The representation of a Rust value. The enum variant is in fact
/// uniquely determined by the value's type, but is kept as a /// 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> { 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, pub fn trans_operand(&mut self,
bcx: &BlockAndBuilder<'bcx, 'tcx>, bcx: &BlockAndBuilder<'bcx, 'tcx>,
operand: &mir::Operand<'tcx>) operand: &mir::Operand<'tcx>)
@ -120,30 +149,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
// out from their home // out from their home
let tr_lvalue = self.trans_lvalue(bcx, lvalue); let tr_lvalue = self.trans_lvalue(bcx, lvalue);
let ty = tr_lvalue.ty.to_ty(bcx.tcx()); let ty = tr_lvalue.ty.to_ty(bcx.tcx());
debug!("trans_operand: tr_lvalue={} @ {:?}", self.trans_load(bcx, tr_lvalue.llval, ty)
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
}
} }
mir::Operand::Constant(ref constant) => { 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<OperandRef<'tcx>>
{
// 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()
}
} }

View File

@ -67,7 +67,7 @@ use middle::traits::{self, ObligationCause};
use middle::traits::{predicate_for_trait_def, report_selection_error}; use middle::traits::{predicate_for_trait_def, report_selection_error};
use middle::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef}; use middle::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef};
use middle::ty::adjustment::{AutoPtr, AutoUnsafe, AdjustReifyFnPointer}; 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::{self, LvaluePreference, TypeAndMut, Ty};
use middle::ty::fold::TypeFoldable; use middle::ty::fold::TypeFoldable;
use middle::ty::error::TypeError; use middle::ty::error::TypeError;
@ -427,6 +427,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
autoref: Some(AutoUnsafe(mutbl_b)), autoref: Some(AutoUnsafe(mutbl_b)),
unsize: None unsize: None
}))) })))
} else if mt_a.mutbl != mutbl_b {
Ok(Some(AdjustMutToConstPointer))
} else { } else {
Ok(None) Ok(None)
} }

View File

@ -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); check_struct_fields_on_error(fcx, expr.id, fields, base_expr);
return; return;
} }
let (adt, variant) = match fcx.def_struct_variant(def, path.span) { let variant = match fcx.def_struct_variant(def, path.span) {
Some((adt, variant)) => (adt, variant), Some((_, variant)) => variant,
None => { None => {
span_err!(fcx.tcx().sess, path.span, E0071, span_err!(fcx.tcx().sess, path.span, E0071,
"`{}` does not name a structure", "`{}` does not name a structure",
@ -3195,15 +3195,26 @@ fn check_expr_with_unifier<'a, 'tcx, F>(fcx: &FnCtxt<'a, 'tcx>,
check_expr_struct_fields(fcx, expr_ty, expr.span, variant, fields, check_expr_struct_fields(fcx, expr_ty, expr.span, variant, fields,
base_expr.is_none()); base_expr.is_none());
if let &Some(ref base_expr) = base_expr { if let &Some(ref base_expr) = base_expr {
check_expr_has_type(fcx, base_expr, expr_ty); check_expr_has_type(fcx, base_expr, expr_ty);
if adt.adt_kind() == ty::AdtKind::Enum { 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, span_err!(tcx.sess, base_expr.span, E0436,
"functional record update syntax requires a struct"); "functional record update syntax requires a struct");
} }
} }
} }
}
type ExprCheckerWithTy = fn(&FnCtxt, &hir::Expr, Ty); type ExprCheckerWithTy = fn(&FnCtxt, &hir::Expr, Ty);

View File

@ -43,6 +43,7 @@ pub fn resolve_type_vars_in_expr(fcx: &FnCtxt, e: &hir::Expr) {
wbcx.visit_upvar_borrow_map(); wbcx.visit_upvar_borrow_map();
wbcx.visit_closures(); wbcx.visit_closures();
wbcx.visit_liberated_fn_sigs(); wbcx.visit_liberated_fn_sigs();
wbcx.visit_fru_field_types();
} }
pub fn resolve_type_vars_in_fn(fcx: &FnCtxt, 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_upvar_borrow_map();
wbcx.visit_closures(); wbcx.visit_closures();
wbcx.visit_liberated_fn_sigs(); wbcx.visit_liberated_fn_sigs();
wbcx.visit_fru_field_types();
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
@ -305,6 +307,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
adjustment::AdjustReifyFnPointer adjustment::AdjustReifyFnPointer
} }
adjustment::AdjustMutToConstPointer => {
adjustment::AdjustMutToConstPointer
}
adjustment::AdjustUnsafeFnPointer => { adjustment::AdjustUnsafeFnPointer => {
adjustment::AdjustUnsafeFnPointer adjustment::AdjustUnsafeFnPointer
} }
@ -367,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<T:TypeFoldable<'tcx>>(&self, t: &T, reason: ResolveReason) -> T { fn resolve<T:TypeFoldable<'tcx>>(&self, t: &T, reason: ResolveReason) -> T {
t.fold_with(&mut Resolver::new(self.fcx, reason)) t.fold_with(&mut Resolver::new(self.fcx, reason))
} }
@ -383,6 +396,7 @@ enum ResolveReason {
ResolvingUpvar(ty::UpvarId), ResolvingUpvar(ty::UpvarId),
ResolvingClosure(DefId), ResolvingClosure(DefId),
ResolvingFnSig(ast::NodeId), ResolvingFnSig(ast::NodeId),
ResolvingFieldTypes(ast::NodeId)
} }
impl ResolveReason { impl ResolveReason {
@ -397,6 +411,9 @@ impl ResolveReason {
ResolvingFnSig(id) => { ResolvingFnSig(id) => {
tcx.map.span(id) tcx.map.span(id)
} }
ResolvingFieldTypes(id) => {
tcx.map.span(id)
}
ResolvingClosure(did) => { ResolvingClosure(did) => {
if let Some(node_id) = tcx.map.as_local_node_id(did) { if let Some(node_id) = tcx.map.as_local_node_id(did) {
tcx.expr_span(node_id) tcx.expr_span(node_id)
@ -474,14 +491,14 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> {
"cannot determine a type for this closure") "cannot determine a type for this closure")
} }
ResolvingFnSig(id) => { ResolvingFnSig(id) | ResolvingFieldTypes(id) => {
// any failures here should also fail when // any failures here should also fail when
// resolving the patterns, closure types, or // resolving the patterns, closure types, or
// something else. // something else.
let span = self.reason.span(self.tcx); let span = self.reason.span(self.tcx);
self.tcx.sess.delay_span_bug( self.tcx.sess.delay_span_bug(
span, span,
&format!("cannot resolve some aspect of fn sig for {:?}", id)); &format!("cannot resolve some aspect of data for {:?}", id));
} }
} }
} }

View File

@ -21,17 +21,14 @@ extern crate syntax;
use rustc::mir::transform::MirPass; use rustc::mir::transform::MirPass;
use rustc::mir::repr::{Mir, Literal}; use rustc::mir::repr::{Mir, Literal};
use rustc::mir::visit::MutVisitor; use rustc::mir::visit::MutVisitor;
use rustc::middle::ty; use rustc::middle::infer::InferCtxt;
use rustc::middle::const_eval::ConstVal; use rustc::middle::const_eval::ConstVal;
use rustc::lint::{LateContext, LintContext, LintPass, LateLintPass, LateLintPassObject, LintArray};
use rustc_plugin::Registry; use rustc_plugin::Registry;
use rustc_front::hir;
use syntax::attr;
struct Pass; struct Pass;
impl MirPass for 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) Visitor.visit_mir(mir)
} }
} }

View File

@ -0,0 +1,187 @@
// 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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![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
// 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));
// 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<u8> for Int {
#[rustc_mir]
fn shl_assign(&mut self, rhs: u8) {
self.0 <<= rhs;
}
}
impl ShlAssign<u16> for Int {
#[rustc_mir]
fn shl_assign(&mut self, rhs: u16) {
self.0 <<= rhs;
}
}
impl ShrAssign<u8> for Int {
#[rustc_mir]
fn shr_assign(&mut self, rhs: u8) {
self.0 >>= rhs;
}
}
impl ShrAssign<u16> 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<i32> for Slice {
#[rustc_mir]
fn add_assign(&mut self, rhs: i32) {
for lhs in &mut self.0 {
*lhs += rhs;
}
}
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![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 _);
}

View File

@ -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 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
#![feature(rustc_attrs)]
use std::marker::PhantomData;
pub trait DataBind {
type Data;
}
impl<T> DataBind for Global<T> {
type Data = T;
}
pub struct Global<T>(PhantomData<T>);
pub struct Data {
pub offsets: <Global<[u32; 2]> 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);
}

View File

@ -8,7 +8,7 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
#![feature(rustc_attrs)] #![feature(rustc_attrs, unboxed_closures, fn_traits)]
#[rustc_mir] #[rustc_mir]
fn test1(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) { 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) f(x, y)
} }
#[rustc_mir]
fn test_fn_direct_call<F>(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: &F) -> i32
where F: Fn(i32, i32) -> i32
{
f.call((100, -1))
}
#[rustc_mir]
fn test_fn_nil_call<F>(f: &F) -> i32
where F: Fn() -> i32
{
f()
}
fn main() { fn main() {
assert_eq!(test1(1, (2, 3), &[4, 5, 6]), (1, (2, 3), &[4, 5, 6][..])); assert_eq!(test1(1, (2, 3), &[4, 5, 6]), (1, (2, 3), &[4, 5, 6][..]));
assert_eq!(test2(98), 98); assert_eq!(test2(98), 98);
@ -128,9 +149,14 @@ fn main() {
assert_eq!(test8(), 2); assert_eq!(test8(), 2);
assert_eq!(test9(), 41 + 42 * 43); assert_eq!(test9(), 41 + 42 * 43);
let closure = |x: i32, y: i32| { x + y }; let r = 3;
assert_eq!(test_closure(&closure, 100, 1), 101); 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; let function_object = &closure as &Fn(i32, i32) -> i32;
assert_eq!(test_fn_object(function_object, 100, 2), 102); assert_eq!(test_fn_object(function_object, 100, 2), 312);
assert_eq!(test_fn_impl(&function_object, 100, 3), 103); 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);
} }