mirror of
https://github.com/rust-lang/rust.git
synced 2025-02-05 11:33:04 +00:00
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:
commit
6c751e0456
@ -721,10 +721,11 @@ impl<'d,'t,'a,'tcx> ExprUseVisitor<'d,'t,'a,'tcx> {
|
||||
if let Some(adjustment) = adj {
|
||||
match adjustment {
|
||||
adjustment::AdjustReifyFnPointer |
|
||||
adjustment::AdjustUnsafeFnPointer => {
|
||||
adjustment::AdjustUnsafeFnPointer |
|
||||
adjustment::AdjustMutToConstPointer => {
|
||||
// Creating a closure/fn-pointer or unsizing consumes
|
||||
// the input and stores it into the resulting rvalue.
|
||||
debug!("walk_adjustment(AdjustReifyFnPointer|AdjustUnsafeFnPointer)");
|
||||
debug!("walk_adjustment: trivial adjustment");
|
||||
let cmt_unadjusted =
|
||||
return_if_err!(self.mc.cat_expr_unadjusted(expr));
|
||||
self.delegate_consume(expr.id, expr.span, cmt_unadjusted);
|
||||
|
@ -430,6 +430,7 @@ impl<'t, 'a,'tcx> MemCategorizationContext<'t, 'a, 'tcx> {
|
||||
|
||||
adjustment::AdjustReifyFnPointer |
|
||||
adjustment::AdjustUnsafeFnPointer |
|
||||
adjustment::AdjustMutToConstPointer |
|
||||
adjustment::AdjustDerefRef(_) => {
|
||||
debug!("cat_expr({:?}): {:?}",
|
||||
adjustment,
|
||||
|
@ -21,8 +21,9 @@ use rustc_front::hir;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum AutoAdjustment<'tcx> {
|
||||
AdjustReifyFnPointer, // go from a fn-item type to a fn-pointer type
|
||||
AdjustUnsafeFnPointer, // go from a safe fn pointer to an unsafe fn pointer
|
||||
AdjustReifyFnPointer, // go from a fn-item type to a fn-pointer type
|
||||
AdjustUnsafeFnPointer, // go from a safe fn pointer to an unsafe fn pointer
|
||||
AdjustMutToConstPointer, // go from a mut raw pointer to a const raw pointer
|
||||
AdjustDerefRef(AutoDerefRef<'tcx>),
|
||||
}
|
||||
|
||||
@ -106,7 +107,8 @@ impl<'tcx> AutoAdjustment<'tcx> {
|
||||
pub fn is_identity(&self) -> bool {
|
||||
match *self {
|
||||
AdjustReifyFnPointer |
|
||||
AdjustUnsafeFnPointer => false,
|
||||
AdjustUnsafeFnPointer |
|
||||
AdjustMutToConstPointer => false,
|
||||
AdjustDerefRef(ref r) => r.is_identity(),
|
||||
}
|
||||
}
|
||||
@ -151,7 +153,7 @@ impl<'tcx> ty::TyS<'tcx> {
|
||||
return match adjustment {
|
||||
Some(adjustment) => {
|
||||
match *adjustment {
|
||||
AdjustReifyFnPointer => {
|
||||
AdjustReifyFnPointer => {
|
||||
match self.sty {
|
||||
ty::TyBareFn(Some(_), b) => {
|
||||
cx.mk_fn(None, b)
|
||||
@ -164,17 +166,32 @@ impl<'tcx> ty::TyS<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
AdjustUnsafeFnPointer => {
|
||||
AdjustUnsafeFnPointer => {
|
||||
match self.sty {
|
||||
ty::TyBareFn(None, b) => cx.safe_to_unsafe_fn_ty(b),
|
||||
ref b => {
|
||||
cx.sess.bug(
|
||||
&format!("AdjustReifyFnPointer adjustment on non-fn-item: \
|
||||
&format!("AdjustUnsafeFnPointer adjustment on non-fn-ptr: \
|
||||
{:?}",
|
||||
b));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AdjustMutToConstPointer => {
|
||||
match self.sty {
|
||||
ty::TyRawPtr(mt) => cx.mk_ptr(ty::TypeAndMut {
|
||||
ty: mt.ty,
|
||||
mutbl: hir::MutImmutable
|
||||
}),
|
||||
ref b => {
|
||||
cx.sess.bug(
|
||||
&format!("AdjustMutToConstPointer on non-raw-ptr: \
|
||||
{:?}",
|
||||
b));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AdjustDerefRef(ref adj) => {
|
||||
let mut adjusted_ty = self;
|
||||
|
@ -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<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> {
|
||||
@ -144,6 +150,7 @@ impl<'tcx> Tables<'tcx> {
|
||||
closure_tys: DefIdMap(),
|
||||
closure_kinds: DefIdMap(),
|
||||
liberated_fn_sigs: NodeMap(),
|
||||
fru_field_types: NodeMap()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,10 +8,12 @@
|
||||
// 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;
|
||||
use middle::ty;
|
||||
use middle::infer;
|
||||
|
||||
pub struct MirMap<'tcx> {
|
||||
pub map: NodeMap<Mir<'tcx>>,
|
||||
@ -19,9 +21,17 @@ pub struct MirMap<'tcx> {
|
||||
|
||||
impl<'tcx> MirMap<'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 {
|
||||
pass.run_on_mir(mir, tcx)
|
||||
pass.run_on_mir(mir, &infcx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -45,6 +45,9 @@ pub struct Mir<'tcx> {
|
||||
/// values in that it is possible to borrow them and mutate them
|
||||
/// through the resulting reference.
|
||||
pub temp_decls: Vec<TempDecl<'tcx>>,
|
||||
|
||||
/// A span representing this MIR, for error reporting
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
/// where execution begins
|
||||
@ -145,7 +148,7 @@ pub enum BorrowKind {
|
||||
|
||||
/// A "variable" is a binding declared by the user as part of the fn
|
||||
/// decl, a let, etc.
|
||||
#[derive(Clone, RustcEncodable, RustcDecodable)]
|
||||
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
|
||||
pub struct VarDecl<'tcx> {
|
||||
pub mutability: Mutability,
|
||||
pub name: Name,
|
||||
@ -154,7 +157,7 @@ pub struct VarDecl<'tcx> {
|
||||
|
||||
/// A "temp" is a temporary that we place on the stack. They are
|
||||
/// anonymous, always mutable, and have only a type.
|
||||
#[derive(Clone, RustcEncodable, RustcDecodable)]
|
||||
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
|
||||
pub struct TempDecl<'tcx> {
|
||||
pub ty: Ty<'tcx>,
|
||||
}
|
||||
@ -170,7 +173,7 @@ pub struct TempDecl<'tcx> {
|
||||
///
|
||||
/// there is only one argument, of type `(i32, u32)`, but two bindings
|
||||
/// (`x` and `y`).
|
||||
#[derive(Clone, RustcEncodable, RustcDecodable)]
|
||||
#[derive(Clone, Debug, RustcEncodable, RustcDecodable)]
|
||||
pub struct ArgDecl<'tcx> {
|
||||
pub ty: Ty<'tcx>,
|
||||
}
|
||||
@ -499,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
|
||||
@ -550,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> {
|
||||
@ -591,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 } =>
|
||||
|
@ -14,7 +14,8 @@
|
||||
*/
|
||||
|
||||
use mir::repr::*;
|
||||
use middle::subst::Substs;
|
||||
use middle::const_eval::ConstVal;
|
||||
use middle::subst::{Subst, Substs};
|
||||
use middle::ty::{self, AdtDef, Ty};
|
||||
use rustc_front::hir;
|
||||
|
||||
@ -72,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 }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -150,6 +135,73 @@ impl<'tcx> Mir<'tcx> {
|
||||
self.lvalue_ty(tcx, &proj.base).projection_ty(tcx, &proj.elem)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rvalue_ty(&self,
|
||||
tcx: &ty::ctxt<'tcx>,
|
||||
rvalue: &Rvalue<'tcx>)
|
||||
-> Option<Ty<'tcx>>
|
||||
{
|
||||
match *rvalue {
|
||||
Rvalue::Use(ref operand) => Some(self.operand_ty(tcx, operand)),
|
||||
Rvalue::Repeat(ref operand, ref count) => {
|
||||
if let ConstVal::Uint(u) = count.value {
|
||||
let op_ty = self.operand_ty(tcx, operand);
|
||||
Some(tcx.mk_array(op_ty, u as usize))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
Rvalue::Ref(reg, bk, ref lv) => {
|
||||
let lv_ty = self.lvalue_ty(tcx, lv).to_ty(tcx);
|
||||
Some(tcx.mk_ref(
|
||||
tcx.mk_region(reg),
|
||||
ty::TypeAndMut {
|
||||
ty: lv_ty,
|
||||
mutbl: bk.to_mutbl_lossy()
|
||||
}
|
||||
))
|
||||
}
|
||||
Rvalue::Len(..) => Some(tcx.types.usize),
|
||||
Rvalue::Cast(_, _, ty) => Some(ty),
|
||||
Rvalue::BinaryOp(op, ref lhs, ref rhs) => {
|
||||
let lhs_ty = self.operand_ty(tcx, lhs);
|
||||
let rhs_ty = self.operand_ty(tcx, rhs);
|
||||
Some(self.binop_ty(tcx, op, lhs_ty, rhs_ty))
|
||||
}
|
||||
Rvalue::UnaryOp(_, ref operand) => {
|
||||
Some(self.operand_ty(tcx, operand))
|
||||
}
|
||||
Rvalue::Box(t) => {
|
||||
Some(tcx.mk_box(t))
|
||||
}
|
||||
Rvalue::Aggregate(ref ak, ref ops) => {
|
||||
match *ak {
|
||||
AggregateKind::Vec => {
|
||||
if let Some(operand) = ops.get(0) {
|
||||
let ty = self.operand_ty(tcx, operand);
|
||||
Some(tcx.mk_array(ty, ops.len()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
AggregateKind::Tuple => {
|
||||
Some(tcx.mk_tup(
|
||||
ops.iter().map(|op| self.operand_ty(tcx, op)).collect()
|
||||
))
|
||||
}
|
||||
AggregateKind::Adt(def, _, substs) => {
|
||||
Some(def.type_scheme(tcx).ty.subst(tcx, substs))
|
||||
}
|
||||
AggregateKind::Closure(did, substs) => {
|
||||
Some(tcx.mk_closure_from_closure_substs(
|
||||
did, Box::new(substs.clone())))
|
||||
}
|
||||
}
|
||||
}
|
||||
Rvalue::Slice { .. } => None,
|
||||
Rvalue::InlineAsm(..) => None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BorrowKind {
|
||||
|
@ -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>);
|
||||
}
|
||||
|
@ -138,6 +138,7 @@ pub struct Options {
|
||||
pub no_trans: bool,
|
||||
pub error_format: ErrorOutputType,
|
||||
pub treat_err_as_bug: bool,
|
||||
pub mir_opt_level: usize,
|
||||
|
||||
/// if true, build up the dep-graph
|
||||
pub build_dep_graph: bool,
|
||||
@ -254,6 +255,7 @@ pub fn basic_options() -> Options {
|
||||
parse_only: false,
|
||||
no_trans: false,
|
||||
treat_err_as_bug: false,
|
||||
mir_opt_level: 1,
|
||||
build_dep_graph: false,
|
||||
dump_dep_graph: false,
|
||||
no_analysis: false,
|
||||
@ -655,6 +657,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
|
||||
"show spans for compiler debugging (expr|pat|ty)"),
|
||||
print_trans_items: Option<String> = (None, parse_opt_string,
|
||||
"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 {
|
||||
@ -988,6 +992,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||
let parse_only = debugging_opts.parse_only;
|
||||
let no_trans = debugging_opts.no_trans;
|
||||
let treat_err_as_bug = debugging_opts.treat_err_as_bug;
|
||||
let mir_opt_level = debugging_opts.mir_opt_level.unwrap_or(1);
|
||||
let incremental_compilation = debugging_opts.incr_comp;
|
||||
let dump_dep_graph = debugging_opts.dump_dep_graph;
|
||||
let no_analysis = debugging_opts.no_analysis;
|
||||
@ -1166,6 +1171,7 @@ pub fn build_session_options(matches: &getopts::Matches) -> Options {
|
||||
parse_only: parse_only,
|
||||
no_trans: no_trans,
|
||||
treat_err_as_bug: treat_err_as_bug,
|
||||
mir_opt_level: mir_opt_level,
|
||||
build_dep_graph: incremental_compilation || dump_dep_graph,
|
||||
dump_dep_graph: dump_dep_graph,
|
||||
no_analysis: no_analysis,
|
||||
|
@ -396,6 +396,9 @@ impl<'tcx> fmt::Debug for ty::adjustment::AutoAdjustment<'tcx> {
|
||||
ty::adjustment::AdjustUnsafeFnPointer => {
|
||||
write!(f, "AdjustUnsafeFnPointer")
|
||||
}
|
||||
ty::adjustment::AdjustMutToConstPointer => {
|
||||
write!(f, "AdjustMutToConstPointer")
|
||||
}
|
||||
ty::adjustment::AdjustDerefRef(ref data) => {
|
||||
write!(f, "{:?}", data)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -610,7 +610,7 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> {
|
||||
|
||||
self.emit_enum("AutoAdjustment", |this| {
|
||||
match *adj {
|
||||
adjustment::AdjustReifyFnPointer=> {
|
||||
adjustment::AdjustReifyFnPointer => {
|
||||
this.emit_enum_variant("AdjustReifyFnPointer", 1, 0, |_| Ok(()))
|
||||
}
|
||||
|
||||
@ -620,8 +620,14 @@ impl<'a, 'tcx> rbml_writer_helpers<'tcx> for Encoder<'a> {
|
||||
})
|
||||
}
|
||||
|
||||
adjustment::AdjustMutToConstPointer => {
|
||||
this.emit_enum_variant("AdjustMutToConstPointer", 3, 0, |_| {
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
||||
adjustment::AdjustDerefRef(ref auto_deref_ref) => {
|
||||
this.emit_enum_variant("AdjustDerefRef", 3, 2, |this| {
|
||||
this.emit_enum_variant("AdjustDerefRef", 4, 2, |this| {
|
||||
this.emit_enum_variant_arg(0,
|
||||
|this| Ok(this.emit_auto_deref_ref(ecx, auto_deref_ref)))
|
||||
})
|
||||
@ -1002,12 +1008,14 @@ impl<'a, 'tcx> rbml_decoder_decoder_helpers<'tcx> for reader::Decoder<'a> {
|
||||
fn read_auto_adjustment<'b, 'c>(&mut self, dcx: &DecodeContext<'b, 'c, 'tcx>)
|
||||
-> adjustment::AutoAdjustment<'tcx> {
|
||||
self.read_enum("AutoAdjustment", |this| {
|
||||
let variants = ["AdjustReifyFnPointer", "AdjustUnsafeFnPointer", "AdjustDerefRef"];
|
||||
let variants = ["AdjustReifyFnPointer", "AdjustUnsafeFnPointer",
|
||||
"AdjustMutToConstPointer", "AdjustDerefRef"];
|
||||
this.read_enum_variant(&variants, |this, i| {
|
||||
Ok(match i {
|
||||
1 => adjustment::AdjustReifyFnPointer,
|
||||
2 => adjustment::AdjustUnsafeFnPointer,
|
||||
3 => {
|
||||
3 => adjustment::AdjustMutToConstPointer,
|
||||
4 => {
|
||||
let auto_deref_ref: adjustment::AutoDerefRef =
|
||||
this.read_enum_variant_arg(0,
|
||||
|this| Ok(this.read_auto_deref_ref(dcx))).unwrap();
|
||||
|
@ -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 } => {
|
||||
|
@ -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,24 @@ 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));
|
||||
|
||||
// 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()
|
||||
.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))
|
||||
|
@ -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)
|
||||
});
|
||||
|
@ -21,7 +21,8 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||
-> Vec<MatchPair<'pat, 'tcx>> {
|
||||
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()
|
||||
|
@ -80,7 +80,7 @@ macro_rules! unpack {
|
||||
/// the main entry point for building MIR for a function
|
||||
|
||||
pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
|
||||
_span: Span,
|
||||
span: Span,
|
||||
implicit_arguments: Vec<Ty<'tcx>>,
|
||||
explicit_arguments: Vec<(Ty<'tcx>, &'tcx hir::Pat)>,
|
||||
argument_extent: CodeExtent,
|
||||
@ -97,7 +97,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
|
||||
temp_decls: vec![],
|
||||
var_decls: vec![],
|
||||
var_indices: FnvHashMap(),
|
||||
unit_temp: None
|
||||
unit_temp: None,
|
||||
};
|
||||
|
||||
assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
|
||||
@ -119,6 +119,7 @@ pub fn construct<'a,'tcx>(hir: Cx<'a,'tcx>,
|
||||
arg_decls: arg_decls,
|
||||
temp_decls: builder.temp_decls,
|
||||
return_ty: return_ty,
|
||||
span: span
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -418,25 +418,28 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||
len: Operand<'tcx>,
|
||||
span: Span) {
|
||||
// fn(&(filename: &'static str, line: u32), index: usize, length: usize) -> !
|
||||
let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
|
||||
let func = self.lang_function(lang_items::PanicBoundsCheckFnLangItem);
|
||||
let args = func.ty.fn_args();
|
||||
let ref_ty = args.skip_binder()[0];
|
||||
let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty {
|
||||
(region, tyandmut.ty)
|
||||
let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0;
|
||||
|
||||
let ref_ty = args[0];
|
||||
let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty {
|
||||
tyandmut.ty
|
||||
} else {
|
||||
self.hir.span_bug(span, &format!("unexpected panic_bound_check type: {:?}", func.ty));
|
||||
};
|
||||
|
||||
let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
|
||||
let (file, line) = self.span_to_fileline_args(span);
|
||||
let elems = vec![Operand::Constant(file), Operand::Constant(line)];
|
||||
// FIXME: We should have this as a constant, rather than a stack variable (to not pollute
|
||||
// icache with cold branch code), however to achieve that we either have to rely on rvalue
|
||||
// promotion or have some way, in MIR, to create constants.
|
||||
self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (file_arg, line_arg);
|
||||
self.cfg.push_assign(block, span, &tuple, // tuple = (file_arg, line_arg);
|
||||
Rvalue::Aggregate(AggregateKind::Tuple, elems));
|
||||
// FIXME: is this region really correct here?
|
||||
self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple;
|
||||
Rvalue::Ref(*region, BorrowKind::Unique, tuple));
|
||||
self.cfg.push_assign(block, span, &tuple_ref, // tuple_ref = &tuple;
|
||||
Rvalue::Ref(region, BorrowKind::Shared, tuple));
|
||||
let cleanup = self.diverge_cleanup();
|
||||
self.cfg.terminate(block, Terminator::Call {
|
||||
func: Operand::Constant(func),
|
||||
@ -449,18 +452,21 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||
/// Create diverge cleanup and branch to it from `block`.
|
||||
pub fn panic(&mut self, block: BasicBlock, msg: &'static str, span: Span) {
|
||||
// fn(&(msg: &'static str filename: &'static str, line: u32)) -> !
|
||||
let region = ty::ReStatic; // FIXME(mir-borrowck): use a better region?
|
||||
let func = self.lang_function(lang_items::PanicFnLangItem);
|
||||
let args = func.ty.fn_args();
|
||||
let ref_ty = args.skip_binder()[0];
|
||||
let (region, tup_ty) = if let ty::TyRef(region, tyandmut) = ref_ty.sty {
|
||||
(region, tyandmut.ty)
|
||||
let args = self.hir.tcx().replace_late_bound_regions(&func.ty.fn_args(), |_| region).0;
|
||||
|
||||
let ref_ty = args[0];
|
||||
let tup_ty = if let ty::TyRef(_, tyandmut) = ref_ty.sty {
|
||||
tyandmut.ty
|
||||
} else {
|
||||
self.hir.span_bug(span, &format!("unexpected panic type: {:?}", func.ty));
|
||||
};
|
||||
|
||||
let (tuple, tuple_ref) = (self.temp(tup_ty), self.temp(ref_ty));
|
||||
let (file, line) = self.span_to_fileline_args(span);
|
||||
let message = Constant {
|
||||
span: DUMMY_SP,
|
||||
span: span,
|
||||
ty: self.hir.tcx().mk_static_str(),
|
||||
literal: self.hir.str_literal(intern_and_get_ident(msg))
|
||||
};
|
||||
@ -470,11 +476,11 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||
// FIXME: We should have this as a constant, rather than a stack variable (to not pollute
|
||||
// icache with cold branch code), however to achieve that we either have to rely on rvalue
|
||||
// promotion or have some way, in MIR, to create constants.
|
||||
self.cfg.push_assign(block, DUMMY_SP, &tuple, // tuple = (message_arg, file_arg, line_arg);
|
||||
self.cfg.push_assign(block, span, &tuple, // tuple = (message_arg, file_arg, line_arg);
|
||||
Rvalue::Aggregate(AggregateKind::Tuple, elems));
|
||||
// FIXME: is this region really correct here?
|
||||
self.cfg.push_assign(block, DUMMY_SP, &tuple_ref, // tuple_ref = &tuple;
|
||||
Rvalue::Ref(*region, BorrowKind::Unique, tuple));
|
||||
self.cfg.push_assign(block, span, &tuple_ref, // tuple_ref = &tuple;
|
||||
Rvalue::Ref(region, BorrowKind::Shared, tuple));
|
||||
let cleanup = self.diverge_cleanup();
|
||||
self.cfg.terminate(block, Terminator::Call {
|
||||
func: Operand::Constant(func),
|
||||
@ -505,11 +511,11 @@ impl<'a,'tcx> Builder<'a,'tcx> {
|
||||
fn span_to_fileline_args(&mut self, span: Span) -> (Constant<'tcx>, Constant<'tcx>) {
|
||||
let span_lines = self.hir.tcx().sess.codemap().lookup_char_pos(span.lo);
|
||||
(Constant {
|
||||
span: DUMMY_SP,
|
||||
span: span,
|
||||
ty: self.hir.tcx().mk_static_str(),
|
||||
literal: self.hir.str_literal(intern_and_get_ident(&span_lines.file.name))
|
||||
}, Constant {
|
||||
span: DUMMY_SP,
|
||||
span: span,
|
||||
ty: self.hir.tcx().types.u32,
|
||||
literal: self.hir.usize_literal(span_lines.line)
|
||||
})
|
||||
|
@ -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:
|
||||
@ -54,14 +56,35 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
|
||||
// Find the actual method implementation being called and
|
||||
// build the appropriate UFCS call expression with the
|
||||
// callee-object as self parameter.
|
||||
|
||||
// rewrite f(u, v) into FnOnce::call_once(f, (u, v))
|
||||
|
||||
let method = method_callee(cx, self, ty::MethodCall::expr(self.id));
|
||||
let mut argrefs = vec![fun.to_ref()];
|
||||
argrefs.extend(args.iter().map(|a| a.to_ref()));
|
||||
|
||||
let sig = match method.ty.sty {
|
||||
ty::TyBareFn(_, fn_ty) => &fn_ty.sig,
|
||||
_ => cx.tcx.sess.span_bug(self.span, "type of method is not an fn")
|
||||
};
|
||||
|
||||
let sig = cx.tcx.no_late_bound_regions(sig).unwrap_or_else(|| {
|
||||
cx.tcx.sess.span_bug(self.span, "method call has late-bound regions")
|
||||
});
|
||||
|
||||
assert_eq!(sig.inputs.len(), 2);
|
||||
|
||||
let tupled_args = Expr {
|
||||
ty: sig.inputs[1],
|
||||
temp_lifetime: temp_lifetime,
|
||||
span: self.span,
|
||||
kind: ExprKind::Tuple {
|
||||
fields: args.iter().map(ToRef::to_ref).collect()
|
||||
}
|
||||
};
|
||||
|
||||
ExprKind::Call {
|
||||
ty: method.ty,
|
||||
fun: method.to_ref(),
|
||||
args: argrefs,
|
||||
args: vec![fun.to_ref(), tupled_args.to_ref()]
|
||||
}
|
||||
} else {
|
||||
let adt_data = if let hir::ExprPath(..) = fun.node {
|
||||
@ -125,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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -227,13 +259,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 {
|
||||
@ -241,7 +283,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 => {
|
||||
@ -385,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,
|
||||
@ -395,6 +434,9 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
|
||||
kind: kind,
|
||||
};
|
||||
|
||||
debug!("make_mirror: unadjusted-expr={:?} applying adjustments={:?}",
|
||||
expr, cx.tcx.tables.borrow().adjustments.get(&self.id));
|
||||
|
||||
// Now apply adjustments, if any.
|
||||
match cx.tcx.tables.borrow().adjustments.get(&self.id) {
|
||||
None => {}
|
||||
@ -416,6 +458,15 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Expr {
|
||||
kind: ExprKind::UnsafeFnPointer { source: expr.to_ref() },
|
||||
};
|
||||
}
|
||||
Some(&ty::adjustment::AdjustMutToConstPointer) => {
|
||||
let adjusted_ty = cx.tcx.expr_ty_adjusted(self);
|
||||
expr = Expr {
|
||||
temp_lifetime: temp_lifetime,
|
||||
ty: adjusted_ty,
|
||||
span: self.span,
|
||||
kind: ExprKind::Cast { source: expr.to_ref() },
|
||||
};
|
||||
}
|
||||
Some(&ty::adjustment::AdjustDerefRef(ref adj)) => {
|
||||
for i in 0..adj.autoderefs {
|
||||
let i = i as u32;
|
||||
@ -426,10 +477,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 {
|
||||
@ -749,11 +828,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()
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -229,7 +229,7 @@ pub enum ExprKind<'tcx> {
|
||||
variant_index: usize,
|
||||
substs: &'tcx Substs<'tcx>,
|
||||
fields: Vec<FieldExprRef<'tcx>>,
|
||||
base: Option<ExprRef<'tcx>>,
|
||||
base: Option<FruInfo<'tcx>>
|
||||
},
|
||||
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<Ty<'tcx>>
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Arm<'tcx> {
|
||||
pub patterns: Vec<Pattern<'tcx>>,
|
||||
|
@ -20,6 +20,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment!
|
||||
#![cfg_attr(not(stage0), deny(warnings))]
|
||||
#![unstable(feature = "rustc_private", issue = "27812")]
|
||||
|
||||
#![feature(box_patterns)]
|
||||
#![feature(rustc_private)]
|
||||
#![feature(staged_api)]
|
||||
|
||||
|
@ -22,7 +22,8 @@ extern crate rustc_front;
|
||||
use build;
|
||||
use graphviz;
|
||||
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::mir::repr::Mir;
|
||||
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) {
|
||||
Ok(mut mir) => {
|
||||
no_landing_pads::NoLandingPads.run_on_mir(&mut mir, self.tcx);
|
||||
simplify_cfg::SimplifyCfg::new().run_on_mir(&mut mir, self.tcx);
|
||||
|
||||
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, &infcx);
|
||||
}
|
||||
let meta_item_list = self.attr
|
||||
.iter()
|
||||
.flat_map(|a| a.meta_item_list())
|
||||
|
81
src/librustc_mir/transform/clear_dead_blocks.rs
Normal file
81
src/librustc_mir/transform/clear_dead_blocks.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -8,7 +8,9 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
pub mod clear_dead_blocks;
|
||||
pub mod simplify_cfg;
|
||||
pub mod erase_regions;
|
||||
pub mod no_landing_pads;
|
||||
pub mod type_check;
|
||||
mod util;
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
598
src/librustc_mir/transform/type_check.rs
Normal file
598
src/librustc_mir/transform/type_check.rs
Normal 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);
|
||||
}
|
||||
}
|
@ -768,7 +768,8 @@ fn check_adjustments<'a, 'tcx>(v: &mut CheckCrateVisitor<'a, 'tcx>, e: &hir::Exp
|
||||
match v.tcx.tables.borrow().adjustments.get(&e.id) {
|
||||
None |
|
||||
Some(&ty::adjustment::AdjustReifyFnPointer) |
|
||||
Some(&ty::adjustment::AdjustUnsafeFnPointer) => {}
|
||||
Some(&ty::adjustment::AdjustUnsafeFnPointer) |
|
||||
Some(&ty::adjustment::AdjustMutToConstPointer) => {}
|
||||
|
||||
Some(&ty::adjustment::AdjustDerefRef(
|
||||
ty::adjustment::AutoDerefRef { autoderefs, .. }
|
||||
|
@ -40,7 +40,7 @@ use trans::type_of;
|
||||
use trans::Disr;
|
||||
use middle::subst::Substs;
|
||||
use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer};
|
||||
use middle::ty::adjustment::AdjustUnsafeFnPointer;
|
||||
use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
|
||||
use middle::ty::{self, Ty};
|
||||
use middle::ty::cast::{CastTy,IntTy};
|
||||
use util::nodemap::NodeMap;
|
||||
@ -354,7 +354,7 @@ pub fn const_expr<'a, 'tcx>(cx: &CrateContext<'a, 'tcx>,
|
||||
// FIXME(#19925) once fn item types are
|
||||
// zero-sized, we'll need to do something here
|
||||
}
|
||||
Some(AdjustUnsafeFnPointer) => {
|
||||
Some(AdjustUnsafeFnPointer) | Some(AdjustMutToConstPointer) => {
|
||||
// purely a type-level thing
|
||||
}
|
||||
Some(AdjustDerefRef(adj)) => {
|
||||
|
@ -71,7 +71,8 @@ use trans::tvec;
|
||||
use trans::type_of;
|
||||
use trans::Disr;
|
||||
use middle::ty::adjustment::{AdjustDerefRef, AdjustReifyFnPointer};
|
||||
use middle::ty::adjustment::{AdjustUnsafeFnPointer, CustomCoerceUnsized};
|
||||
use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
|
||||
use middle::ty::adjustment::CustomCoerceUnsized;
|
||||
use middle::ty::{self, Ty};
|
||||
use middle::ty::MethodCall;
|
||||
use middle::ty::cast::{CastKind, CastTy};
|
||||
@ -354,7 +355,7 @@ fn adjustment_required<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
// zero-sized, we'll need to return true here
|
||||
false
|
||||
}
|
||||
AdjustUnsafeFnPointer => {
|
||||
AdjustUnsafeFnPointer | AdjustMutToConstPointer => {
|
||||
// purely a type-level thing
|
||||
false
|
||||
}
|
||||
@ -391,7 +392,7 @@ fn apply_adjustments<'blk, 'tcx>(bcx: Block<'blk, 'tcx>,
|
||||
// FIXME(#19925) once fn item types are
|
||||
// zero-sized, we'll need to do something here
|
||||
}
|
||||
AdjustUnsafeFnPointer => {
|
||||
AdjustUnsafeFnPointer | AdjustMutToConstPointer => {
|
||||
// purely a type-level thing
|
||||
}
|
||||
AdjustDerefRef(ref adj) => {
|
||||
|
@ -9,7 +9,7 @@
|
||||
// except according to those terms.
|
||||
|
||||
use llvm::{BasicBlockRef, ValueRef, OperandBundleDef};
|
||||
use rustc::middle::ty;
|
||||
use rustc::middle::ty::{self, Ty};
|
||||
use rustc::mir::repr as mir;
|
||||
use syntax::abi::Abi;
|
||||
use trans::adt;
|
||||
@ -26,8 +26,55 @@ use trans::type_::Type;
|
||||
|
||||
use super::MirContext;
|
||||
use super::operand::OperandValue::{FatPtr, Immediate, Ref};
|
||||
use super::operand::OperandRef;
|
||||
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum AbiStyle {
|
||||
Foreign,
|
||||
RustCall,
|
||||
Rust
|
||||
}
|
||||
|
||||
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||
fn abi_style(&self, fn_ty: Ty<'tcx>) -> AbiStyle {
|
||||
if let ty::TyBareFn(_, ref f) = fn_ty.sty {
|
||||
// We do not translate intrinsics here (they shouldn’t be functions)
|
||||
assert!(f.abi != Abi::RustIntrinsic && f.abi != Abi::PlatformIntrinsic);
|
||||
|
||||
match f.abi {
|
||||
Abi::Rust => AbiStyle::Rust,
|
||||
Abi::RustCall => AbiStyle::RustCall,
|
||||
_ => AbiStyle::Foreign
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
fn arg_operands(&mut self,
|
||||
bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
||||
abi_style: AbiStyle,
|
||||
args: &[mir::Operand<'tcx>])
|
||||
-> Vec<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) {
|
||||
debug!("trans_block({:?})", bb);
|
||||
|
||||
@ -159,13 +206,8 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||
let mut arg_tys = Vec::new();
|
||||
|
||||
// Foreign-ABI functions are translated differently
|
||||
let is_foreign = if let ty::TyBareFn(_, ref f) = callee.ty.sty {
|
||||
// We do not translate intrinsics here (they shouldn’t be functions)
|
||||
assert!(f.abi != Abi::RustIntrinsic && f.abi != Abi::PlatformIntrinsic);
|
||||
f.abi != Abi::Rust && f.abi != Abi::RustCall
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let abi_style = self.abi_style(callee.ty);
|
||||
let is_foreign = abi_style == AbiStyle::Foreign;
|
||||
|
||||
// Prepare the return value destination
|
||||
let (ret_dest_ty, must_copy_dest) = if let Some((ref d, _)) = *destination {
|
||||
@ -182,8 +224,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||
};
|
||||
|
||||
// Process the rest of the args.
|
||||
for arg in args {
|
||||
let operand = self.trans_operand(&bcx, arg);
|
||||
for operand in self.arg_operands(&bcx, abi_style, args) {
|
||||
match operand.val {
|
||||
Ref(llval) | Immediate(llval) => llargs.push(llval),
|
||||
FatPtr(b, e) => {
|
||||
|
@ -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 {
|
||||
|
@ -9,13 +9,16 @@
|
||||
// except according to those terms.
|
||||
|
||||
use llvm::ValueRef;
|
||||
use rustc::middle::ty::{Ty, TypeFoldable};
|
||||
use rustc::middle::ty::{self, Ty};
|
||||
use rustc::mir::repr as mir;
|
||||
use trans::adt;
|
||||
use trans::base;
|
||||
use trans::common::{self, Block, BlockAndBuilder};
|
||||
use trans::datum;
|
||||
use trans::Disr;
|
||||
|
||||
use super::{MirContext, TempRef};
|
||||
use super::lvalue::LvalueRef;
|
||||
|
||||
/// The representation of a Rust value. The enum variant is in fact
|
||||
/// uniquely determined by the value's type, but is kept as a
|
||||
@ -90,6 +93,32 @@ impl<'tcx> OperandRef<'tcx> {
|
||||
}
|
||||
|
||||
impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||
pub fn trans_load(&mut self,
|
||||
bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
||||
llval: ValueRef,
|
||||
ty: Ty<'tcx>)
|
||||
-> OperandRef<'tcx>
|
||||
{
|
||||
debug!("trans_load: {} @ {:?}", bcx.val_to_string(llval), ty);
|
||||
|
||||
let val = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) {
|
||||
datum::ByValue => {
|
||||
bcx.with_block(|bcx| {
|
||||
OperandValue::Immediate(base::load_ty(bcx, llval, ty))
|
||||
})
|
||||
}
|
||||
datum::ByRef if common::type_is_fat_ptr(bcx.tcx(), ty) => {
|
||||
let (lldata, llextra) = bcx.with_block(|bcx| {
|
||||
base::load_fat_ptr(bcx, llval, ty)
|
||||
});
|
||||
OperandValue::FatPtr(lldata, llextra)
|
||||
}
|
||||
datum::ByRef => OperandValue::Ref(llval)
|
||||
};
|
||||
|
||||
OperandRef { val: val, ty: ty }
|
||||
}
|
||||
|
||||
pub fn trans_operand(&mut self,
|
||||
bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
||||
operand: &mir::Operand<'tcx>)
|
||||
@ -120,30 +149,7 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||
// out from their home
|
||||
let tr_lvalue = self.trans_lvalue(bcx, lvalue);
|
||||
let ty = tr_lvalue.ty.to_ty(bcx.tcx());
|
||||
debug!("trans_operand: tr_lvalue={} @ {:?}",
|
||||
bcx.val_to_string(tr_lvalue.llval),
|
||||
ty);
|
||||
let val = match datum::appropriate_rvalue_mode(bcx.ccx(), ty) {
|
||||
datum::ByValue => {
|
||||
bcx.with_block(|bcx| {
|
||||
OperandValue::Immediate(base::load_ty(bcx, tr_lvalue.llval, ty))
|
||||
})
|
||||
}
|
||||
datum::ByRef if common::type_is_fat_ptr(bcx.tcx(), ty) => {
|
||||
let (lldata, llextra) = bcx.with_block(|bcx| {
|
||||
base::load_fat_ptr(bcx, tr_lvalue.llval, ty)
|
||||
});
|
||||
OperandValue::FatPtr(lldata, llextra)
|
||||
}
|
||||
datum::ByRef => OperandValue::Ref(tr_lvalue.llval)
|
||||
};
|
||||
|
||||
assert!(!ty.has_erasable_regions());
|
||||
|
||||
OperandRef {
|
||||
val: val,
|
||||
ty: ty
|
||||
}
|
||||
self.trans_load(bcx, tr_lvalue.llval, ty)
|
||||
}
|
||||
|
||||
mir::Operand::Constant(ref constant) => {
|
||||
@ -197,4 +203,46 @@ impl<'bcx, 'tcx> MirContext<'bcx, 'tcx> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn trans_operand_untupled(&mut self,
|
||||
bcx: &BlockAndBuilder<'bcx, 'tcx>,
|
||||
operand: &mir::Operand<'tcx>)
|
||||
-> Vec<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()
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ use middle::traits::{self, ObligationCause};
|
||||
use middle::traits::{predicate_for_trait_def, report_selection_error};
|
||||
use middle::ty::adjustment::{AutoAdjustment, AutoDerefRef, AdjustDerefRef};
|
||||
use middle::ty::adjustment::{AutoPtr, AutoUnsafe, AdjustReifyFnPointer};
|
||||
use middle::ty::adjustment::{AdjustUnsafeFnPointer};
|
||||
use middle::ty::adjustment::{AdjustUnsafeFnPointer, AdjustMutToConstPointer};
|
||||
use middle::ty::{self, LvaluePreference, TypeAndMut, Ty};
|
||||
use middle::ty::fold::TypeFoldable;
|
||||
use middle::ty::error::TypeError;
|
||||
@ -427,6 +427,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
||||
autoref: Some(AutoUnsafe(mutbl_b)),
|
||||
unsize: None
|
||||
})))
|
||||
} else if mt_a.mutbl != mutbl_b {
|
||||
Ok(Some(AdjustMutToConstPointer))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
@ -305,6 +307,10 @@ impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> {
|
||||
adjustment::AdjustReifyFnPointer
|
||||
}
|
||||
|
||||
adjustment::AdjustMutToConstPointer => {
|
||||
adjustment::AdjustMutToConstPointer
|
||||
}
|
||||
|
||||
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 {
|
||||
t.fold_with(&mut Resolver::new(self.fcx, reason))
|
||||
}
|
||||
@ -383,6 +396,7 @@ enum ResolveReason {
|
||||
ResolvingUpvar(ty::UpvarId),
|
||||
ResolvingClosure(DefId),
|
||||
ResolvingFnSig(ast::NodeId),
|
||||
ResolvingFieldTypes(ast::NodeId)
|
||||
}
|
||||
|
||||
impl ResolveReason {
|
||||
@ -397,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)
|
||||
@ -474,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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
187
src/test/run-pass/mir_augmented_assignments.rs
Normal file
187
src/test/run-pass/mir_augmented_assignments.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
41
src/test/run-pass/mir_autoderef.rs
Normal file
41
src/test/run-pass/mir_autoderef.rs
Normal 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 _);
|
||||
}
|
41
src/test/run-pass/mir_struct_with_assoc_ty.rs
Normal file
41
src/test/run-pass/mir_struct_with_assoc_ty.rs
Normal 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);
|
||||
}
|
@ -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: &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() {
|
||||
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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user