add MIR code (unused thus far)
This commit is contained in:
parent
0e764ec5ce
commit
9bd35c07c2
|
@ -329,6 +329,9 @@ impl RegionMaps {
|
||||||
pub fn item_extent(&self, n: ast::NodeId) -> CodeExtent {
|
pub fn item_extent(&self, n: ast::NodeId) -> CodeExtent {
|
||||||
self.lookup_code_extent(CodeExtentData::DestructionScope(n))
|
self.lookup_code_extent(CodeExtentData::DestructionScope(n))
|
||||||
}
|
}
|
||||||
|
pub fn opt_destruction_extent(&self, n: ast::NodeId) -> Option<CodeExtent> {
|
||||||
|
self.code_extent_interner.borrow().get(&CodeExtentData::DestructionScope(n)).cloned()
|
||||||
|
}
|
||||||
pub fn intern_code_extent(&self,
|
pub fn intern_code_extent(&self,
|
||||||
e: CodeExtentData,
|
e: CodeExtentData,
|
||||||
parent: CodeExtent) -> CodeExtent {
|
parent: CodeExtent) -> CodeExtent {
|
||||||
|
|
|
@ -3475,6 +3475,13 @@ impl<'tcx, 'container> AdtDefData<'tcx, 'container> {
|
||||||
.expect("variant_with_id: unknown variant")
|
.expect("variant_with_id: unknown variant")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn variant_index_with_id(&self, vid: DefId) -> usize {
|
||||||
|
self.variants
|
||||||
|
.iter()
|
||||||
|
.position(|v| v.did == vid)
|
||||||
|
.expect("variant_index_with_id: unknown variant")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn variant_of_def(&self, def: def::Def) -> &VariantDefData<'tcx, 'container> {
|
pub fn variant_of_def(&self, def: def::Def) -> &VariantDefData<'tcx, 'container> {
|
||||||
match def {
|
match def {
|
||||||
def::DefVariant(_, vid, _) => self.variant_with_id(vid),
|
def::DefVariant(_, vid, _) => self.variant_with_id(vid),
|
||||||
|
@ -5223,14 +5230,11 @@ impl<'tcx> TyS<'tcx> {
|
||||||
{
|
{
|
||||||
let method_call = MethodCall::autoderef(expr_id, autoderef);
|
let method_call = MethodCall::autoderef(expr_id, autoderef);
|
||||||
let mut adjusted_ty = self;
|
let mut adjusted_ty = self;
|
||||||
match method_type(method_call) {
|
if let Some(method_ty) = method_type(method_call) {
|
||||||
Some(method_ty) => {
|
// Method calls always have all late-bound regions
|
||||||
// Method calls always have all late-bound regions
|
// fully instantiated.
|
||||||
// fully instantiated.
|
let fn_ret = cx.no_late_bound_regions(&method_ty.fn_ret()).unwrap();
|
||||||
let fn_ret = cx.no_late_bound_regions(&method_ty.fn_ret()).unwrap();
|
adjusted_ty = fn_ret.unwrap();
|
||||||
adjusted_ty = fn_ret.unwrap();
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
match adjusted_ty.builtin_deref(true, NoPreference) {
|
match adjusted_ty.builtin_deref(true, NoPreference) {
|
||||||
Some(mt) => mt.ty,
|
Some(mt) => mt.ty,
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
use build::{BlockAnd, Builder};
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
pub fn ast_block(&mut self,
|
||||||
|
destination: &Lvalue<H>,
|
||||||
|
mut block: BasicBlock,
|
||||||
|
ast_block: H::Block)
|
||||||
|
-> BlockAnd<()> {
|
||||||
|
let this = self;
|
||||||
|
let Block { extent, span: _, stmts, expr } = this.hir.mirror(ast_block);
|
||||||
|
this.in_scope(extent, block, |this| {
|
||||||
|
unpack!(block = this.stmts(block, stmts));
|
||||||
|
this.into(destination, block, expr)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//! Routines for manipulating the control-flow graph.
|
||||||
|
|
||||||
|
use build::CFG;
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
impl<H:Hair> CFG<H> {
|
||||||
|
pub fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<H> {
|
||||||
|
&self.basic_blocks[blk.index()]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn block_data_mut(&mut self, blk: BasicBlock) -> &mut BasicBlockData<H> {
|
||||||
|
&mut self.basic_blocks[blk.index()]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn end_point(&self, block: BasicBlock) -> ExecutionPoint {
|
||||||
|
ExecutionPoint {
|
||||||
|
block: block,
|
||||||
|
statement: self.block_data(block).statements.len() as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn start_new_block(&mut self) -> BasicBlock {
|
||||||
|
let node_index = self.basic_blocks.len();
|
||||||
|
self.basic_blocks.push(BasicBlockData::new(Terminator::Diverge));
|
||||||
|
BasicBlock::new(node_index)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&mut self, block: BasicBlock, statement: Statement<H>) {
|
||||||
|
debug!("push({:?}, {:?})", block, statement);
|
||||||
|
self.block_data_mut(block).statements.push(statement);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_assign_constant(&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
span: H::Span,
|
||||||
|
temp: &Lvalue<H>,
|
||||||
|
constant: Constant<H>) {
|
||||||
|
self.push_assign(block, span, temp, Rvalue::Use(Operand::Constant(constant)));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_drop(&mut self, block: BasicBlock, span: H::Span,
|
||||||
|
kind: DropKind, lvalue: &Lvalue<H>) {
|
||||||
|
self.push(block, Statement {
|
||||||
|
span: span,
|
||||||
|
kind: StatementKind::Drop(kind, lvalue.clone())
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_assign(&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
span: H::Span,
|
||||||
|
lvalue: &Lvalue<H>,
|
||||||
|
rvalue: Rvalue<H>) {
|
||||||
|
self.push(block, Statement {
|
||||||
|
span: span,
|
||||||
|
kind: StatementKind::Assign(lvalue.clone(), rvalue)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn terminate(&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
terminator: Terminator<H>) {
|
||||||
|
// Check whether this block has already been terminated. For
|
||||||
|
// this, we rely on the fact that the initial state is to have
|
||||||
|
// a Diverge terminator and an empty list of targets (which
|
||||||
|
// is not a valid state).
|
||||||
|
debug_assert!(match self.block_data(block).terminator { Terminator::Diverge => true,
|
||||||
|
_ => false },
|
||||||
|
"terminate: block {:?} already has a terminator set", block);
|
||||||
|
|
||||||
|
self.block_data_mut(block).terminator = terminator;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,123 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! See docs in build/expr/mod.rs
|
||||||
|
|
||||||
|
use rustc_data_structures::fnv::FnvHashMap;
|
||||||
|
|
||||||
|
use build::{Builder};
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
/// Compile `expr`, yielding a compile-time constant. Assumes that
|
||||||
|
/// `expr` is a valid compile-time constant!
|
||||||
|
pub fn as_constant<M>(&mut self, expr: M) -> Constant<H>
|
||||||
|
where M: Mirror<H, Output=Expr<H>>
|
||||||
|
{
|
||||||
|
let expr = self.hir.mirror(expr);
|
||||||
|
self.expr_as_constant(expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_as_constant(&mut self, expr: Expr<H>) -> Constant<H> {
|
||||||
|
let this = self;
|
||||||
|
let Expr { ty: _, temp_lifetime: _, span, kind } = expr;
|
||||||
|
let kind = match kind {
|
||||||
|
ExprKind::Scope { extent: _, value } => {
|
||||||
|
return this.as_constant(value);
|
||||||
|
}
|
||||||
|
ExprKind::Paren { arg } => {
|
||||||
|
return this.as_constant(arg);
|
||||||
|
}
|
||||||
|
ExprKind::Literal { literal } => {
|
||||||
|
ConstantKind::Literal(literal)
|
||||||
|
}
|
||||||
|
ExprKind::Vec { fields } => {
|
||||||
|
let fields = this.as_constants(fields);
|
||||||
|
ConstantKind::Aggregate(AggregateKind::Vec, fields)
|
||||||
|
}
|
||||||
|
ExprKind::Tuple { fields } => {
|
||||||
|
let fields = this.as_constants(fields);
|
||||||
|
ConstantKind::Aggregate(AggregateKind::Tuple, fields)
|
||||||
|
}
|
||||||
|
ExprKind::Adt { adt_def, variant_index, substs, fields, base: None } => {
|
||||||
|
let field_names = this.hir.fields(adt_def, variant_index);
|
||||||
|
let fields = this.named_field_constants(field_names, fields);
|
||||||
|
ConstantKind::Aggregate(AggregateKind::Adt(adt_def, variant_index, substs), fields)
|
||||||
|
}
|
||||||
|
ExprKind::Repeat { value, count } => {
|
||||||
|
let value = Box::new(this.as_constant(value));
|
||||||
|
let count = Box::new(this.as_constant(count));
|
||||||
|
ConstantKind::Repeat(value, count)
|
||||||
|
}
|
||||||
|
ExprKind::Binary { op, lhs, rhs } => {
|
||||||
|
let lhs = Box::new(this.as_constant(lhs));
|
||||||
|
let rhs = Box::new(this.as_constant(rhs));
|
||||||
|
ConstantKind::BinaryOp(op, lhs, rhs)
|
||||||
|
}
|
||||||
|
ExprKind::Unary { op, arg } => {
|
||||||
|
let arg = Box::new(this.as_constant(arg));
|
||||||
|
ConstantKind::UnaryOp(op, arg)
|
||||||
|
}
|
||||||
|
ExprKind::Field { lhs, name } => {
|
||||||
|
let lhs = this.as_constant(lhs);
|
||||||
|
ConstantKind::Projection(
|
||||||
|
Box::new(ConstantProjection {
|
||||||
|
base: lhs,
|
||||||
|
elem: ProjectionElem::Field(name),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
ExprKind::Deref { arg } => {
|
||||||
|
let arg = this.as_constant(arg);
|
||||||
|
ConstantKind::Projection(
|
||||||
|
Box::new(ConstantProjection {
|
||||||
|
base: arg,
|
||||||
|
elem: ProjectionElem::Deref,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
ExprKind::Call { fun, args } => {
|
||||||
|
let fun = this.as_constant(fun);
|
||||||
|
let args = this.as_constants(args);
|
||||||
|
ConstantKind::Call(Box::new(fun), args)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
this.hir.span_bug(
|
||||||
|
span,
|
||||||
|
&format!("expression is not a valid constant {:?}", kind));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Constant { span: span, kind: kind }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_constants(&mut self,
|
||||||
|
exprs: Vec<ExprRef<H>>)
|
||||||
|
-> Vec<Constant<H>>
|
||||||
|
{
|
||||||
|
exprs.into_iter().map(|expr| self.as_constant(expr)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn named_field_constants(&mut self,
|
||||||
|
field_names: Vec<Field<H>>,
|
||||||
|
field_exprs: Vec<FieldExprRef<H>>)
|
||||||
|
-> Vec<Constant<H>>
|
||||||
|
{
|
||||||
|
let fields_map: FnvHashMap<_, _> =
|
||||||
|
field_exprs.into_iter()
|
||||||
|
.map(|f| (f.name, self.as_constant(f.expr)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let fields: Vec<_> =
|
||||||
|
field_names.into_iter()
|
||||||
|
.map(|n| fields_map[&n].clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
fields
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! See docs in build/expr/mod.rs
|
||||||
|
|
||||||
|
use build::{BlockAnd, Builder};
|
||||||
|
use build::expr::category::Category;
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
/// Compile `expr`, yielding an lvalue that we can move from etc.
|
||||||
|
pub fn as_lvalue<M>(&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
expr: M)
|
||||||
|
-> BlockAnd<Lvalue<H>>
|
||||||
|
where M: Mirror<H, Output=Expr<H>>
|
||||||
|
{
|
||||||
|
let expr = self.hir.mirror(expr);
|
||||||
|
self.expr_as_lvalue(block, expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_as_lvalue(&mut self,
|
||||||
|
mut block: BasicBlock,
|
||||||
|
expr: Expr<H>)
|
||||||
|
-> BlockAnd<Lvalue<H>>
|
||||||
|
{
|
||||||
|
debug!("expr_as_lvalue(block={:?}, expr={:?})",
|
||||||
|
block, expr);
|
||||||
|
|
||||||
|
let this = self;
|
||||||
|
let expr_span = expr.span;
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Scope { extent, value } => {
|
||||||
|
this.in_scope(extent, block, |this| {
|
||||||
|
this.as_lvalue(block, value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ExprKind::Paren { arg } => {
|
||||||
|
this.as_lvalue(block, arg)
|
||||||
|
}
|
||||||
|
ExprKind::Field { lhs, name } => {
|
||||||
|
let lvalue = unpack!(block = this.as_lvalue(block, lhs));
|
||||||
|
let lvalue = lvalue.field(name);
|
||||||
|
block.and(lvalue)
|
||||||
|
}
|
||||||
|
ExprKind::Deref { arg } => {
|
||||||
|
let lvalue = unpack!(block = this.as_lvalue(block, arg));
|
||||||
|
let lvalue = lvalue.deref();
|
||||||
|
block.and(lvalue)
|
||||||
|
}
|
||||||
|
ExprKind::Index { lhs, index } => {
|
||||||
|
let (usize_ty, bool_ty) = (this.hir.usize_ty(), this.hir.bool_ty());
|
||||||
|
|
||||||
|
let slice = unpack!(block = this.as_lvalue(block, lhs));
|
||||||
|
|
||||||
|
let idx = unpack!(block = this.as_operand(block, index));
|
||||||
|
|
||||||
|
// bounds check:
|
||||||
|
let (len, lt) = (this.temp(usize_ty.clone()), this.temp(bool_ty));
|
||||||
|
this.cfg.push_assign(block, expr_span, // len = len(slice)
|
||||||
|
&len, Rvalue::Len(slice.clone()));
|
||||||
|
this.cfg.push_assign(block, expr_span, // lt = idx < len
|
||||||
|
<, Rvalue::BinaryOp(BinOp::Lt,
|
||||||
|
idx.clone(),
|
||||||
|
Operand::Consume(len)));
|
||||||
|
|
||||||
|
let (success, failure) = (this.cfg.start_new_block(),
|
||||||
|
this.cfg.start_new_block());
|
||||||
|
this.cfg.terminate(block,
|
||||||
|
Terminator::If {
|
||||||
|
cond: Operand::Consume(lt),
|
||||||
|
targets: [success, failure]
|
||||||
|
});
|
||||||
|
this.panic(failure);
|
||||||
|
success.and(slice.index(idx))
|
||||||
|
}
|
||||||
|
ExprKind::SelfRef => {
|
||||||
|
block.and(Lvalue::Arg(0))
|
||||||
|
}
|
||||||
|
ExprKind::VarRef { id } => {
|
||||||
|
let index = this.var_indices[&id];
|
||||||
|
block.and(Lvalue::Var(index))
|
||||||
|
}
|
||||||
|
ExprKind::StaticRef { id } => {
|
||||||
|
block.and(Lvalue::Static(id))
|
||||||
|
}
|
||||||
|
|
||||||
|
ExprKind::Vec { .. } |
|
||||||
|
ExprKind::Tuple { .. } |
|
||||||
|
ExprKind::Adt { .. } |
|
||||||
|
ExprKind::Closure { .. } |
|
||||||
|
ExprKind::Unary { .. } |
|
||||||
|
ExprKind::Binary { .. } |
|
||||||
|
ExprKind::LogicalOp { .. } |
|
||||||
|
ExprKind::Box { .. } |
|
||||||
|
ExprKind::Cast { .. } |
|
||||||
|
ExprKind::ReifyFnPointer { .. } |
|
||||||
|
ExprKind::UnsafeFnPointer { .. } |
|
||||||
|
ExprKind::Unsize { .. } |
|
||||||
|
ExprKind::Repeat { .. } |
|
||||||
|
ExprKind::Borrow { .. } |
|
||||||
|
ExprKind::If { .. } |
|
||||||
|
ExprKind::Match { .. } |
|
||||||
|
ExprKind::Loop { .. } |
|
||||||
|
ExprKind::Block { .. } |
|
||||||
|
ExprKind::Assign { .. } |
|
||||||
|
ExprKind::AssignOp { .. } |
|
||||||
|
ExprKind::Break { .. } |
|
||||||
|
ExprKind::Continue { .. } |
|
||||||
|
ExprKind::Return { .. } |
|
||||||
|
ExprKind::Literal { .. } |
|
||||||
|
ExprKind::InlineAsm { .. } |
|
||||||
|
ExprKind::Call { .. } => {
|
||||||
|
// these are not lvalues, so we need to make a temporary.
|
||||||
|
debug_assert!(match Category::of(&expr.kind) {
|
||||||
|
Some(Category::Lvalue) => false,
|
||||||
|
_ => true,
|
||||||
|
});
|
||||||
|
this.as_temp(block, expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! See docs in build/expr/mod.rs
|
||||||
|
|
||||||
|
use build::{BlockAnd, Builder};
|
||||||
|
use build::expr::category::Category;
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
/// Compile `expr` into a value that can be used as an operand.
|
||||||
|
/// If `expr` is an lvalue like `x`, this will introduce a
|
||||||
|
/// temporary `tmp = x`, so that we capture the value of `x` at
|
||||||
|
/// this time.
|
||||||
|
pub fn as_operand<M>(&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
expr: M)
|
||||||
|
-> BlockAnd<Operand<H>>
|
||||||
|
where M: Mirror<H, Output=Expr<H>>
|
||||||
|
{
|
||||||
|
let expr = self.hir.mirror(expr);
|
||||||
|
self.expr_as_operand(block, expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_as_operand(&mut self,
|
||||||
|
mut block: BasicBlock,
|
||||||
|
expr: Expr<H>)
|
||||||
|
-> BlockAnd<Operand<H>>
|
||||||
|
{
|
||||||
|
debug!("expr_as_operand(block={:?}, expr={:?})",
|
||||||
|
block, expr);
|
||||||
|
let this = self;
|
||||||
|
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Scope { extent, value } => {
|
||||||
|
return this.in_scope(extent, block, |this| {
|
||||||
|
this.as_operand(block, value)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ExprKind::Paren { arg } => {
|
||||||
|
return this.as_operand(block, arg);
|
||||||
|
}
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
|
||||||
|
let category = Category::of(&expr.kind).unwrap();
|
||||||
|
debug!("expr_as_operand: category={:?} for={:?}", category, expr.kind);
|
||||||
|
match category {
|
||||||
|
Category::Constant => {
|
||||||
|
let constant = this.as_constant(expr);
|
||||||
|
block.and(Operand::Constant(constant))
|
||||||
|
}
|
||||||
|
Category::Lvalue |
|
||||||
|
Category::Rvalue(..) => {
|
||||||
|
let operand = unpack!(block = this.as_temp(block, expr));
|
||||||
|
block.and(Operand::Consume(operand))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,220 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! See docs in build/expr/mod.rs
|
||||||
|
|
||||||
|
use rustc_data_structures::fnv::FnvHashMap;
|
||||||
|
|
||||||
|
use build::{BlockAnd, Builder};
|
||||||
|
use build::expr::category::{Category, RvalueFunc};
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
/// Compile `expr`, yielding an rvalue.
|
||||||
|
pub fn as_rvalue<M>(&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
expr: M)
|
||||||
|
-> BlockAnd<Rvalue<H>>
|
||||||
|
where M: Mirror<H, Output=Expr<H>>
|
||||||
|
{
|
||||||
|
let expr = self.hir.mirror(expr);
|
||||||
|
self.expr_as_rvalue(block, expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_as_rvalue(&mut self,
|
||||||
|
mut block: BasicBlock,
|
||||||
|
expr: Expr<H>)
|
||||||
|
-> BlockAnd<Rvalue<H>>
|
||||||
|
{
|
||||||
|
debug!("expr_as_rvalue(block={:?}, expr={:?})",
|
||||||
|
block, expr);
|
||||||
|
|
||||||
|
let this = self;
|
||||||
|
let expr_span = expr.span;
|
||||||
|
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Scope { extent, value } => {
|
||||||
|
this.in_scope(extent, block, |this| {
|
||||||
|
this.as_rvalue(block, value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ExprKind::Paren { arg } => {
|
||||||
|
this.as_rvalue(block, arg)
|
||||||
|
}
|
||||||
|
ExprKind::InlineAsm { asm } => {
|
||||||
|
block.and(Rvalue::InlineAsm(asm))
|
||||||
|
}
|
||||||
|
ExprKind::Repeat { value, count } => {
|
||||||
|
let value_operand = unpack!(block = this.as_operand(block, value));
|
||||||
|
let count_operand = unpack!(block = this.as_operand(block, count));
|
||||||
|
block.and(Rvalue::Repeat(value_operand, count_operand))
|
||||||
|
}
|
||||||
|
ExprKind::Borrow { region, borrow_kind, arg } => {
|
||||||
|
let arg_lvalue = unpack!(block = this.as_lvalue(block, arg));
|
||||||
|
block.and(Rvalue::Ref(region, borrow_kind, arg_lvalue))
|
||||||
|
}
|
||||||
|
ExprKind::Binary { op, lhs, rhs } => {
|
||||||
|
let lhs = unpack!(block = this.as_operand(block, lhs));
|
||||||
|
let rhs = unpack!(block = this.as_operand(block, rhs));
|
||||||
|
block.and(Rvalue::BinaryOp(op, lhs, rhs))
|
||||||
|
}
|
||||||
|
ExprKind::Unary { op, arg } => {
|
||||||
|
let arg = unpack!(block = this.as_operand(block, arg));
|
||||||
|
block.and(Rvalue::UnaryOp(op, arg))
|
||||||
|
}
|
||||||
|
ExprKind::Box { place: _, value } => {
|
||||||
|
let value = this.hir.mirror(value);
|
||||||
|
let value_ty = value.ty.clone();
|
||||||
|
let result = this.temp(value_ty.clone());
|
||||||
|
|
||||||
|
// to start, malloc some memory of suitable type (thus far, uninitialized):
|
||||||
|
let rvalue = Rvalue::Box(value.ty.clone());
|
||||||
|
this.cfg.push_assign(block, expr_span, &result, rvalue);
|
||||||
|
|
||||||
|
// schedule a shallow free of that memory, lest we unwind:
|
||||||
|
let extent = this.extent_of_innermost_scope().unwrap();
|
||||||
|
this.schedule_drop(expr_span, extent, DropKind::Shallow, &result, value_ty);
|
||||||
|
|
||||||
|
// initialize the box contents:
|
||||||
|
let contents = result.clone().deref();
|
||||||
|
unpack!(block = this.into(&contents, block, value));
|
||||||
|
|
||||||
|
// now that the result is fully initialized, cancel the drop
|
||||||
|
// by "using" the result (which is linear):
|
||||||
|
block.and(Rvalue::Use(Operand::Consume(result)))
|
||||||
|
}
|
||||||
|
ExprKind::Cast { source } => {
|
||||||
|
let source = unpack!(block = this.as_operand(block, source));
|
||||||
|
block.and(Rvalue::Cast(CastKind::Misc, source, expr.ty))
|
||||||
|
}
|
||||||
|
ExprKind::ReifyFnPointer { source } => {
|
||||||
|
let source = unpack!(block = this.as_operand(block, source));
|
||||||
|
block.and(Rvalue::Cast(CastKind::ReifyFnPointer, source, expr.ty))
|
||||||
|
}
|
||||||
|
ExprKind::UnsafeFnPointer { source } => {
|
||||||
|
let source = unpack!(block = this.as_operand(block, source));
|
||||||
|
block.and(Rvalue::Cast(CastKind::UnsafeFnPointer, source, expr.ty))
|
||||||
|
}
|
||||||
|
ExprKind::Unsize { source } => {
|
||||||
|
let source = unpack!(block = this.as_operand(block, source));
|
||||||
|
block.and(Rvalue::Cast(CastKind::Unsize, source, expr.ty))
|
||||||
|
}
|
||||||
|
ExprKind::Vec { fields } => {
|
||||||
|
// (*) We would (maybe) be closer to trans if we
|
||||||
|
// handled this and other aggregate cases via
|
||||||
|
// `into()`, not `as_rvalue` -- in that case, instead
|
||||||
|
// of generating
|
||||||
|
//
|
||||||
|
// let tmp1 = ...1;
|
||||||
|
// let tmp2 = ...2;
|
||||||
|
// dest = Rvalue::Aggregate(Foo, [tmp1, tmp2])
|
||||||
|
//
|
||||||
|
// we could just generate
|
||||||
|
//
|
||||||
|
// dest.f = ...1;
|
||||||
|
// dest.g = ...2;
|
||||||
|
//
|
||||||
|
// The problem is that then we would need to:
|
||||||
|
//
|
||||||
|
// (a) have a more complex mechanism for handling
|
||||||
|
// partial cleanup;
|
||||||
|
// (b) distinguish the case where the type `Foo` has a
|
||||||
|
// destructor, in which case creating an instance
|
||||||
|
// as a whole "arms" the destructor, and you can't
|
||||||
|
// write individual fields; and,
|
||||||
|
// (c) handle the case where the type Foo has no
|
||||||
|
// fields. We don't want `let x: ();` to compile
|
||||||
|
// to the same MIR as `let x = ();`.
|
||||||
|
|
||||||
|
// first process the set of fields
|
||||||
|
let fields: Vec<_> =
|
||||||
|
fields.into_iter()
|
||||||
|
.map(|f| unpack!(block = this.as_operand(block, f)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
block.and(Rvalue::Aggregate(AggregateKind::Vec, fields))
|
||||||
|
}
|
||||||
|
ExprKind::Tuple { fields } => { // see (*) above
|
||||||
|
// first process the set of fields
|
||||||
|
let fields: Vec<_> =
|
||||||
|
fields.into_iter()
|
||||||
|
.map(|f| unpack!(block = this.as_operand(block, f)))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
block.and(Rvalue::Aggregate(AggregateKind::Tuple, fields))
|
||||||
|
}
|
||||||
|
ExprKind::Closure { closure_id, substs, upvars } => { // see (*) above
|
||||||
|
let upvars =
|
||||||
|
upvars.into_iter()
|
||||||
|
.map(|upvar| unpack!(block = this.as_operand(block, upvar)))
|
||||||
|
.collect();
|
||||||
|
block.and(Rvalue::Aggregate(AggregateKind::Closure(closure_id, substs), upvars))
|
||||||
|
}
|
||||||
|
ExprKind::Adt { adt_def, variant_index, substs, fields, base } => { // see (*) above
|
||||||
|
// first process the set of fields
|
||||||
|
let fields_map: FnvHashMap<_, _> =
|
||||||
|
fields.into_iter()
|
||||||
|
.map(|f| (f.name, unpack!(block = this.as_operand(block, f.expr))))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let field_names =
|
||||||
|
this.hir.fields(adt_def, variant_index);
|
||||||
|
|
||||||
|
let base =
|
||||||
|
base.map(|base| unpack!(block = this.as_lvalue(block, base)));
|
||||||
|
|
||||||
|
// 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<_> =
|
||||||
|
field_names.into_iter()
|
||||||
|
.map(|n| match fields_map.get(&n) {
|
||||||
|
Some(v) => v.clone(),
|
||||||
|
None => Operand::Consume(base.clone().unwrap().field(n)),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
block.and(Rvalue::Aggregate(AggregateKind::Adt(adt_def, variant_index, substs),
|
||||||
|
fields))
|
||||||
|
}
|
||||||
|
ExprKind::Literal { .. } |
|
||||||
|
ExprKind::Block { .. } |
|
||||||
|
ExprKind::Match { .. } |
|
||||||
|
ExprKind::If { .. } |
|
||||||
|
ExprKind::Loop { .. } |
|
||||||
|
ExprKind::LogicalOp { .. } |
|
||||||
|
ExprKind::Call { .. } |
|
||||||
|
ExprKind::Field { .. } |
|
||||||
|
ExprKind::Deref { .. } |
|
||||||
|
ExprKind::Index { .. } |
|
||||||
|
ExprKind::VarRef { .. } |
|
||||||
|
ExprKind::SelfRef |
|
||||||
|
ExprKind::Assign { .. } |
|
||||||
|
ExprKind::AssignOp { .. } |
|
||||||
|
ExprKind::Break { .. } |
|
||||||
|
ExprKind::Continue { .. } |
|
||||||
|
ExprKind::Return { .. } |
|
||||||
|
ExprKind::StaticRef { .. } => {
|
||||||
|
// these do not have corresponding `Rvalue` variants,
|
||||||
|
// so make an operand and then return that
|
||||||
|
debug_assert!(match Category::of(&expr.kind) {
|
||||||
|
Some(Category::Rvalue(RvalueFunc::AsRvalue)) => false,
|
||||||
|
_ => true,
|
||||||
|
});
|
||||||
|
let operand = unpack!(block = this.as_operand(block, expr));
|
||||||
|
block.and(Rvalue::Use(operand))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! See docs in build/expr/mod.rs
|
||||||
|
|
||||||
|
use build::{BlockAnd, Builder};
|
||||||
|
use build::expr::category::Category;
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
/// Compile `expr` into a fresh temporary. This is used when building
|
||||||
|
/// up rvalues so as to freeze the value that will be consumed.
|
||||||
|
pub fn as_temp<M>(&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
expr: M)
|
||||||
|
-> BlockAnd<Lvalue<H>>
|
||||||
|
where M: Mirror<H, Output=Expr<H>>
|
||||||
|
{
|
||||||
|
let expr = self.hir.mirror(expr);
|
||||||
|
self.expr_as_temp(block, expr)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn expr_as_temp(&mut self,
|
||||||
|
mut block: BasicBlock,
|
||||||
|
expr: Expr<H>)
|
||||||
|
-> BlockAnd<Lvalue<H>>
|
||||||
|
{
|
||||||
|
debug!("expr_as_temp(block={:?}, expr={:?})",
|
||||||
|
block, expr);
|
||||||
|
let this = self;
|
||||||
|
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Scope { extent, value } => {
|
||||||
|
return this.in_scope(extent, block, |this| {
|
||||||
|
this.as_temp(block, value)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ExprKind::Paren { arg } => {
|
||||||
|
return this.as_temp(block, arg);
|
||||||
|
}
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
|
||||||
|
let expr_ty = expr.ty.clone();
|
||||||
|
let temp = this.temp(expr_ty.clone());
|
||||||
|
let temp_lifetime = match expr.temp_lifetime {
|
||||||
|
Some(t) => t,
|
||||||
|
None => {
|
||||||
|
this.hir.span_bug(
|
||||||
|
expr.span,
|
||||||
|
&format!("no temp_lifetime for expr"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this.schedule_drop(expr.span, temp_lifetime, DropKind::Deep, &temp, expr_ty);
|
||||||
|
|
||||||
|
// Careful here not to cause an infinite cycle. If we always
|
||||||
|
// called `into`, then for lvalues like `x.f`, it would
|
||||||
|
// eventually fallback to us, and we'd loop. There's a reason
|
||||||
|
// for this: `as_temp` is the point where we bridge the "by
|
||||||
|
// reference" semantics of `as_lvalue` with the "by value"
|
||||||
|
// semantics of `into`, `as_operand`, `as_rvalue`, and (of
|
||||||
|
// course) `as_temp`.
|
||||||
|
match Category::of(&expr.kind).unwrap() {
|
||||||
|
Category::Lvalue => {
|
||||||
|
let expr_span = expr.span;
|
||||||
|
let lvalue = unpack!(block = this.as_lvalue(block, expr));
|
||||||
|
let rvalue = Rvalue::Use(Operand::Consume(lvalue));
|
||||||
|
this.cfg.push_assign(block, expr_span, &temp, rvalue);
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
unpack!(block = this.into(&temp, block, expr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
block.and(temp)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,93 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use hair::*;
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum Category {
|
||||||
|
// An assignable memory location like `x`, `x.f`, `foo()[3]`, that
|
||||||
|
// sort of thing. Something that could appear on the LHS of an `=`
|
||||||
|
// sign.
|
||||||
|
Lvalue,
|
||||||
|
|
||||||
|
// A literal like `23` or `"foo"`. Does not include constant
|
||||||
|
// expressions like `3 + 5`.
|
||||||
|
Constant,
|
||||||
|
|
||||||
|
// Something that generates a new value at runtime, like `x + y`
|
||||||
|
// or `foo()`.
|
||||||
|
Rvalue(RvalueFunc),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Rvalues fall into different "styles" that will determine which fn
|
||||||
|
// is best suited to generate them.
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
pub enum RvalueFunc {
|
||||||
|
// Best generated by `into`. This is generally exprs that
|
||||||
|
// cause branching, like `match`, but also includes calls.
|
||||||
|
Into,
|
||||||
|
|
||||||
|
// Best generated by `as_rvalue`. This is usually the case.
|
||||||
|
AsRvalue,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Determines the category for a given expression. Note that scope
|
||||||
|
/// and paren expressions have no category.
|
||||||
|
impl Category {
|
||||||
|
pub fn of<H:Hair>(ek: &ExprKind<H>) -> Option<Category> {
|
||||||
|
match *ek {
|
||||||
|
ExprKind::Scope { .. } |
|
||||||
|
ExprKind::Paren { .. } =>
|
||||||
|
None,
|
||||||
|
|
||||||
|
ExprKind::Field { .. } |
|
||||||
|
ExprKind::Deref { .. } |
|
||||||
|
ExprKind::Index { .. } |
|
||||||
|
ExprKind::SelfRef |
|
||||||
|
ExprKind::VarRef { .. } |
|
||||||
|
ExprKind::StaticRef { .. } =>
|
||||||
|
Some(Category::Lvalue),
|
||||||
|
|
||||||
|
ExprKind::LogicalOp { .. } |
|
||||||
|
ExprKind::If { .. } |
|
||||||
|
ExprKind::Match { .. } |
|
||||||
|
ExprKind::Call { .. } =>
|
||||||
|
Some(Category::Rvalue(RvalueFunc::Into)),
|
||||||
|
|
||||||
|
ExprKind::Vec { .. } |
|
||||||
|
ExprKind::Tuple { .. } |
|
||||||
|
ExprKind::Adt { .. } |
|
||||||
|
ExprKind::Closure { .. } |
|
||||||
|
ExprKind::Unary { .. } |
|
||||||
|
ExprKind::Binary { .. } |
|
||||||
|
ExprKind::Box { .. } |
|
||||||
|
ExprKind::Cast { .. } |
|
||||||
|
ExprKind::ReifyFnPointer { .. } |
|
||||||
|
ExprKind::UnsafeFnPointer { .. } |
|
||||||
|
ExprKind::Unsize { .. } |
|
||||||
|
ExprKind::Repeat { .. } |
|
||||||
|
ExprKind::Borrow { .. } |
|
||||||
|
ExprKind::Assign { .. } |
|
||||||
|
ExprKind::AssignOp { .. } |
|
||||||
|
ExprKind::InlineAsm { .. } =>
|
||||||
|
Some(Category::Rvalue(RvalueFunc::AsRvalue)),
|
||||||
|
|
||||||
|
ExprKind::Literal { .. } =>
|
||||||
|
Some(Category::Constant),
|
||||||
|
|
||||||
|
ExprKind::Loop { .. } |
|
||||||
|
ExprKind::Block { .. } |
|
||||||
|
ExprKind::Break { .. } |
|
||||||
|
ExprKind::Continue { .. } |
|
||||||
|
ExprKind::Return { .. } =>
|
||||||
|
Some(Category::Rvalue(RvalueFunc::Into)), // TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,282 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! See docs in build/expr/mod.rs
|
||||||
|
|
||||||
|
use build::{BlockAnd, Builder};
|
||||||
|
use build::expr::category::{Category, RvalueFunc};
|
||||||
|
use build::scope::LoopScope;
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
/// Compile `expr`, storing the result into `destination`, which
|
||||||
|
/// is assumed to be uninitialized.
|
||||||
|
pub fn into_expr(&mut self,
|
||||||
|
destination: &Lvalue<H>,
|
||||||
|
mut block: BasicBlock,
|
||||||
|
expr: Expr<H>)
|
||||||
|
-> BlockAnd<()>
|
||||||
|
{
|
||||||
|
debug!("into_expr(destination={:?}, block={:?}, expr={:?})",
|
||||||
|
destination, block, expr);
|
||||||
|
|
||||||
|
// since we frequently have to reference `self` from within a
|
||||||
|
// closure, where `self` would be shadowed, it's easier to
|
||||||
|
// just use the name `this` uniformly
|
||||||
|
let this = self;
|
||||||
|
let expr_span = expr.span;
|
||||||
|
|
||||||
|
match expr.kind {
|
||||||
|
ExprKind::Scope { extent, value } => {
|
||||||
|
this.in_scope(extent, block, |this| {
|
||||||
|
this.into(destination, block, value)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ExprKind::Paren { arg } => {
|
||||||
|
this.into(destination, block, arg)
|
||||||
|
}
|
||||||
|
ExprKind::Block { body: ast_block } => {
|
||||||
|
this.ast_block(destination, block, ast_block)
|
||||||
|
}
|
||||||
|
ExprKind::Match { discriminant, arms } => {
|
||||||
|
this.match_expr(destination, expr_span, block, discriminant, arms)
|
||||||
|
}
|
||||||
|
ExprKind::If { condition: cond_expr, then: then_expr, otherwise: else_expr } => {
|
||||||
|
let operand = unpack!(block = this.as_operand(block, cond_expr));
|
||||||
|
|
||||||
|
let mut then_block = this.cfg.start_new_block();
|
||||||
|
let mut else_block = this.cfg.start_new_block();
|
||||||
|
this.cfg.terminate(block, Terminator::If {
|
||||||
|
cond: operand,
|
||||||
|
targets: [then_block, else_block]
|
||||||
|
});
|
||||||
|
|
||||||
|
unpack!(then_block = this.into(destination, then_block, then_expr));
|
||||||
|
unpack!(else_block = this.into(destination, else_block, else_expr));
|
||||||
|
|
||||||
|
let join_block = this.cfg.start_new_block();
|
||||||
|
this.cfg.terminate(then_block, Terminator::Goto { target: join_block });
|
||||||
|
this.cfg.terminate(else_block, Terminator::Goto { target: join_block });
|
||||||
|
|
||||||
|
join_block.unit()
|
||||||
|
}
|
||||||
|
ExprKind::LogicalOp { op, lhs, rhs } => {
|
||||||
|
// And:
|
||||||
|
//
|
||||||
|
// [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
|
||||||
|
// | | (false)
|
||||||
|
// +----------false-----------+------------------> [false_block]
|
||||||
|
//
|
||||||
|
// Or:
|
||||||
|
//
|
||||||
|
// [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
|
||||||
|
// | | (false)
|
||||||
|
// +----------true------------+-------------------> [false_block]
|
||||||
|
|
||||||
|
let (true_block, false_block, mut else_block, join_block) =
|
||||||
|
(this.cfg.start_new_block(), this.cfg.start_new_block(),
|
||||||
|
this.cfg.start_new_block(), this.cfg.start_new_block());
|
||||||
|
|
||||||
|
let lhs = unpack!(block = this.as_operand(block, lhs));
|
||||||
|
let blocks = match op {
|
||||||
|
LogicalOp::And => [else_block, false_block],
|
||||||
|
LogicalOp::Or => [true_block, else_block],
|
||||||
|
};
|
||||||
|
this.cfg.terminate(block, Terminator::If { cond: lhs, targets: blocks });
|
||||||
|
|
||||||
|
let rhs = unpack!(else_block = this.as_operand(else_block, rhs));
|
||||||
|
this.cfg.terminate(else_block, Terminator::If {
|
||||||
|
cond: rhs,
|
||||||
|
targets: [true_block, false_block]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.cfg.push_assign_constant(
|
||||||
|
true_block, expr_span, destination,
|
||||||
|
Constant {
|
||||||
|
span: expr_span,
|
||||||
|
kind: ConstantKind::Literal(Literal::Bool { value: true }),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.cfg.push_assign_constant(
|
||||||
|
false_block, expr_span, destination,
|
||||||
|
Constant {
|
||||||
|
span: expr_span,
|
||||||
|
kind: ConstantKind::Literal(Literal::Bool { value: false }),
|
||||||
|
});
|
||||||
|
|
||||||
|
this.cfg.terminate(true_block, Terminator::Goto { target: join_block });
|
||||||
|
this.cfg.terminate(false_block, Terminator::Goto { target: join_block });
|
||||||
|
|
||||||
|
join_block.unit()
|
||||||
|
}
|
||||||
|
ExprKind::Loop { condition: opt_cond_expr, body } => {
|
||||||
|
// [block] --> [loop_block] ~~> [loop_block_end] -1-> [exit_block]
|
||||||
|
// ^ |
|
||||||
|
// | 0
|
||||||
|
// | |
|
||||||
|
// | v
|
||||||
|
// [body_block_end] <~~~ [body_block]
|
||||||
|
//
|
||||||
|
// If `opt_cond_expr` is `None`, then the graph is somewhat simplified:
|
||||||
|
//
|
||||||
|
// [block] --> [loop_block / body_block ] ~~> [body_block_end] [exit_block]
|
||||||
|
// ^ |
|
||||||
|
// | |
|
||||||
|
// +--------------------------+
|
||||||
|
//
|
||||||
|
|
||||||
|
let loop_block = this.cfg.start_new_block();
|
||||||
|
let exit_block = this.cfg.start_new_block();
|
||||||
|
|
||||||
|
// start the loop
|
||||||
|
this.cfg.terminate(block, Terminator::Goto { target: loop_block });
|
||||||
|
|
||||||
|
this.in_loop_scope(loop_block, exit_block, |this| {
|
||||||
|
// conduct the test, if necessary
|
||||||
|
let body_block;
|
||||||
|
let opt_cond_expr = opt_cond_expr; // FIXME rustc bug
|
||||||
|
if let Some(cond_expr) = opt_cond_expr {
|
||||||
|
let loop_block_end;
|
||||||
|
let cond = unpack!(loop_block_end = this.as_operand(loop_block, cond_expr));
|
||||||
|
body_block = this.cfg.start_new_block();
|
||||||
|
this.cfg.terminate(loop_block_end,
|
||||||
|
Terminator::If {
|
||||||
|
cond: cond,
|
||||||
|
targets: [body_block, exit_block]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
body_block = loop_block;
|
||||||
|
}
|
||||||
|
|
||||||
|
// execute the body, branching back to the test
|
||||||
|
let unit_temp = this.unit_temp.clone();
|
||||||
|
let body_block_end = unpack!(this.into(&unit_temp, body_block, body));
|
||||||
|
this.cfg.terminate(body_block_end, Terminator::Goto { target: loop_block });
|
||||||
|
|
||||||
|
// final point is exit_block
|
||||||
|
exit_block.unit()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ExprKind::Assign { lhs, rhs } => {
|
||||||
|
// Note: we evaluate assignments right-to-left. This
|
||||||
|
// is better for borrowck interaction with overloaded
|
||||||
|
// operators like x[j] = x[i].
|
||||||
|
let rhs = unpack!(block = this.as_operand(block, rhs));
|
||||||
|
let lhs = unpack!(block = this.as_lvalue(block, lhs));
|
||||||
|
this.cfg.push_drop(block, expr_span, DropKind::Deep, &lhs);
|
||||||
|
this.cfg.push_assign(block, expr_span, &lhs, Rvalue::Use(rhs));
|
||||||
|
block.unit()
|
||||||
|
}
|
||||||
|
ExprKind::AssignOp { op, lhs, rhs } => {
|
||||||
|
// FIXME(#28160) there is an interesting semantics
|
||||||
|
// question raised here -- should we "freeze" the
|
||||||
|
// value of the lhs here? I'm inclined to think not,
|
||||||
|
// since it seems closer to the semantics of the
|
||||||
|
// overloaded version, which takes `&mut self`. This
|
||||||
|
// only affects weird things like `x += {x += 1; x}`
|
||||||
|
// -- is that equal to `x + (x + 1)` or `2*(x+1)`?
|
||||||
|
|
||||||
|
// As above, RTL.
|
||||||
|
let rhs = unpack!(block = this.as_operand(block, rhs));
|
||||||
|
let lhs = unpack!(block = this.as_lvalue(block, lhs));
|
||||||
|
|
||||||
|
// we don't have to drop prior contents or anything
|
||||||
|
// because AssignOp is only legal for Copy types
|
||||||
|
// (overloaded ops should be desugared into a call).
|
||||||
|
this.cfg.push_assign(block, expr_span, &lhs,
|
||||||
|
Rvalue::BinaryOp(op,
|
||||||
|
Operand::Consume(lhs.clone()),
|
||||||
|
rhs));
|
||||||
|
|
||||||
|
block.unit()
|
||||||
|
}
|
||||||
|
ExprKind::Continue { label } => {
|
||||||
|
this.break_or_continue(expr_span, label, block,
|
||||||
|
|loop_scope| loop_scope.continue_block)
|
||||||
|
}
|
||||||
|
ExprKind::Break { label } => {
|
||||||
|
this.break_or_continue(expr_span, label, block,
|
||||||
|
|loop_scope| loop_scope.break_block)
|
||||||
|
}
|
||||||
|
ExprKind::Return { value } => {
|
||||||
|
unpack!(block = this.into(&Lvalue::ReturnPointer, block, value));
|
||||||
|
let extent = this.extent_of_outermost_scope().unwrap();
|
||||||
|
this.exit_scope(expr_span, extent, block, END_BLOCK);
|
||||||
|
this.cfg.start_new_block().unit()
|
||||||
|
}
|
||||||
|
ExprKind::Call { fun, args } => {
|
||||||
|
let fun = unpack!(block = this.as_lvalue(block, fun));
|
||||||
|
let args: Vec<_> =
|
||||||
|
args.into_iter()
|
||||||
|
.map(|arg| unpack!(block = this.as_lvalue(block, arg)))
|
||||||
|
.collect();
|
||||||
|
let success = this.cfg.start_new_block();
|
||||||
|
let panic = this.diverge_cleanup();
|
||||||
|
this.cfg.terminate(block,
|
||||||
|
Terminator::Call {
|
||||||
|
data: CallData {
|
||||||
|
destination: destination.clone(),
|
||||||
|
func: fun,
|
||||||
|
args: args
|
||||||
|
},
|
||||||
|
targets: [success, panic]
|
||||||
|
});
|
||||||
|
success.unit()
|
||||||
|
}
|
||||||
|
|
||||||
|
// these are the cases that are more naturally handled by some other mode
|
||||||
|
ExprKind::Unary { .. } |
|
||||||
|
ExprKind::Binary { .. } |
|
||||||
|
ExprKind::Box { .. } |
|
||||||
|
ExprKind::Cast { .. } |
|
||||||
|
ExprKind::ReifyFnPointer { .. } |
|
||||||
|
ExprKind::UnsafeFnPointer { .. } |
|
||||||
|
ExprKind::Unsize { .. } |
|
||||||
|
ExprKind::Repeat { .. } |
|
||||||
|
ExprKind::Borrow { .. } |
|
||||||
|
ExprKind::VarRef { .. } |
|
||||||
|
ExprKind::SelfRef |
|
||||||
|
ExprKind::StaticRef { .. } |
|
||||||
|
ExprKind::Vec { .. } |
|
||||||
|
ExprKind::Tuple { .. } |
|
||||||
|
ExprKind::Adt { .. } |
|
||||||
|
ExprKind::Closure { .. } |
|
||||||
|
ExprKind::Index { .. } |
|
||||||
|
ExprKind::Deref { .. } |
|
||||||
|
ExprKind::Literal { .. } |
|
||||||
|
ExprKind::InlineAsm { .. } |
|
||||||
|
ExprKind::Field { .. } => {
|
||||||
|
debug_assert!(match Category::of(&expr.kind).unwrap() {
|
||||||
|
Category::Rvalue(RvalueFunc::Into) => false,
|
||||||
|
_ => true,
|
||||||
|
});
|
||||||
|
|
||||||
|
let rvalue = unpack!(block = this.as_rvalue(block, expr));
|
||||||
|
this.cfg.push_assign(block, expr_span, destination, rvalue);
|
||||||
|
block.unit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn break_or_continue<F>(&mut self,
|
||||||
|
span: H::Span,
|
||||||
|
label: Option<H::CodeExtent>,
|
||||||
|
block: BasicBlock,
|
||||||
|
exit_selector: F)
|
||||||
|
-> BlockAnd<()>
|
||||||
|
where F: FnOnce(&LoopScope<H>) -> BasicBlock
|
||||||
|
{
|
||||||
|
let loop_scope = self.find_loop_scope(span, label);
|
||||||
|
let exit_block = exit_selector(&loop_scope);
|
||||||
|
self.exit_scope(span, loop_scope.extent, block, exit_block);
|
||||||
|
self.cfg.start_new_block().unit()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Translates expressions into MIR. As a caller into this module, you
|
||||||
|
//! have many options, but the first thing you have to decide is
|
||||||
|
//! whether you are evaluating this expression for its *value*, its
|
||||||
|
//! *location*, or as a *constant*.
|
||||||
|
//!
|
||||||
|
//! Typically, you want the value: e.g., if you are doing `expr_a +
|
||||||
|
//! expr_b`, you want the values of those expressions. In that case,
|
||||||
|
//! you want one of the following functions. Note that if the expr has
|
||||||
|
//! a type that is not `Copy`, then using any of these functions will
|
||||||
|
//! "move" the value out of its current home (if any).
|
||||||
|
//!
|
||||||
|
//! - `into` -- writes the value into a specific location, which
|
||||||
|
//! should be uninitialized
|
||||||
|
//! - `as_operand` -- evaluates the value and yields an `Operand`,
|
||||||
|
//! suitable for use as an argument to an `Rvalue`
|
||||||
|
//! - `as_temp` -- evaluates into a temporary; this is similar to `as_operand`
|
||||||
|
//! except it always returns a fresh lvalue, even for constants
|
||||||
|
//! - `as_rvalue` -- yields an `Rvalue`, suitable for use in an assignment;
|
||||||
|
//! as of this writing, never needed outside of the `expr` module itself
|
||||||
|
//!
|
||||||
|
//! Sometimes though want the expression's *location*. An example
|
||||||
|
//! would be during a match statement, or the operand of the `&`
|
||||||
|
//! operator. In that case, you want `as_lvalue`. This will create a
|
||||||
|
//! temporary if necessary.
|
||||||
|
//!
|
||||||
|
//! Finally, if it's a constant you seek, then call
|
||||||
|
//! `as_constant`. This creates a `Constant<H>`, but naturally it can
|
||||||
|
//! only be used on constant expressions and hence is needed only in
|
||||||
|
//! very limited contexts.
|
||||||
|
//!
|
||||||
|
//! ### Implementation notes
|
||||||
|
//!
|
||||||
|
//! For any given kind of expression, there is generally one way that
|
||||||
|
//! can be translated most naturally. This is specified by the
|
||||||
|
//! `Category::of` function in the `category` module. For example, a
|
||||||
|
//! struct expression (or other expression that creates a new value)
|
||||||
|
//! is typically easiest to write in terms of `as_rvalue` or `into`,
|
||||||
|
//! whereas a reference to a field is easiest to write in terms of
|
||||||
|
//! `as_lvalue`. (The exception to this is scope and paren
|
||||||
|
//! expressions, which have no category.)
|
||||||
|
//!
|
||||||
|
//! Therefore, the various functions above make use of one another in
|
||||||
|
//! a descending fashion. For any given expression, you should pick
|
||||||
|
//! the most suitable spot to implement it, and then just let the
|
||||||
|
//! other fns cycle around. The handoff works like this:
|
||||||
|
//!
|
||||||
|
//! - `into(lv)` -> fallback is to create a rvalue with `as_rvalue` and assign it to `lv`
|
||||||
|
//! - `as_rvalue` -> fallback is to create an Operand with `as_operand` and use `Rvalue::use`
|
||||||
|
//! - `as_operand` -> either invokes `as_constant` or `as_temp`
|
||||||
|
//! - `as_constant` -> (no fallback)
|
||||||
|
//! - `as_temp` -> creates a temporary and either calls `as_lvalue` or `into`
|
||||||
|
//! - `as_lvalue` -> for rvalues, falls back to `as_temp` and returns that
|
||||||
|
//!
|
||||||
|
//! As you can see, there is a cycle where `into` can (in theory) fallback to `as_temp`
|
||||||
|
//! which can fallback to `into`. So if one of the `ExprKind` variants is not, in fact,
|
||||||
|
//! implemented in the category where it is supposed to be, there will be a problem.
|
||||||
|
//!
|
||||||
|
//! Of those fallbacks, the most interesting one is `as_temp`, because
|
||||||
|
//! it discriminates based on the category of the expression. This is
|
||||||
|
//! basically the point where the "by value" operations are bridged
|
||||||
|
//! over to the "by reference" mode (`as_lvalue`).
|
||||||
|
|
||||||
|
mod as_constant;
|
||||||
|
mod as_lvalue;
|
||||||
|
mod as_rvalue;
|
||||||
|
mod as_operand;
|
||||||
|
mod as_temp;
|
||||||
|
mod category;
|
||||||
|
mod into;
|
|
@ -0,0 +1,70 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! In general, there are a number of things for which it's convenient
|
||||||
|
//! to just call `builder.into` and have it emit its result into a
|
||||||
|
//! given location. This is basically for expressions or things that can be
|
||||||
|
//! wrapped up as expressions (e.g. blocks). To make this ergonomic, we use this
|
||||||
|
//! latter `EvalInto` trait.
|
||||||
|
|
||||||
|
use build::{BlockAnd, Builder};
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
pub trait EvalInto<H:Hair> {
|
||||||
|
fn eval_into(self, builder: &mut Builder<H>, destination: &Lvalue<H>,
|
||||||
|
block: BasicBlock) -> BlockAnd<()>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
pub fn into<E>(&mut self,
|
||||||
|
destination: &Lvalue<H>,
|
||||||
|
block: BasicBlock,
|
||||||
|
expr: E)
|
||||||
|
-> BlockAnd<()>
|
||||||
|
where E: EvalInto<H>
|
||||||
|
{
|
||||||
|
expr.eval_into(self, destination, block)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> EvalInto<H> for ExprRef<H> {
|
||||||
|
fn eval_into(self,
|
||||||
|
builder: &mut Builder<H>,
|
||||||
|
destination: &Lvalue<H>,
|
||||||
|
block: BasicBlock)
|
||||||
|
-> BlockAnd<()> {
|
||||||
|
let expr = builder.hir.mirror(self);
|
||||||
|
builder.into_expr(destination, block, expr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> EvalInto<H> for Expr<H> {
|
||||||
|
fn eval_into(self,
|
||||||
|
builder: &mut Builder<H>,
|
||||||
|
destination: &Lvalue<H>,
|
||||||
|
block: BasicBlock)
|
||||||
|
-> BlockAnd<()> {
|
||||||
|
builder.into_expr(destination, block, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> EvalInto<H> for Option<ExprRef<H>> {
|
||||||
|
fn eval_into(self,
|
||||||
|
builder: &mut Builder<H>,
|
||||||
|
destination: &Lvalue<H>,
|
||||||
|
block: BasicBlock)
|
||||||
|
-> BlockAnd<()> {
|
||||||
|
match self {
|
||||||
|
Some(expr) => builder.into(destination, block, expr),
|
||||||
|
None => block.unit()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,409 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Code related to match expresions. These are sufficiently complex
|
||||||
|
//! to warrant their own module and submodules. :) This main module
|
||||||
|
//! includes the high-level algorithm, the submodules contain the
|
||||||
|
//! details.
|
||||||
|
|
||||||
|
use build::{BlockAnd, Builder};
|
||||||
|
use repr::*;
|
||||||
|
use hair::*;
|
||||||
|
|
||||||
|
// helper functions, broken out by category:
|
||||||
|
mod simplify;
|
||||||
|
mod test;
|
||||||
|
mod util;
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
pub fn match_expr(&mut self,
|
||||||
|
destination: &Lvalue<H>,
|
||||||
|
span: H::Span,
|
||||||
|
mut block: BasicBlock,
|
||||||
|
discriminant: ExprRef<H>,
|
||||||
|
arms: Vec<Arm<H>>)
|
||||||
|
-> BlockAnd<()>
|
||||||
|
{
|
||||||
|
let discriminant_lvalue =
|
||||||
|
unpack!(block = self.as_lvalue(block, discriminant));
|
||||||
|
|
||||||
|
let arm_blocks: Vec<BasicBlock> =
|
||||||
|
arms.iter()
|
||||||
|
.map(|_| self.cfg.start_new_block())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let arm_bodies: Vec<ExprRef<H>> =
|
||||||
|
arms.iter()
|
||||||
|
.map(|arm| arm.body.clone())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// assemble a list of candidates: there is one candidate per
|
||||||
|
// pattern, which means there may be more than one candidate
|
||||||
|
// *per arm*. These candidates are kept sorted such that the
|
||||||
|
// highest priority candidate comes last in the list. This the
|
||||||
|
// reverse of the order in which candidates are written in the
|
||||||
|
// source.
|
||||||
|
let candidates: Vec<Candidate<H>> =
|
||||||
|
arms.into_iter()
|
||||||
|
.zip(arm_blocks.iter())
|
||||||
|
.rev() // highest priority comes last
|
||||||
|
.flat_map(|(arm, &arm_block)| {
|
||||||
|
let guard = arm.guard;
|
||||||
|
arm.patterns.into_iter()
|
||||||
|
.rev()
|
||||||
|
.map(move |pat| (arm_block, pat, guard.clone()))
|
||||||
|
})
|
||||||
|
.map(|(arm_block, pattern, guard)| {
|
||||||
|
Candidate {
|
||||||
|
match_pairs: vec![self.match_pair(discriminant_lvalue.clone(), pattern)],
|
||||||
|
bindings: vec![],
|
||||||
|
guard: guard,
|
||||||
|
arm_block: arm_block,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
// this will generate code to test discriminant_lvalue and
|
||||||
|
// branch to the appropriate arm block
|
||||||
|
let var_extent = self.extent_of_innermost_scope().unwrap();
|
||||||
|
self.match_candidates(span, var_extent, candidates, block);
|
||||||
|
|
||||||
|
// all the arm blocks will rejoin here
|
||||||
|
let end_block = self.cfg.start_new_block();
|
||||||
|
|
||||||
|
for (arm_body, &arm_block) in arm_bodies.into_iter().zip(arm_blocks.iter()) {
|
||||||
|
let mut arm_block = arm_block;
|
||||||
|
unpack!(arm_block = self.into(destination, arm_block, arm_body));
|
||||||
|
self.cfg.terminate(arm_block, Terminator::Goto { target: end_block });
|
||||||
|
}
|
||||||
|
|
||||||
|
end_block.unit()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn expr_into_pattern(&mut self,
|
||||||
|
mut block: BasicBlock,
|
||||||
|
var_extent: H::CodeExtent, // lifetime of vars
|
||||||
|
irrefutable_pat: PatternRef<H>,
|
||||||
|
initializer: ExprRef<H>)
|
||||||
|
-> BlockAnd<()>
|
||||||
|
{
|
||||||
|
// optimize the case of `let x = ...`
|
||||||
|
let irrefutable_pat = self.hir.mirror(irrefutable_pat);
|
||||||
|
match irrefutable_pat.kind {
|
||||||
|
PatternKind::Binding { mutability,
|
||||||
|
name,
|
||||||
|
mode: BindingMode::ByValue,
|
||||||
|
var,
|
||||||
|
ty,
|
||||||
|
subpattern: None } => {
|
||||||
|
let index = self.declare_binding(var_extent, mutability, name,
|
||||||
|
var, ty, irrefutable_pat.span);
|
||||||
|
let lvalue = Lvalue::Var(index);
|
||||||
|
return self.into(&lvalue, block, initializer);
|
||||||
|
}
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
let lvalue = unpack!(block = self.as_lvalue(block, initializer));
|
||||||
|
self.lvalue_into_pattern(block, var_extent,
|
||||||
|
PatternRef::Mirror(Box::new(irrefutable_pat)), &lvalue)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn lvalue_into_pattern(&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
var_extent: H::CodeExtent,
|
||||||
|
irrefutable_pat: PatternRef<H>,
|
||||||
|
initializer: &Lvalue<H>)
|
||||||
|
-> BlockAnd<()>
|
||||||
|
{
|
||||||
|
// create a dummy candidate
|
||||||
|
let mut candidate = Candidate::<H> {
|
||||||
|
match_pairs: vec![self.match_pair(initializer.clone(), irrefutable_pat)],
|
||||||
|
bindings: vec![],
|
||||||
|
guard: None,
|
||||||
|
arm_block: block
|
||||||
|
};
|
||||||
|
|
||||||
|
// Simplify the candidate. Since the pattern is irrefutable, this should
|
||||||
|
// always convert all match-pairs into bindings.
|
||||||
|
self.simplify_candidate(&mut candidate);
|
||||||
|
|
||||||
|
if !candidate.match_pairs.is_empty() {
|
||||||
|
self.hir.span_bug(
|
||||||
|
candidate.match_pairs[0].pattern.span,
|
||||||
|
&format!("match pairs {:?} remaining after simplifying irrefutable pattern",
|
||||||
|
candidate.match_pairs));
|
||||||
|
}
|
||||||
|
|
||||||
|
// now apply the bindings, which will also declare the variables
|
||||||
|
self.bind_matched_candidate(block, var_extent, candidate.bindings);
|
||||||
|
|
||||||
|
block.unit()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn declare_uninitialized_variables(&mut self,
|
||||||
|
var_extent: H::CodeExtent,
|
||||||
|
pattern: PatternRef<H>)
|
||||||
|
{
|
||||||
|
let pattern = self.hir.mirror(pattern);
|
||||||
|
match pattern.kind {
|
||||||
|
PatternKind::Binding { mutability, name, mode: _, var, ty, subpattern } => {
|
||||||
|
self.declare_binding(var_extent, mutability, name, var, ty, pattern.span);
|
||||||
|
if let Some(subpattern) = subpattern {
|
||||||
|
self.declare_uninitialized_variables(var_extent, subpattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PatternKind::Array { prefix, slice, suffix } |
|
||||||
|
PatternKind::Slice { prefix, slice, suffix } => {
|
||||||
|
for subpattern in prefix.into_iter().chain(slice).chain(suffix) {
|
||||||
|
self.declare_uninitialized_variables(var_extent, subpattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PatternKind::Constant { .. } | PatternKind::Range { .. } | PatternKind::Wild => {
|
||||||
|
}
|
||||||
|
PatternKind::Deref { subpattern } => {
|
||||||
|
self.declare_uninitialized_variables(var_extent, subpattern);
|
||||||
|
}
|
||||||
|
PatternKind::Leaf { subpatterns } |
|
||||||
|
PatternKind::Variant { subpatterns, .. } => {
|
||||||
|
for subpattern in subpatterns {
|
||||||
|
self.declare_uninitialized_variables(var_extent, subpattern.pattern);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct Candidate<H:Hair> {
|
||||||
|
// all of these must be satisfied...
|
||||||
|
match_pairs: Vec<MatchPair<H>>,
|
||||||
|
|
||||||
|
// ...these bindings established...
|
||||||
|
bindings: Vec<Binding<H>>,
|
||||||
|
|
||||||
|
// ...and the guard must be evaluated...
|
||||||
|
guard: Option<ExprRef<H>>,
|
||||||
|
|
||||||
|
// ...and then we branch here.
|
||||||
|
arm_block: BasicBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct Binding<H:Hair> {
|
||||||
|
span: H::Span,
|
||||||
|
source: Lvalue<H>,
|
||||||
|
name: H::Ident,
|
||||||
|
var_id: H::VarId,
|
||||||
|
var_ty: H::Ty,
|
||||||
|
mutability: Mutability,
|
||||||
|
binding_mode: BindingMode<H>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct MatchPair<H:Hair> {
|
||||||
|
// this lvalue...
|
||||||
|
lvalue: Lvalue<H>,
|
||||||
|
|
||||||
|
// ... must match this pattern.
|
||||||
|
pattern: Pattern<H>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
enum TestKind<H:Hair> {
|
||||||
|
// test the branches of enum
|
||||||
|
Switch { adt_def: H::AdtDef },
|
||||||
|
|
||||||
|
// test for equality
|
||||||
|
Eq { value: Constant<H>, ty: H::Ty },
|
||||||
|
|
||||||
|
// test whether the value falls within an inclusive range
|
||||||
|
Range { lo: Constant<H>, hi: Constant<H>, ty: H::Ty },
|
||||||
|
|
||||||
|
// test length of the slice is equal to len
|
||||||
|
Len { len: usize, op: BinOp },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct Test<H:Hair> {
|
||||||
|
span: H::Span,
|
||||||
|
|
||||||
|
// the kind of test to be performed,
|
||||||
|
kind: TestKind<H>,
|
||||||
|
|
||||||
|
// the outcome we expect,
|
||||||
|
outcome: usize,
|
||||||
|
|
||||||
|
// and the match pairs that will result
|
||||||
|
match_pairs: Vec<MatchPair<H>>
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Main matching algorithm
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
fn match_candidates(&mut self,
|
||||||
|
span: H::Span,
|
||||||
|
var_extent: H::CodeExtent,
|
||||||
|
mut candidates: Vec<Candidate<H>>,
|
||||||
|
mut block: BasicBlock)
|
||||||
|
{
|
||||||
|
debug!("matched_candidate(span={:?}, var_extent={:?}, block={:?}, candidates={:?})",
|
||||||
|
span, var_extent, block, candidates);
|
||||||
|
|
||||||
|
// Start by simplifying candidates. Once this process is
|
||||||
|
// complete, all the match pairs which remain require some
|
||||||
|
// form of test, whether it be a switch or pattern comparison.
|
||||||
|
for candidate in &mut candidates {
|
||||||
|
self.simplify_candidate(candidate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The candidates are inversely sorted by priority. Check to
|
||||||
|
// see whether the candidates in the front of the queue (and
|
||||||
|
// hence back of the vec) have satisfied all their match
|
||||||
|
// pairs.
|
||||||
|
let fully_matched =
|
||||||
|
candidates.iter().rev().take_while(|c| c.match_pairs.is_empty()).count();
|
||||||
|
debug!("match_candidates: {:?} candidates fully matched", fully_matched);
|
||||||
|
for _ in 0..fully_matched {
|
||||||
|
// If so, apply any bindings, test the guard (if any), and
|
||||||
|
// branch to the arm.
|
||||||
|
let candidate = candidates.pop().unwrap();
|
||||||
|
match self.bind_and_guard_matched_candidate(block, var_extent, candidate) {
|
||||||
|
None => { return; }
|
||||||
|
Some(b) => { block = b; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there are no candidates that still need testing, we're done.
|
||||||
|
// Since all matches are exhaustive, execution should never reach this point.
|
||||||
|
if candidates.is_empty() {
|
||||||
|
return self.panic(block);
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, extract the next match pair and construct tests
|
||||||
|
let match_pair = &candidates.last().unwrap().match_pairs[0];
|
||||||
|
let test = self.test(match_pair);
|
||||||
|
debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair);
|
||||||
|
let target_blocks = self.perform_test(block, &match_pair.lvalue, &test);
|
||||||
|
|
||||||
|
for (outcome, target_block) in target_blocks.into_iter().enumerate() {
|
||||||
|
let applicable_candidates: Vec<Candidate<H>> =
|
||||||
|
candidates.iter()
|
||||||
|
.filter_map(|candidate| {
|
||||||
|
self.candidate_under_assumption(&match_pair.lvalue,
|
||||||
|
&test.kind,
|
||||||
|
outcome,
|
||||||
|
candidate)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
self.match_candidates(span, var_extent, applicable_candidates, target_block);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initializes each of the bindings from the candidate by
|
||||||
|
/// moving/copying/ref'ing the source as appropriate. Tests the
|
||||||
|
/// guard, if any, and then branches to the arm. Returns the block
|
||||||
|
/// for the case where the guard fails.
|
||||||
|
///
|
||||||
|
/// Note: we check earlier that if there is a guard, there cannot
|
||||||
|
/// be move bindings. This isn't really important for the
|
||||||
|
/// self-consistency of this fn, but the reason for it should be
|
||||||
|
/// clear: after we've done the assignments, if there were move
|
||||||
|
/// bindings, further tests would be a use-after-move (which would
|
||||||
|
/// in turn be detected by the borrowck code that runs on the
|
||||||
|
/// MIR).
|
||||||
|
fn bind_and_guard_matched_candidate(&mut self,
|
||||||
|
mut block: BasicBlock,
|
||||||
|
var_extent: H::CodeExtent,
|
||||||
|
candidate: Candidate<H>)
|
||||||
|
-> Option<BasicBlock> {
|
||||||
|
debug!("bind_and_guard_matched_candidate(block={:?}, var_extent={:?}, candidate={:?})",
|
||||||
|
block, var_extent, candidate);
|
||||||
|
|
||||||
|
debug_assert!(candidate.match_pairs.is_empty());
|
||||||
|
|
||||||
|
self.bind_matched_candidate(block, var_extent, candidate.bindings);
|
||||||
|
|
||||||
|
if let Some(guard) = candidate.guard {
|
||||||
|
// the block to branch to if the guard fails; if there is no
|
||||||
|
// guard, this block is simply unreachable
|
||||||
|
let cond = unpack!(block = self.as_operand(block, guard));
|
||||||
|
let otherwise = self.cfg.start_new_block();
|
||||||
|
self.cfg.terminate(block, Terminator::If { cond: cond,
|
||||||
|
targets: [candidate.arm_block, otherwise]});
|
||||||
|
Some(otherwise)
|
||||||
|
} else {
|
||||||
|
self.cfg.terminate(block, Terminator::Goto { target: candidate.arm_block });
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bind_matched_candidate(&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
var_extent: H::CodeExtent,
|
||||||
|
bindings: Vec<Binding<H>>) {
|
||||||
|
debug!("bind_matched_candidate(block={:?}, var_extent={:?}, bindings={:?})",
|
||||||
|
block, var_extent, bindings);
|
||||||
|
|
||||||
|
// Assign each of the bindings. This may trigger moves out of the candidate.
|
||||||
|
for binding in bindings {
|
||||||
|
// Create a variable for the `var_id` being bound. In the
|
||||||
|
// case where there are multiple patterns for a single
|
||||||
|
// arm, it may already exist.
|
||||||
|
let var_index = if !self.var_indices.contains_key(&binding.var_id) {
|
||||||
|
self.declare_binding(var_extent,
|
||||||
|
binding.mutability,
|
||||||
|
binding.name,
|
||||||
|
binding.var_id,
|
||||||
|
binding.var_ty,
|
||||||
|
binding.span)
|
||||||
|
} else {
|
||||||
|
self.var_indices[&binding.var_id]
|
||||||
|
};
|
||||||
|
|
||||||
|
let rvalue = match binding.binding_mode {
|
||||||
|
BindingMode::ByValue =>
|
||||||
|
Rvalue::Use(Operand::Consume(binding.source)),
|
||||||
|
BindingMode::ByRef(region, borrow_kind) =>
|
||||||
|
Rvalue::Ref(region, borrow_kind, binding.source),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.cfg.push_assign(block, binding.span, &Lvalue::Var(var_index), rvalue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn declare_binding(&mut self,
|
||||||
|
var_extent: H::CodeExtent,
|
||||||
|
mutability: Mutability,
|
||||||
|
name: H::Ident,
|
||||||
|
var_id: H::VarId,
|
||||||
|
var_ty: H::Ty,
|
||||||
|
span: H::Span)
|
||||||
|
-> u32
|
||||||
|
{
|
||||||
|
debug!("declare_binding(var_id={:?}, name={:?}, var_ty={:?}, var_extent={:?}, span={:?})",
|
||||||
|
var_id, name, var_ty, var_extent, span);
|
||||||
|
|
||||||
|
let index = self.var_decls.len();
|
||||||
|
self.var_decls.push(VarDecl::<H> {
|
||||||
|
mutability: mutability,
|
||||||
|
name: name,
|
||||||
|
ty: var_ty.clone(),
|
||||||
|
});
|
||||||
|
let index = index as u32;
|
||||||
|
self.schedule_drop(span, var_extent, DropKind::Deep, &Lvalue::Var(index), var_ty);
|
||||||
|
self.var_indices.insert(var_id, index);
|
||||||
|
|
||||||
|
debug!("declare_binding: index={:?}", index);
|
||||||
|
|
||||||
|
index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,127 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Simplifying Candidates
|
||||||
|
//!
|
||||||
|
//! *Simplifying* a match pair `lvalue @ pattern` means breaking it down
|
||||||
|
//! into bindings or other, simpler match pairs. For example:
|
||||||
|
//!
|
||||||
|
//! - `lvalue @ (P1, P2)` can be simplified to `[lvalue.0 @ P1, lvalue.1 @ P2]`
|
||||||
|
//! - `lvalue @ x` can be simplified to `[]` by binding `x` to `lvalue`
|
||||||
|
//!
|
||||||
|
//! The `simplify_candidate` routine just repeatedly applies these
|
||||||
|
//! sort of simplifications until there is nothing left to
|
||||||
|
//! simplify. Match pairs cannot be simplified if they require some
|
||||||
|
//! sort of test: for example, testing which variant an enum is, or
|
||||||
|
//! testing a value against a constant.
|
||||||
|
|
||||||
|
use build::Builder;
|
||||||
|
use build::matches::{Binding, MatchPair, Candidate};
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
pub fn simplify_candidate(&mut self,
|
||||||
|
candidate: &mut Candidate<H>)
|
||||||
|
{
|
||||||
|
// repeatedly simplify match pairs until fixed point is reached
|
||||||
|
loop {
|
||||||
|
let match_pairs = mem::replace(&mut candidate.match_pairs, vec![]);
|
||||||
|
let mut progress = match_pairs.len(); // count how many were simplified
|
||||||
|
for match_pair in match_pairs {
|
||||||
|
if let Err(match_pair) = self.simplify_match_pair(match_pair, candidate) {
|
||||||
|
candidate.match_pairs.push(match_pair);
|
||||||
|
progress -= 1; // this one was not simplified
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if progress == 0 {
|
||||||
|
return; // if we were not able to simplify any, done.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tries to simplify `match_pair`, returning true if
|
||||||
|
/// successful. If successful, new match pairs and bindings will
|
||||||
|
/// have been pushed into the candidate. On failure (if false is
|
||||||
|
/// returned), no changes are made to candidate.
|
||||||
|
fn simplify_match_pair(&mut self,
|
||||||
|
match_pair: MatchPair<H>,
|
||||||
|
candidate: &mut Candidate<H>)
|
||||||
|
-> Result<(), MatchPair<H>> // returns Err() if cannot simplify
|
||||||
|
{
|
||||||
|
match match_pair.pattern.kind {
|
||||||
|
PatternKind::Wild(..) => {
|
||||||
|
// nothing left to do
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Binding { name, mutability, mode, var, ty, subpattern } => {
|
||||||
|
candidate.bindings.push(Binding {
|
||||||
|
name: name,
|
||||||
|
mutability: mutability,
|
||||||
|
span: match_pair.pattern.span,
|
||||||
|
source: match_pair.lvalue.clone(),
|
||||||
|
var_id: var,
|
||||||
|
var_ty: ty,
|
||||||
|
binding_mode: mode,
|
||||||
|
});
|
||||||
|
|
||||||
|
if let Some(subpattern) = subpattern {
|
||||||
|
// this is the `x @ P` case; have to keep matching against `P` now
|
||||||
|
let subpattern = self.hir.mirror(subpattern);
|
||||||
|
candidate.match_pairs.push(MatchPair::new(match_pair.lvalue, subpattern));
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Constant { .. } => {
|
||||||
|
// FIXME normalize patterns when possible
|
||||||
|
Err(match_pair)
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Array { prefix, slice: None, suffix } => {
|
||||||
|
self.append_prefix_suffix_pairs(
|
||||||
|
&mut candidate.match_pairs, match_pair.lvalue.clone(), prefix, suffix);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Array { prefix: _, slice: Some(_), suffix: _ } => {
|
||||||
|
self.hir.span_bug(
|
||||||
|
match_pair.pattern.span,
|
||||||
|
&format!("slice patterns not implemented in MIR"));
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Slice { .. } |
|
||||||
|
PatternKind::Range { .. } |
|
||||||
|
PatternKind::Variant { .. } => {
|
||||||
|
// cannot simplify, test is required
|
||||||
|
Err(match_pair)
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Leaf { subpatterns } => {
|
||||||
|
// tuple struct, match subpats (if any)
|
||||||
|
candidate.match_pairs.extend(
|
||||||
|
self.field_match_pairs(match_pair.lvalue, subpatterns));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Deref { subpattern } => {
|
||||||
|
let lvalue = match_pair.lvalue.deref();
|
||||||
|
let subpattern = self.hir.mirror(subpattern);
|
||||||
|
candidate.match_pairs.push(MatchPair::new(lvalue, subpattern));
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,301 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
// Testing candidates
|
||||||
|
//
|
||||||
|
// After candidates have been simplified, the only match pairs that
|
||||||
|
// remain are those that require some sort of test. The functions here
|
||||||
|
// identify what tests are needed, perform the tests, and then filter
|
||||||
|
// the candidates based on the result.
|
||||||
|
|
||||||
|
use build::Builder;
|
||||||
|
use build::matches::{Candidate, MatchPair, Test, TestKind};
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
/// Identifies what test is needed to decide if `match_pair` is applicable.
|
||||||
|
///
|
||||||
|
/// It is a bug to call this with a simplifyable pattern.
|
||||||
|
pub fn test(&mut self, match_pair: &MatchPair<H>) -> Test<H> {
|
||||||
|
match match_pair.pattern.kind.clone() {
|
||||||
|
PatternKind::Variant { adt_def, variant_index, subpatterns } => {
|
||||||
|
let elem = ProjectionElem::Downcast(adt_def, variant_index);
|
||||||
|
let downcast_lvalue = match_pair.lvalue.clone().elem(elem);
|
||||||
|
|
||||||
|
let consequent_match_pairs =
|
||||||
|
subpatterns.into_iter()
|
||||||
|
.map(|subpattern| {
|
||||||
|
let lvalue =
|
||||||
|
downcast_lvalue.clone().field(
|
||||||
|
subpattern.field);
|
||||||
|
self.match_pair(lvalue, subpattern.pattern)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
Test {
|
||||||
|
span: match_pair.pattern.span,
|
||||||
|
kind: TestKind::Switch { adt_def: adt_def },
|
||||||
|
outcome: variant_index,
|
||||||
|
match_pairs: consequent_match_pairs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Constant { expr } => {
|
||||||
|
let expr = self.as_constant(expr);
|
||||||
|
Test {
|
||||||
|
span: match_pair.pattern.span,
|
||||||
|
kind: TestKind::Eq { value: expr,
|
||||||
|
ty: match_pair.pattern.ty.clone() },
|
||||||
|
outcome: 0, // 0 == true, of course. :)
|
||||||
|
match_pairs: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Range { lo, hi } => {
|
||||||
|
let lo = self.as_constant(lo);
|
||||||
|
let hi = self.as_constant(hi);
|
||||||
|
Test {
|
||||||
|
span: match_pair.pattern.span,
|
||||||
|
kind: TestKind::Range { lo: lo,
|
||||||
|
hi: hi,
|
||||||
|
ty: match_pair.pattern.ty.clone() },
|
||||||
|
outcome: 0, // 0 == true, of course. :)
|
||||||
|
match_pairs: vec![]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Slice { prefix, slice: None, suffix } => {
|
||||||
|
let len = prefix.len() + suffix.len();
|
||||||
|
let mut consequent_match_pairs = vec![];
|
||||||
|
self.append_prefix_suffix_pairs(
|
||||||
|
&mut consequent_match_pairs, match_pair.lvalue.clone(), prefix, suffix);
|
||||||
|
Test {
|
||||||
|
span: match_pair.pattern.span,
|
||||||
|
kind: TestKind::Len { len: len, op: BinOp::Eq },
|
||||||
|
outcome: 0, // 0 == true, of course. :)
|
||||||
|
match_pairs: consequent_match_pairs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Slice { prefix: _, slice: Some(_), suffix: _ } => {
|
||||||
|
self.hir.span_bug(
|
||||||
|
match_pair.pattern.span,
|
||||||
|
&format!("slice patterns not implemented in MIR"));
|
||||||
|
}
|
||||||
|
|
||||||
|
PatternKind::Array { .. } |
|
||||||
|
PatternKind::Wild |
|
||||||
|
PatternKind::Binding { .. } |
|
||||||
|
PatternKind::Leaf { .. } |
|
||||||
|
PatternKind::Deref { .. } => {
|
||||||
|
self.error_simplifyable(match_pair)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Generates the code to perform a test.
|
||||||
|
pub fn perform_test(&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
lvalue: &Lvalue<H>,
|
||||||
|
test: &Test<H>)
|
||||||
|
-> Vec<BasicBlock> {
|
||||||
|
match test.kind.clone() {
|
||||||
|
TestKind::Switch { adt_def } => {
|
||||||
|
let num_enum_variants = self.hir.num_variants(adt_def);
|
||||||
|
let target_blocks: Vec<_> =
|
||||||
|
(0..num_enum_variants).map(|_| self.cfg.start_new_block())
|
||||||
|
.collect();
|
||||||
|
self.cfg.terminate(block, Terminator::Switch {
|
||||||
|
discr: lvalue.clone(),
|
||||||
|
targets: target_blocks.clone()
|
||||||
|
});
|
||||||
|
target_blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
TestKind::Eq { value, ty } => {
|
||||||
|
// call PartialEq::eq(discrim, constant)
|
||||||
|
let constant = self.push_constant(block, test.span, ty.clone(), value);
|
||||||
|
let item_ref = self.hir.partial_eq(ty);
|
||||||
|
self.call_comparison_fn(block, test.span, item_ref, lvalue.clone(), constant)
|
||||||
|
}
|
||||||
|
|
||||||
|
TestKind::Range { lo, hi, ty } => {
|
||||||
|
// Test `v` by computing `PartialOrd::le(lo, v) && PartialOrd::le(v, hi)`.
|
||||||
|
let lo = self.push_constant(block, test.span, ty.clone(), lo);
|
||||||
|
let hi = self.push_constant(block, test.span, ty.clone(), hi);
|
||||||
|
let item_ref = self.hir.partial_le(ty);
|
||||||
|
|
||||||
|
let lo_blocks =
|
||||||
|
self.call_comparison_fn(block, test.span, item_ref.clone(), lo, lvalue.clone());
|
||||||
|
|
||||||
|
let hi_blocks =
|
||||||
|
self.call_comparison_fn(lo_blocks[0], test.span, item_ref, lvalue.clone(), hi);
|
||||||
|
|
||||||
|
let failure = self.cfg.start_new_block();
|
||||||
|
self.cfg.terminate(lo_blocks[1], Terminator::Goto { target: failure });
|
||||||
|
self.cfg.terminate(hi_blocks[1], Terminator::Goto { target: failure });
|
||||||
|
|
||||||
|
vec![hi_blocks[0], failure]
|
||||||
|
}
|
||||||
|
|
||||||
|
TestKind::Len { len, op } => {
|
||||||
|
let (usize_ty, bool_ty) = (self.hir.usize_ty(), self.hir.bool_ty());
|
||||||
|
let (actual, result) = (self.temp(usize_ty), self.temp(bool_ty));
|
||||||
|
|
||||||
|
// actual = len(lvalue)
|
||||||
|
self.cfg.push_assign(
|
||||||
|
block, test.span,
|
||||||
|
&actual, Rvalue::Len(lvalue.clone()));
|
||||||
|
|
||||||
|
// expected = <N>
|
||||||
|
let expected =
|
||||||
|
self.push_usize(block, test.span, len);
|
||||||
|
|
||||||
|
// result = actual == expected OR result = actual < expected
|
||||||
|
self.cfg.push_assign(
|
||||||
|
block, test.span,
|
||||||
|
&result, Rvalue::BinaryOp(op,
|
||||||
|
Operand::Consume(actual),
|
||||||
|
Operand::Consume(expected)));
|
||||||
|
|
||||||
|
// branch based on result
|
||||||
|
let target_blocks: Vec<_> = vec![self.cfg.start_new_block(),
|
||||||
|
self.cfg.start_new_block()];
|
||||||
|
self.cfg.terminate(block, Terminator::If {
|
||||||
|
cond: Operand::Consume(result),
|
||||||
|
targets: [target_blocks[0], target_blocks[1]]
|
||||||
|
});
|
||||||
|
|
||||||
|
target_blocks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_comparison_fn(&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
span: H::Span,
|
||||||
|
item_ref: ItemRef<H>,
|
||||||
|
lvalue1: Lvalue<H>,
|
||||||
|
lvalue2: Lvalue<H>)
|
||||||
|
-> Vec<BasicBlock> {
|
||||||
|
let target_blocks = vec![self.cfg.start_new_block(),
|
||||||
|
self.cfg.start_new_block()];
|
||||||
|
|
||||||
|
let bool_ty = self.hir.bool_ty();
|
||||||
|
let eq_result = self.temp(bool_ty);
|
||||||
|
let func = self.push_item_ref(block, span, item_ref);
|
||||||
|
let call_blocks = [self.cfg.start_new_block(), self.diverge_cleanup()];
|
||||||
|
self.cfg.terminate(block,
|
||||||
|
Terminator::Call {
|
||||||
|
data: CallData {
|
||||||
|
destination: eq_result.clone(),
|
||||||
|
func: func,
|
||||||
|
args: vec![lvalue1, lvalue2],
|
||||||
|
},
|
||||||
|
targets: call_blocks,
|
||||||
|
});
|
||||||
|
|
||||||
|
// check the result
|
||||||
|
self.cfg.terminate(call_blocks[0],
|
||||||
|
Terminator::If {
|
||||||
|
cond: Operand::Consume(eq_result),
|
||||||
|
targets: [target_blocks[0], target_blocks[1]]
|
||||||
|
});
|
||||||
|
|
||||||
|
target_blocks
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Given a candidate and the outcome of a test we have performed,
|
||||||
|
/// transforms the candidate into a new candidate that reflects
|
||||||
|
/// further tests still needed. Returns `None` if this candidate
|
||||||
|
/// has now been ruled out.
|
||||||
|
///
|
||||||
|
/// For example, if a candidate included the patterns `[x.0 @
|
||||||
|
/// Ok(P1), x.1 @ 22]`, and we did a switch test on `x.0` and
|
||||||
|
/// found the variant `Err` (as indicated by the `test_outcome`
|
||||||
|
/// parameter), we would return `None`. But if the test_outcome
|
||||||
|
/// were `Ok`, we would return `Some([x.0.downcast<Ok>.0 @ P1, x.1
|
||||||
|
/// @ 22])`.
|
||||||
|
pub fn candidate_under_assumption(&mut self,
|
||||||
|
test_lvalue: &Lvalue<H>,
|
||||||
|
test_kind: &TestKind<H>,
|
||||||
|
test_outcome: usize,
|
||||||
|
candidate: &Candidate<H>)
|
||||||
|
-> Option<Candidate<H>> {
|
||||||
|
let candidate = candidate.clone();
|
||||||
|
let match_pairs = candidate.match_pairs;
|
||||||
|
match self.match_pairs_under_assumption(test_lvalue, test_kind, test_outcome, match_pairs) {
|
||||||
|
Some(match_pairs) => Some(Candidate { match_pairs: match_pairs, ..candidate }),
|
||||||
|
None => None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper for candidate_under_assumption that does the actual
|
||||||
|
/// work of transforming the list of match pairs.
|
||||||
|
fn match_pairs_under_assumption(&mut self,
|
||||||
|
test_lvalue: &Lvalue<H>,
|
||||||
|
test_kind: &TestKind<H>,
|
||||||
|
test_outcome: usize,
|
||||||
|
match_pairs: Vec<MatchPair<H>>)
|
||||||
|
-> Option<Vec<MatchPair<H>>> {
|
||||||
|
let mut result = vec![];
|
||||||
|
for match_pair in match_pairs {
|
||||||
|
// if the match pair is testing a different lvalue, it
|
||||||
|
// is unaffected by this test.
|
||||||
|
if match_pair.lvalue != *test_lvalue {
|
||||||
|
result.push(match_pair);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let desired_test = self.test(&match_pair);
|
||||||
|
|
||||||
|
if *test_kind != desired_test.kind {
|
||||||
|
// if the match pair wants to (e.g.) test for
|
||||||
|
// equality against some particular constant, but
|
||||||
|
// we did a switch, then we can't say whether it
|
||||||
|
// matches or not, so we still have to include it
|
||||||
|
// as a possibility.
|
||||||
|
//
|
||||||
|
// For example, we have a constant `FOO:
|
||||||
|
// Option<i32> = Some(22)`, and `match_pair` is `x
|
||||||
|
// @ FOO`, but we did a switch on the variant
|
||||||
|
// (`Some` vs `None`). (OK, in principle this
|
||||||
|
// could tell us something, but we're not that
|
||||||
|
// smart yet to actually dig into the constant
|
||||||
|
// itself)
|
||||||
|
result.push(match_pair);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if test_outcome != desired_test.outcome {
|
||||||
|
// if we did the right kind of test, but it had the
|
||||||
|
// wrong outcome, then this *entire candidate* can no
|
||||||
|
// longer apply, huzzah! Therefore, we can stop this
|
||||||
|
// iteration and just return `None` to our caller.
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise, the test passed, so we now have to include the
|
||||||
|
// "unlocked" set of match pairs. For example, if we had `x @
|
||||||
|
// Some(P1)`, and here we `test_kind==Switch` and
|
||||||
|
// `outcome=Some`, then we would return `x.downcast<Some>.0 @
|
||||||
|
// P1`.
|
||||||
|
result.extend(desired_test.match_pairs);
|
||||||
|
}
|
||||||
|
Some(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn error_simplifyable(&mut self, match_pair: &MatchPair<H>) -> ! {
|
||||||
|
self.hir.span_bug(
|
||||||
|
match_pair.pattern.span,
|
||||||
|
&format!("simplifyable pattern found: {:?}", match_pair.pattern))
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use build::Builder;
|
||||||
|
use build::matches::MatchPair;
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
use std::u32;
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
pub fn field_match_pairs(&mut self,
|
||||||
|
lvalue: Lvalue<H>,
|
||||||
|
subpatterns: Vec<FieldPatternRef<H>>)
|
||||||
|
-> Vec<MatchPair<H>> {
|
||||||
|
subpatterns.into_iter()
|
||||||
|
.map(|fieldpat| {
|
||||||
|
let lvalue = lvalue.clone().field(fieldpat.field);
|
||||||
|
self.match_pair(lvalue, fieldpat.pattern)
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn match_pair(&mut self, lvalue: Lvalue<H>, pattern: PatternRef<H>) -> MatchPair<H> {
|
||||||
|
let pattern = self.hir.mirror(pattern);
|
||||||
|
MatchPair::new(lvalue, pattern)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn append_prefix_suffix_pairs(&mut self,
|
||||||
|
match_pairs: &mut Vec<MatchPair<H>>,
|
||||||
|
lvalue: Lvalue<H>,
|
||||||
|
prefix: Vec<PatternRef<H>>,
|
||||||
|
suffix: Vec<PatternRef<H>>)
|
||||||
|
{
|
||||||
|
let min_length = prefix.len() + suffix.len();
|
||||||
|
assert!(min_length < u32::MAX as usize);
|
||||||
|
let min_length = min_length as u32;
|
||||||
|
|
||||||
|
let prefix_pairs: Vec<_> =
|
||||||
|
prefix.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, subpattern)| {
|
||||||
|
let elem = ProjectionElem::ConstantIndex {
|
||||||
|
offset: idx as u32,
|
||||||
|
min_length: min_length,
|
||||||
|
from_end: false,
|
||||||
|
};
|
||||||
|
let lvalue = lvalue.clone().elem(elem);
|
||||||
|
self.match_pair(lvalue, subpattern)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let suffix_pairs: Vec<_> =
|
||||||
|
suffix.into_iter()
|
||||||
|
.rev()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(idx, subpattern)| {
|
||||||
|
let elem = ProjectionElem::ConstantIndex {
|
||||||
|
offset: (idx+1) as u32,
|
||||||
|
min_length: min_length,
|
||||||
|
from_end: true,
|
||||||
|
};
|
||||||
|
let lvalue = lvalue.clone().elem(elem);
|
||||||
|
self.match_pair(lvalue, subpattern)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
match_pairs.extend(prefix_pairs.into_iter().chain(suffix_pairs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> MatchPair<H> {
|
||||||
|
pub fn new(lvalue: Lvalue<H>, pattern: Pattern<H>) -> MatchPair<H> {
|
||||||
|
MatchPair { lvalue: lvalue, pattern: pattern }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! Miscellaneous builder routines that are not specific to building any particular
|
||||||
|
//! kind of thing.
|
||||||
|
|
||||||
|
use build::Builder;
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
use std::u32;
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
/// Add a new temporary value of type `ty` storing the result of
|
||||||
|
/// evaluating `expr`.
|
||||||
|
///
|
||||||
|
/// NB: **No cleanup is scheduled for this temporary.** You should
|
||||||
|
/// call `schedule_drop` once the temporary is initialized.
|
||||||
|
pub fn temp(&mut self, ty: H::Ty) -> Lvalue<H> {
|
||||||
|
let index = self.temp_decls.len();
|
||||||
|
self.temp_decls.push(TempDecl { ty: ty });
|
||||||
|
assert!(index < (u32::MAX) as usize);
|
||||||
|
let lvalue = Lvalue::Temp(index as u32);
|
||||||
|
debug!("temp: created temp {:?} with type {:?}",
|
||||||
|
lvalue, self.temp_decls.last().unwrap().ty);
|
||||||
|
lvalue
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_constant(&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
span: H::Span,
|
||||||
|
ty: H::Ty,
|
||||||
|
constant: Constant<H>)
|
||||||
|
-> Lvalue<H> {
|
||||||
|
let temp = self.temp(ty);
|
||||||
|
self.cfg.push_assign_constant(block, span, &temp, constant);
|
||||||
|
temp
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_usize(&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
span: H::Span,
|
||||||
|
value: usize)
|
||||||
|
-> Lvalue<H> {
|
||||||
|
let usize_ty = self.hir.usize_ty();
|
||||||
|
let temp = self.temp(usize_ty);
|
||||||
|
self.cfg.push_assign_constant(
|
||||||
|
block, span, &temp,
|
||||||
|
Constant {
|
||||||
|
span: span,
|
||||||
|
kind: ConstantKind::Literal(Literal::Uint { bits: IntegralBits::BSize,
|
||||||
|
value: value as u64 }),
|
||||||
|
});
|
||||||
|
temp
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_item_ref(&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
span: H::Span,
|
||||||
|
item_ref: ItemRef<H>)
|
||||||
|
-> Lvalue<H> {
|
||||||
|
let constant = Constant {
|
||||||
|
span: span,
|
||||||
|
kind: ConstantKind::Literal(Literal::Item {
|
||||||
|
def_id: item_ref.def_id,
|
||||||
|
substs: item_ref.substs
|
||||||
|
})
|
||||||
|
};
|
||||||
|
self.push_constant(block, span, item_ref.ty, constant)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
// Copyright 2012-2014 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.
|
||||||
|
|
||||||
|
use hair::{self, Hair};
|
||||||
|
use rustc_data_structures::fnv::FnvHashMap;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
struct Builder<H:Hair> {
|
||||||
|
hir: H,
|
||||||
|
extents: FnvHashMap<H::CodeExtent, Vec<GraphExtent>>,
|
||||||
|
cfg: CFG<H>,
|
||||||
|
scopes: Vec<scope::Scope<H>>,
|
||||||
|
loop_scopes: Vec<scope::LoopScope<H>>,
|
||||||
|
unit_temp: Lvalue<H>,
|
||||||
|
var_decls: Vec<VarDecl<H>>,
|
||||||
|
var_indices: FnvHashMap<H::VarId, u32>,
|
||||||
|
temp_decls: Vec<TempDecl<H>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct CFG<H:Hair> {
|
||||||
|
basic_blocks: Vec<BasicBlockData<H>>
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// The `BlockAnd` "monad" packages up the new basic block along with a
|
||||||
|
// produced value (sometimes just unit, of course). The `unpack!`
|
||||||
|
// macro (and methods below) makes working with `BlockAnd` much more
|
||||||
|
// convenient.
|
||||||
|
|
||||||
|
#[must_use] // if you don't use one of these results, you're leaving a dangling edge
|
||||||
|
struct BlockAnd<T>(BasicBlock, T);
|
||||||
|
|
||||||
|
impl BasicBlock {
|
||||||
|
fn and<T>(self, v: T) -> BlockAnd<T> {
|
||||||
|
BlockAnd(self, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unit(self) -> BlockAnd<()> {
|
||||||
|
BlockAnd(self, ())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update a block pointer and return the value.
|
||||||
|
/// Use it like `let x = unpack!(block = self.foo(block, foo))`.
|
||||||
|
macro_rules! unpack {
|
||||||
|
($x:ident = $c:expr) => {
|
||||||
|
{
|
||||||
|
let BlockAnd(b, v) = $c;
|
||||||
|
$x = b;
|
||||||
|
v
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
($c:expr) => {
|
||||||
|
{
|
||||||
|
let BlockAnd(b, ()) = $c;
|
||||||
|
b
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// construct() -- the main entry point for building MIR for a function
|
||||||
|
|
||||||
|
pub fn construct<H:Hair>(mut hir: H,
|
||||||
|
_span: H::Span,
|
||||||
|
implicit_arguments: Vec<H::Ty>,
|
||||||
|
explicit_arguments: Vec<(H::Ty, H::Pattern)>,
|
||||||
|
argument_extent: H::CodeExtent,
|
||||||
|
ast_block: H::Block)
|
||||||
|
-> Mir<H> {
|
||||||
|
let cfg = CFG { basic_blocks: vec![] };
|
||||||
|
|
||||||
|
// it's handy to have a temporary of type `()` sometimes, so make
|
||||||
|
// one from the start and keep it available
|
||||||
|
let temp_decls = vec![TempDecl::<H> { ty: hir.unit_ty() }];
|
||||||
|
let unit_temp = Lvalue::Temp(0);
|
||||||
|
|
||||||
|
let mut builder = Builder {
|
||||||
|
hir: hir,
|
||||||
|
cfg: cfg,
|
||||||
|
extents: FnvHashMap(),
|
||||||
|
scopes: vec![],
|
||||||
|
loop_scopes: vec![],
|
||||||
|
temp_decls: temp_decls,
|
||||||
|
var_decls: vec![],
|
||||||
|
var_indices: FnvHashMap(),
|
||||||
|
unit_temp: unit_temp,
|
||||||
|
};
|
||||||
|
|
||||||
|
assert_eq!(builder.cfg.start_new_block(), START_BLOCK);
|
||||||
|
assert_eq!(builder.cfg.start_new_block(), END_BLOCK);
|
||||||
|
assert_eq!(builder.cfg.start_new_block(), DIVERGE_BLOCK);
|
||||||
|
|
||||||
|
let mut block = START_BLOCK;
|
||||||
|
let arg_decls = unpack!(block = builder.args_and_body(block,
|
||||||
|
implicit_arguments,
|
||||||
|
explicit_arguments,
|
||||||
|
argument_extent,
|
||||||
|
ast_block));
|
||||||
|
|
||||||
|
builder.cfg.terminate(block, Terminator::Goto { target: END_BLOCK });
|
||||||
|
builder.cfg.terminate(END_BLOCK, Terminator::Return);
|
||||||
|
|
||||||
|
Mir {
|
||||||
|
basic_blocks: builder.cfg.basic_blocks,
|
||||||
|
extents: builder.extents,
|
||||||
|
var_decls: builder.var_decls,
|
||||||
|
arg_decls: arg_decls,
|
||||||
|
temp_decls: builder.temp_decls,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
fn args_and_body(&mut self,
|
||||||
|
mut block: BasicBlock,
|
||||||
|
implicit_arguments: Vec<H::Ty>,
|
||||||
|
explicit_arguments: Vec<(H::Ty, H::Pattern)>,
|
||||||
|
argument_extent: H::CodeExtent,
|
||||||
|
ast_block: H::Block)
|
||||||
|
-> BlockAnd<Vec<ArgDecl<H>>>
|
||||||
|
{
|
||||||
|
self.in_scope(argument_extent, block, |this| {
|
||||||
|
let arg_decls = {
|
||||||
|
let implicit_arg_decls = implicit_arguments.into_iter()
|
||||||
|
.map(|ty| ArgDecl { ty: ty });
|
||||||
|
|
||||||
|
// to start, translate the argument patterns and collect the
|
||||||
|
// argument types.
|
||||||
|
let explicit_arg_decls =
|
||||||
|
explicit_arguments
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, (ty, pattern))| {
|
||||||
|
let lvalue = Lvalue::Arg(index as u32);
|
||||||
|
unpack!(block = this.lvalue_into_pattern(block,
|
||||||
|
argument_extent,
|
||||||
|
hair::PatternRef::Hair(pattern),
|
||||||
|
&lvalue));
|
||||||
|
ArgDecl { ty: ty }
|
||||||
|
});
|
||||||
|
|
||||||
|
implicit_arg_decls.chain(explicit_arg_decls).collect()
|
||||||
|
};
|
||||||
|
|
||||||
|
// start the first basic block and translate the body
|
||||||
|
unpack!(block = this.ast_block(&Lvalue::ReturnPointer, block, ast_block));
|
||||||
|
|
||||||
|
block.and(arg_decls)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Builder methods are broken up into modules, depending on what kind
|
||||||
|
// of thing is being translated. Note that they use the `unpack` macro
|
||||||
|
// above extensively.
|
||||||
|
|
||||||
|
mod block;
|
||||||
|
mod cfg;
|
||||||
|
mod expr;
|
||||||
|
mod into;
|
||||||
|
mod matches;
|
||||||
|
mod misc;
|
||||||
|
mod scope;
|
||||||
|
mod stmt;
|
||||||
|
|
|
@ -0,0 +1,304 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Managing the scope stack. The scopes are tied to lexical scopes, so as
|
||||||
|
we descend the HAIR, we push a scope on the stack, translate ite
|
||||||
|
contents, and then pop it off. Every scope is named by a
|
||||||
|
`H::CodeExtent`.
|
||||||
|
|
||||||
|
### SEME Regions
|
||||||
|
|
||||||
|
When pushing a new scope, we record the current point in the graph (a
|
||||||
|
basic block); this marks the entry to the scope. We then generate more
|
||||||
|
stuff in the control-flow graph. Whenever the scope is exited, either
|
||||||
|
via a `break` or `return` or just by fallthrough, that marks an exit
|
||||||
|
from the scope. Each lexical scope thus corresponds to a single-entry,
|
||||||
|
multiple-exit (SEME) region in the control-flow graph.
|
||||||
|
|
||||||
|
For now, we keep a mapping from each `H::CodeExtent` to its
|
||||||
|
corresponding SEME region for later reference (see caveat in next
|
||||||
|
paragraph). This is because region scopes are tied to
|
||||||
|
them. Eventually, when we shift to non-lexical lifetimes, three should
|
||||||
|
be no need to remember this mapping.
|
||||||
|
|
||||||
|
There is one additional wrinkle, actually, that I wanted to hide from
|
||||||
|
you but duty compels me to mention. In the course of translating
|
||||||
|
matches, it sometimes happen that certain code (namely guards) gets
|
||||||
|
executed multiple times. This means that the scope lexical scope may
|
||||||
|
in fact correspond to multiple, disjoint SEME regions. So in fact our
|
||||||
|
mapping os from one scope to a vector of SEME regions.
|
||||||
|
|
||||||
|
### Drops
|
||||||
|
|
||||||
|
The primary purpose for scopes is to insert drops: while translating
|
||||||
|
the contents, we also accumulate lvalues that need to be dropped upon
|
||||||
|
exit from each scope. This is done by calling `schedule_drop`. Once a
|
||||||
|
drop is scheduled, whenever we branch out we will insert drops of all
|
||||||
|
those lvalues onto the outgoing edge. Note that we don't know the full
|
||||||
|
set of scheduled drops up front, and so whenever we exit from the
|
||||||
|
scope we only drop the values scheduled thus far. For example, consider
|
||||||
|
the scope S corresponding to this loop:
|
||||||
|
|
||||||
|
```
|
||||||
|
loop {
|
||||||
|
let x = ...;
|
||||||
|
if cond { break; }
|
||||||
|
let y = ...;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When processing the `let x`, we will add one drop to the scope for
|
||||||
|
`x`. The break will then insert a drop for `x`. When we process `let
|
||||||
|
y`, we will add another drop (in fact, to a subscope, but let's ignore
|
||||||
|
that for now); any later drops would also drop `y`.
|
||||||
|
|
||||||
|
### Early exit
|
||||||
|
|
||||||
|
There are numerous "normal" ways to early exit a scope: `break`,
|
||||||
|
`continue`, `return` (panics are handled separately). Whenever an
|
||||||
|
early exit occurs, the method `exit_scope` is called. It is given the
|
||||||
|
current point in execution where the early exit occurs, as well as the
|
||||||
|
scope you want to branch to (note that all early exits from to some
|
||||||
|
other enclosing scope). `exit_scope` will record thid exit point and
|
||||||
|
also add all drops.
|
||||||
|
|
||||||
|
Panics are handled in a similar fashion, except that a panic always
|
||||||
|
returns out to the `DIVERGE_BLOCK`. To trigger a panic, simply call
|
||||||
|
`panic(p)` with the current point `p`. Or else you can call
|
||||||
|
`diverge_cleanup`, which will produce a block that you can branch to
|
||||||
|
which does the appropriate cleanup and then diverges. `panic(p)`
|
||||||
|
simply calls `diverge_cleanup()` and adds an edge from `p` to the
|
||||||
|
result.
|
||||||
|
|
||||||
|
### Loop scopes
|
||||||
|
|
||||||
|
In addition to the normal scope stack, we track a loop scope stack
|
||||||
|
that contains only loops. It tracks where a `break` and `continue`
|
||||||
|
should go to.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
use build::{BlockAnd, Builder, CFG};
|
||||||
|
use hair::Hair;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
pub struct Scope<H:Hair> {
|
||||||
|
extent: H::CodeExtent,
|
||||||
|
exits: Vec<ExecutionPoint>,
|
||||||
|
drops: Vec<(DropKind, H::Span, Lvalue<H>)>,
|
||||||
|
cached_block: Option<BasicBlock>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct LoopScope<H:Hair> {
|
||||||
|
pub extent: H::CodeExtent, // extent of the loop
|
||||||
|
pub continue_block: BasicBlock, // where to go on a `loop`
|
||||||
|
pub break_block: BasicBlock, // where to go on a `break
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
/// Start a loop scope, which tracks where `continue` and `break`
|
||||||
|
/// should branch to. See module comment for more details.
|
||||||
|
pub fn in_loop_scope<F,R>(&mut self,
|
||||||
|
loop_block: BasicBlock,
|
||||||
|
break_block: BasicBlock,
|
||||||
|
f: F)
|
||||||
|
-> BlockAnd<R>
|
||||||
|
where F: FnOnce(&mut Builder<H>) -> BlockAnd<R>
|
||||||
|
{
|
||||||
|
let extent = self.extent_of_innermost_scope().unwrap();
|
||||||
|
let loop_scope = LoopScope::<H> { extent: extent.clone(),
|
||||||
|
continue_block: loop_block,
|
||||||
|
break_block: break_block };
|
||||||
|
self.loop_scopes.push(loop_scope);
|
||||||
|
let r = f(self);
|
||||||
|
assert!(self.loop_scopes.pop().unwrap().extent == extent);
|
||||||
|
r
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start a scope. The closure `f` should translate the contents
|
||||||
|
/// of the scope. See module comment for more details.
|
||||||
|
pub fn in_scope<F,R>(&mut self,
|
||||||
|
extent: H::CodeExtent,
|
||||||
|
block: BasicBlock,
|
||||||
|
f: F)
|
||||||
|
-> BlockAnd<R>
|
||||||
|
where F: FnOnce(&mut Builder<H>) -> BlockAnd<R>
|
||||||
|
{
|
||||||
|
debug!("in_scope(extent={:?}, block={:?})", extent, block);
|
||||||
|
|
||||||
|
let start_point = self.cfg.end_point(block);
|
||||||
|
|
||||||
|
// push scope, execute `f`, then pop scope again
|
||||||
|
self.scopes.push(Scope {
|
||||||
|
extent: extent.clone(),
|
||||||
|
drops: vec![],
|
||||||
|
exits: vec![],
|
||||||
|
cached_block: None,
|
||||||
|
});
|
||||||
|
let BlockAnd(fallthrough_block, rv) = f(self);
|
||||||
|
let mut scope = self.scopes.pop().unwrap();
|
||||||
|
|
||||||
|
// add in any drops needed on the fallthrough path (any other
|
||||||
|
// exiting paths, such as those that arise from `break`, will
|
||||||
|
// have drops already)
|
||||||
|
for (kind, span, lvalue) in scope.drops {
|
||||||
|
self.cfg.push_drop(fallthrough_block, span, kind, &lvalue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add the implicit fallthrough edge
|
||||||
|
scope.exits.push(self.cfg.end_point(fallthrough_block));
|
||||||
|
|
||||||
|
// compute the extent from start to finish and store it in the graph
|
||||||
|
let graph_extent = self.graph_extent(start_point, scope.exits);
|
||||||
|
self.extents.entry(extent)
|
||||||
|
.or_insert(vec![])
|
||||||
|
.push(graph_extent);
|
||||||
|
|
||||||
|
debug!("in_scope: exiting extent={:?} fallthrough_block={:?}", extent, fallthrough_block);
|
||||||
|
fallthrough_block.and(rv)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a graph extent (SEME region) from an entry point and
|
||||||
|
/// exit points.
|
||||||
|
fn graph_extent(&self, entry: ExecutionPoint, exits: Vec<ExecutionPoint>) -> GraphExtent {
|
||||||
|
if exits.len() == 1 && entry.block == exits[0].block {
|
||||||
|
GraphExtent { entry: entry, exit: GraphExtentExit::Statement(exits[0].statement) }
|
||||||
|
} else {
|
||||||
|
GraphExtent { entry: entry, exit: GraphExtentExit::Points(exits) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds the loop scope for a given label. This is used for
|
||||||
|
/// resolving `break` and `continue`.
|
||||||
|
pub fn find_loop_scope(&mut self,
|
||||||
|
span: H::Span,
|
||||||
|
label: Option<H::CodeExtent>)
|
||||||
|
-> LoopScope<H> {
|
||||||
|
let loop_scope =
|
||||||
|
match label {
|
||||||
|
None => {
|
||||||
|
// no label? return the innermost loop scope
|
||||||
|
self.loop_scopes.iter()
|
||||||
|
.rev()
|
||||||
|
.next()
|
||||||
|
}
|
||||||
|
Some(label) => {
|
||||||
|
// otherwise, find the loop-scope with the correct id
|
||||||
|
self.loop_scopes.iter()
|
||||||
|
.rev()
|
||||||
|
.filter(|loop_scope| loop_scope.extent == label)
|
||||||
|
.next()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match loop_scope {
|
||||||
|
Some(loop_scope) => loop_scope.clone(),
|
||||||
|
None => self.hir.span_bug(span, "no enclosing loop scope found?")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Branch out of `block` to `target`, exiting all scopes up to
|
||||||
|
/// and including `extent`. This will insert whatever drops are
|
||||||
|
/// needed, as well as tracking this exit for the SEME region. See
|
||||||
|
/// module comment for details.
|
||||||
|
pub fn exit_scope(&mut self,
|
||||||
|
span: H::Span,
|
||||||
|
extent: H::CodeExtent,
|
||||||
|
block: BasicBlock,
|
||||||
|
target: BasicBlock) {
|
||||||
|
let popped_scopes =
|
||||||
|
match self.scopes.iter().rev().position(|scope| scope.extent == extent) {
|
||||||
|
Some(p) => p + 1,
|
||||||
|
None => self.hir.span_bug(span, &format!("extent {:?} does not enclose",
|
||||||
|
extent)),
|
||||||
|
};
|
||||||
|
|
||||||
|
for scope in self.scopes.iter_mut().rev().take(popped_scopes) {
|
||||||
|
for &(kind, drop_span, ref lvalue) in &scope.drops {
|
||||||
|
self.cfg.push_drop(block, drop_span, kind, lvalue);
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.exits.push(self.cfg.end_point(block));
|
||||||
|
}
|
||||||
|
|
||||||
|
self.cfg.terminate(block, Terminator::Goto { target: target });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a path that performs all required cleanup for
|
||||||
|
/// unwinding. This path terminates in DIVERGE. Returns the start
|
||||||
|
/// of the path. See module comment for more details.
|
||||||
|
pub fn diverge_cleanup(&mut self) -> BasicBlock {
|
||||||
|
diverge_cleanup_helper(&mut self.cfg, &mut self.scopes)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create diverge cleanup and branch to it from `block`.
|
||||||
|
pub fn panic(&mut self, block: BasicBlock) {
|
||||||
|
let cleanup = self.diverge_cleanup();
|
||||||
|
self.cfg.terminate(block, Terminator::Panic { target: cleanup });
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Indicates that `lvalue` should be dropped on exit from
|
||||||
|
/// `extent`.
|
||||||
|
pub fn schedule_drop(&mut self,
|
||||||
|
span: H::Span,
|
||||||
|
extent: H::CodeExtent,
|
||||||
|
kind: DropKind,
|
||||||
|
lvalue: &Lvalue<H>,
|
||||||
|
lvalue_ty: H::Ty)
|
||||||
|
{
|
||||||
|
if self.hir.needs_drop(lvalue_ty, span) {
|
||||||
|
match self.scopes.iter_mut().rev().find(|s| s.extent == extent) {
|
||||||
|
Some(scope) => {
|
||||||
|
scope.drops.push((kind, span, lvalue.clone()));
|
||||||
|
scope.cached_block = None;
|
||||||
|
}
|
||||||
|
None => self.hir.span_bug(span, &format!("extent {:?} not in scope to drop {:?}",
|
||||||
|
extent, lvalue)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extent_of_innermost_scope(&self) -> Option<H::CodeExtent> {
|
||||||
|
self.scopes.last().map(|scope| scope.extent)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn extent_of_outermost_scope(&self) -> Option<H::CodeExtent> {
|
||||||
|
self.scopes.first().map(|scope| scope.extent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn diverge_cleanup_helper<H:Hair>(cfg: &mut CFG<H>,
|
||||||
|
scopes: &mut [Scope<H>])
|
||||||
|
-> BasicBlock {
|
||||||
|
let len = scopes.len();
|
||||||
|
|
||||||
|
if len == 0 {
|
||||||
|
return DIVERGE_BLOCK;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (remaining, scope) = scopes.split_at_mut(len - 1);
|
||||||
|
let scope = &mut scope[0];
|
||||||
|
|
||||||
|
if let Some(b) = scope.cached_block {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
let block = cfg.start_new_block();
|
||||||
|
for &(kind, span, ref lvalue) in &scope.drops {
|
||||||
|
cfg.push_drop(block, span, kind, lvalue);
|
||||||
|
}
|
||||||
|
scope.cached_block = Some(block);
|
||||||
|
|
||||||
|
let remaining_cleanup_block = diverge_cleanup_helper(cfg, remaining);
|
||||||
|
cfg.terminate(block, Terminator::Goto { target: remaining_cleanup_block });
|
||||||
|
block
|
||||||
|
}
|
|
@ -0,0 +1,61 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use build::{BlockAnd, Builder};
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
impl<H:Hair> Builder<H> {
|
||||||
|
pub fn stmts(&mut self, mut block: BasicBlock, stmts: Vec<StmtRef<H>>) -> BlockAnd<()> {
|
||||||
|
for stmt in stmts {
|
||||||
|
unpack!(block = self.stmt(block, stmt));
|
||||||
|
}
|
||||||
|
block.unit()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn stmt(&mut self, mut block: BasicBlock, stmt: StmtRef<H>) -> BlockAnd<()> {
|
||||||
|
let this = self;
|
||||||
|
let Stmt { span, kind } = this.hir.mirror(stmt);
|
||||||
|
match kind {
|
||||||
|
StmtKind::Let { remainder_scope,
|
||||||
|
init_scope,
|
||||||
|
pattern,
|
||||||
|
initializer: Some(initializer),
|
||||||
|
stmts } => {
|
||||||
|
this.in_scope(remainder_scope, block, |this| {
|
||||||
|
unpack!(block = this.in_scope(init_scope, block, |this| {
|
||||||
|
this.expr_into_pattern(block, remainder_scope, pattern, initializer)
|
||||||
|
}));
|
||||||
|
this.stmts(block, stmts)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
StmtKind::Let { remainder_scope, init_scope, pattern, initializer: None, stmts } => {
|
||||||
|
this.in_scope(remainder_scope, block, |this| {
|
||||||
|
unpack!(block = this.in_scope(init_scope, block, |this| {
|
||||||
|
this.declare_uninitialized_variables(remainder_scope, pattern);
|
||||||
|
block.unit()
|
||||||
|
}));
|
||||||
|
this.stmts(block, stmts)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
StmtKind::Expr { scope, expr } => {
|
||||||
|
this.in_scope(scope, block, |this| {
|
||||||
|
let expr = this.hir.mirror(expr);
|
||||||
|
let temp = this.temp(expr.ty.clone());
|
||||||
|
unpack!(block = this.into(&temp, block, expr));
|
||||||
|
this.cfg.push_drop(block, span, DropKind::Deep, &temp);
|
||||||
|
block.unit()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,225 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! An experimental pass that scources for `#[rustc_mir]` attributes,
|
||||||
|
//! builds the resulting MIR, and dumps it out into a file for inspection.
|
||||||
|
//!
|
||||||
|
//! The attribute formats that are currently accepted are:
|
||||||
|
//!
|
||||||
|
//! - `#[rustc_mir(graphviz="file.gv")]`
|
||||||
|
//! - `#[rustc_mir(pretty="file.mir")]`
|
||||||
|
|
||||||
|
extern crate syntax;
|
||||||
|
extern crate rustc;
|
||||||
|
extern crate rustc_front;
|
||||||
|
|
||||||
|
use build;
|
||||||
|
use dot;
|
||||||
|
use repr::Mir;
|
||||||
|
use std::fs::File;
|
||||||
|
use tcx::{PatNode, Cx};
|
||||||
|
|
||||||
|
use self::rustc::middle::def_id::DefId;
|
||||||
|
use self::rustc::middle::infer;
|
||||||
|
use self::rustc::middle::region::CodeExtentData;
|
||||||
|
use self::rustc::middle::ty::{self, Ty};
|
||||||
|
use self::rustc::util::common::ErrorReported;
|
||||||
|
use self::rustc_front::hir;
|
||||||
|
use self::rustc_front::attr::{AttrMetaMethods};
|
||||||
|
use self::rustc_front::visit;
|
||||||
|
use self::syntax::ast;
|
||||||
|
use self::syntax::codemap::Span;
|
||||||
|
|
||||||
|
pub fn dump_crate(tcx: &ty::ctxt) {
|
||||||
|
let mut dump = OuterDump { tcx: tcx };
|
||||||
|
visit::walk_crate(&mut dump, tcx.map.krate());
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// OuterDump -- walks a crate, looking for fn items and methods to build MIR from
|
||||||
|
|
||||||
|
struct OuterDump<'a,'tcx:'a> {
|
||||||
|
tcx: &'a ty::ctxt<'tcx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> OuterDump<'a, 'tcx> {
|
||||||
|
fn visit_mir<OP>(&self, attributes: &'tcx [hir::Attribute], mut walk_op: OP)
|
||||||
|
where OP: FnMut(&mut InnerDump<'a,'tcx>)
|
||||||
|
{
|
||||||
|
let mut built_mir = false;
|
||||||
|
|
||||||
|
for attr in attributes {
|
||||||
|
if attr.check_name("rustc_mir") {
|
||||||
|
let mut closure_dump = InnerDump { tcx: self.tcx, attr: Some(attr) };
|
||||||
|
walk_op(&mut closure_dump);
|
||||||
|
built_mir = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let always_build_mir = self.tcx.sess.opts.always_build_mir;
|
||||||
|
if !built_mir && always_build_mir {
|
||||||
|
let mut closure_dump = InnerDump { tcx: self.tcx, attr: None };
|
||||||
|
walk_op(&mut closure_dump);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<'a, 'tcx> visit::Visitor<'tcx> for OuterDump<'a, 'tcx> {
|
||||||
|
fn visit_item(&mut self, item: &'tcx hir::Item) {
|
||||||
|
self.visit_mir(&item.attrs, |c| visit::walk_item(c, item));
|
||||||
|
visit::walk_item(self, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) {
|
||||||
|
match trait_item.node {
|
||||||
|
hir::MethodTraitItem(_, Some(_)) => {
|
||||||
|
self.visit_mir(&trait_item.attrs, |c| visit::walk_trait_item(c, trait_item));
|
||||||
|
}
|
||||||
|
_ => { }
|
||||||
|
}
|
||||||
|
visit::walk_trait_item(self, trait_item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// InnerDump -- dumps MIR for a single fn and its contained closures
|
||||||
|
|
||||||
|
struct InnerDump<'a,'tcx:'a> {
|
||||||
|
tcx: &'a ty::ctxt<'tcx>,
|
||||||
|
attr: Option<&'a hir::Attribute>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'tcx> visit::Visitor<'tcx> for InnerDump<'a,'tcx> {
|
||||||
|
fn visit_item(&mut self, _: &'tcx hir::Item) {
|
||||||
|
// ignore nested items; they need their own graphviz annotation
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_fn(&mut self,
|
||||||
|
fk: visit::FnKind<'tcx>,
|
||||||
|
decl: &'tcx hir::FnDecl,
|
||||||
|
body: &'tcx hir::Block,
|
||||||
|
span: Span,
|
||||||
|
id: ast::NodeId) {
|
||||||
|
let (prefix, implicit_arg_tys) = match fk {
|
||||||
|
visit::FnKind::Closure =>
|
||||||
|
(format!("{}-", id), vec![closure_self_ty(&self.tcx, id, body.id)]),
|
||||||
|
_ =>
|
||||||
|
(format!(""), vec![]),
|
||||||
|
};
|
||||||
|
|
||||||
|
let param_env =
|
||||||
|
ty::ParameterEnvironment::for_item(self.tcx, id);
|
||||||
|
|
||||||
|
let infcx =
|
||||||
|
infer::new_infer_ctxt(self.tcx,
|
||||||
|
&self.tcx.tables,
|
||||||
|
Some(param_env),
|
||||||
|
true);
|
||||||
|
|
||||||
|
match build_mir(Cx::new(&infcx), implicit_arg_tys, id, span, decl, body) {
|
||||||
|
Ok(mir) => {
|
||||||
|
let meta_item_list =
|
||||||
|
self.attr.iter()
|
||||||
|
.flat_map(|a| a.meta_item_list())
|
||||||
|
.flat_map(|l| l.iter());
|
||||||
|
for item in meta_item_list {
|
||||||
|
if item.check_name("graphviz") {
|
||||||
|
match item.value_str() {
|
||||||
|
Some(s) => {
|
||||||
|
match
|
||||||
|
File::create(format!("{}{}", prefix, s))
|
||||||
|
.and_then(|ref mut output| dot::render(&mir, output))
|
||||||
|
{
|
||||||
|
Ok(()) => { }
|
||||||
|
Err(e) => {
|
||||||
|
self.tcx.sess.span_fatal(
|
||||||
|
item.span,
|
||||||
|
&format!("Error writing graphviz \
|
||||||
|
results to `{}`: {}",
|
||||||
|
s, e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
self.tcx.sess.span_err(
|
||||||
|
item.span,
|
||||||
|
&format!("graphviz attribute requires a path"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(ErrorReported) => { }
|
||||||
|
}
|
||||||
|
|
||||||
|
visit::walk_fn(self, fk, decl, body, span);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_mir<'a,'tcx:'a>(cx: Cx<'a,'tcx>,
|
||||||
|
implicit_arg_tys: Vec<Ty<'tcx>>,
|
||||||
|
fn_id: ast::NodeId,
|
||||||
|
span: Span,
|
||||||
|
decl: &'tcx hir::FnDecl,
|
||||||
|
body: &'tcx hir::Block)
|
||||||
|
-> Result<Mir<Cx<'a,'tcx>>, ErrorReported> {
|
||||||
|
let arguments =
|
||||||
|
decl.inputs
|
||||||
|
.iter()
|
||||||
|
.map(|arg| {
|
||||||
|
let ty = cx.tcx.node_id_to_type(arg.id);
|
||||||
|
(ty, PatNode::irrefutable(&arg.pat))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let parameter_scope =
|
||||||
|
cx.tcx.region_maps.lookup_code_extent(
|
||||||
|
CodeExtentData::ParameterScope { fn_id: fn_id, body_id: body.id });
|
||||||
|
Ok(build::construct(cx,
|
||||||
|
span,
|
||||||
|
implicit_arg_tys,
|
||||||
|
arguments,
|
||||||
|
parameter_scope,
|
||||||
|
body))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn closure_self_ty<'a,'tcx>(tcx: &ty::ctxt<'tcx>,
|
||||||
|
closure_expr_id: ast::NodeId,
|
||||||
|
body_id: ast::NodeId)
|
||||||
|
-> Ty<'tcx>
|
||||||
|
{
|
||||||
|
let closure_ty = tcx.node_id_to_type(closure_expr_id);
|
||||||
|
|
||||||
|
// We're just hard-coding the idea that the signature will be
|
||||||
|
// &self or &mut self and hence will have a bound region with
|
||||||
|
// number 0, hokey.
|
||||||
|
let region =
|
||||||
|
ty::Region::ReFree(
|
||||||
|
ty::FreeRegion {
|
||||||
|
scope: tcx.region_maps.item_extent(body_id),
|
||||||
|
bound_region: ty::BoundRegion::BrAnon(0)
|
||||||
|
});
|
||||||
|
let region =
|
||||||
|
tcx.mk_region(region);
|
||||||
|
|
||||||
|
match tcx.closure_kind(DefId::local(closure_expr_id)) {
|
||||||
|
ty::ClosureKind::FnClosureKind =>
|
||||||
|
tcx.mk_ref(region,
|
||||||
|
ty::TypeAndMut { ty: closure_ty,
|
||||||
|
mutbl: hir::MutImmutable }),
|
||||||
|
ty::ClosureKind::FnMutClosureKind =>
|
||||||
|
tcx.mk_ref(region,
|
||||||
|
ty::TypeAndMut { ty: closure_ty,
|
||||||
|
mutbl: hir::MutMutable }),
|
||||||
|
ty::ClosureKind::FnOnceClosureKind =>
|
||||||
|
closure_ty
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,157 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
use dot;
|
||||||
|
use hair::Hair;
|
||||||
|
use repr::*;
|
||||||
|
use std::borrow::IntoCow;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub struct EdgeIndex {
|
||||||
|
source: BasicBlock,
|
||||||
|
target: BasicBlock,
|
||||||
|
index: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,H:Hair> dot::Labeller<'a, BasicBlock, EdgeIndex> for Mir<H> {
|
||||||
|
fn graph_id(&'a self) -> dot::Id<'a> {
|
||||||
|
dot::Id::new("Mir").unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_id(&'a self, n: &BasicBlock) -> dot::Id<'a> {
|
||||||
|
dot::Id::new(format!("BB{}", n.index())).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_shape(&'a self, _: &BasicBlock) -> Option<dot::LabelText<'a>> {
|
||||||
|
Some(dot::LabelText::label("none"))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn node_label(&'a self, &n: &BasicBlock) -> dot::LabelText<'a> {
|
||||||
|
let mut buffer = String::new();
|
||||||
|
buffer.push_str("<TABLE ALIGN=\"LEFT\">");
|
||||||
|
|
||||||
|
buffer.push_str("<TR><TD>");
|
||||||
|
buffer.push_str(&format!("{:?}", n));
|
||||||
|
buffer.push_str("</TD></TR>");
|
||||||
|
|
||||||
|
let data = self.basic_block_data(n);
|
||||||
|
for statement in &data.statements {
|
||||||
|
buffer.push_str("<TR><TD>");
|
||||||
|
buffer.push_str(&escape(format!("{:?}", statement)));
|
||||||
|
buffer.push_str("</TD></TR>");
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer.push_str("<TR><TD>");
|
||||||
|
buffer.push_str(&escape(format!("{:?}", &data.terminator)));
|
||||||
|
buffer.push_str("</TD></TR>");
|
||||||
|
|
||||||
|
buffer.push_str("</TABLE>");
|
||||||
|
|
||||||
|
dot::LabelText::html(buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edge_label(&'a self, edge: &EdgeIndex) -> dot::LabelText<'a> {
|
||||||
|
dot::LabelText::label(format!("{}", edge.index))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,H:Hair> dot::GraphWalk<'a, BasicBlock, EdgeIndex> for Mir<H> {
|
||||||
|
fn nodes(&'a self) -> dot::Nodes<'a, BasicBlock> {
|
||||||
|
self.all_basic_blocks().into_cow()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn edges(&'a self) -> dot::Edges<'a, EdgeIndex> {
|
||||||
|
self.all_basic_blocks()
|
||||||
|
.into_iter()
|
||||||
|
.flat_map(|source| {
|
||||||
|
self.basic_block_data(source).terminator
|
||||||
|
.successors()
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(move |(index, &target)| {
|
||||||
|
EdgeIndex { source: source,
|
||||||
|
target: target,
|
||||||
|
index: index }
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.into_cow()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn source(&'a self, edge: &EdgeIndex) -> BasicBlock {
|
||||||
|
edge.source
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target(&'a self, edge: &EdgeIndex) -> BasicBlock {
|
||||||
|
edge.target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn escape(text: String) -> String {
|
||||||
|
let text = dot::escape_html(&text);
|
||||||
|
let text = all_to_subscript("Temp", text);
|
||||||
|
let text = all_to_subscript("Var", text);
|
||||||
|
let text = all_to_subscript("Arg", text);
|
||||||
|
let text = all_to_subscript("BB", text);
|
||||||
|
text
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A call like `all_to_subscript("Temp", "Temp(123)")` will convert
|
||||||
|
/// to `Temp₁₂₃`.
|
||||||
|
fn all_to_subscript(header: &str, mut text: String) -> String {
|
||||||
|
let mut offset = 0;
|
||||||
|
while offset < text.len() {
|
||||||
|
if let Some(text1) = to_subscript1(header, &text, &mut offset) {
|
||||||
|
text = text1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return text;
|
||||||
|
|
||||||
|
/// Looks for `Foo(\d*)` where `header=="Foo"` and replaces the `\d` with subscripts.
|
||||||
|
/// Updates `offset` to point to the next location where we might want to search.
|
||||||
|
/// Returns an updated string if changes were made, else None.
|
||||||
|
fn to_subscript1(header: &str, text: &str, offset: &mut usize) -> Option<String> {
|
||||||
|
let a = match text[*offset..].find(header) {
|
||||||
|
None => { *offset = text.len(); return None; }
|
||||||
|
Some(a) => a + *offset,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Example:
|
||||||
|
//
|
||||||
|
// header: "Foo"
|
||||||
|
// text: ....Foo(123)...
|
||||||
|
// ^ ^
|
||||||
|
// a b
|
||||||
|
|
||||||
|
let b = a + header.len();
|
||||||
|
*offset = b;
|
||||||
|
|
||||||
|
let mut chars = text[b..].chars();
|
||||||
|
if Some('(') != chars.next() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut result = String::new();
|
||||||
|
result.push_str(&text[..b]);
|
||||||
|
|
||||||
|
while let Some(c) = chars.next() {
|
||||||
|
if c == ')' { break; }
|
||||||
|
if !c.is_digit(10) { return None; }
|
||||||
|
|
||||||
|
// 0x208 is _0 in unicode, 0x209 is _1, etc
|
||||||
|
const SUBSCRIPTS: &'static str = "₀₁₂₃₄₅₆₇₈₉";
|
||||||
|
let n = (c as usize) - ('0' as usize);
|
||||||
|
result.extend(SUBSCRIPTS.chars().skip(n).take(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
result.extend(chars);
|
||||||
|
return Some(result);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,382 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
//! The MIR is translated from some high-level abstract IR
|
||||||
|
//! (HAIR). This section defines the HAIR along with a trait for
|
||||||
|
//! accessing it. The intention is to allow MIR construction to be
|
||||||
|
//! unit-tested and separated from the Rust source and compiler data
|
||||||
|
//! structures.
|
||||||
|
|
||||||
|
use repr::{BinOp, BorrowKind, Field, Literal, Mutability, UnOp};
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use std::hash::Hash;
|
||||||
|
|
||||||
|
pub trait Hair: Sized+Debug+Clone+Eq+Hash { // (*)
|
||||||
|
|
||||||
|
// (*) the `Sized` and Debug` bounds are the only ones that really
|
||||||
|
// make sense. The rest are just there so that we can
|
||||||
|
// `#[derive(Clone)]` on things that are parameterized over
|
||||||
|
// `H:HAIR`. It's kind of lame.
|
||||||
|
|
||||||
|
type VarId: Copy+Debug+Eq+Hash; // e.g., NodeId for a variable
|
||||||
|
type DefId: Copy+Debug+Eq+Hash; // e.g., DefId
|
||||||
|
type AdtDef: Copy+Debug+Eq+Hash; // e.g., AdtDef<'tcx>
|
||||||
|
type Name: Copy+Debug+Eq+Hash; // e.g., ast::Name
|
||||||
|
type Ident: Copy+Debug+Eq+Hash; // e.g., ast::Ident
|
||||||
|
type InternedString: Clone+Debug+Eq+Hash; // e.g., InternedString
|
||||||
|
type Bytes: Clone+Debug+Eq+Hash; // e.g., Rc<Vec<u8>>
|
||||||
|
type Span: Copy+Debug+Eq; // e.g., syntax::codemap::Span
|
||||||
|
type Projection: Clone+Debug+Eq; // e.g., ty::ProjectionTy<'tcx>
|
||||||
|
type Substs: Clone+Debug+Eq; // e.g., substs::Substs<'tcx>
|
||||||
|
type ClosureSubsts: Clone+Debug+Eq; // e.g., ty::ClosureSubsts<'tcx>
|
||||||
|
type Ty: Clone+Debug+Eq; // e.g., ty::Ty<'tcx>
|
||||||
|
type Region: Copy+Debug; // e.g., ty::Region
|
||||||
|
type CodeExtent: Copy+Debug+Hash+Eq; // e.g., region::CodeExtent
|
||||||
|
type Pattern: Clone+Debug+Mirror<Self,Output=Pattern<Self>>; // e.g., &P<ast::Pat>
|
||||||
|
type Expr: Clone+Debug+Mirror<Self,Output=Expr<Self>>; // e.g., &P<ast::Expr>
|
||||||
|
type Stmt: Clone+Debug+Mirror<Self,Output=Stmt<Self>>; // e.g., &P<ast::Stmt>
|
||||||
|
type Block: Clone+Debug+Mirror<Self,Output=Block<Self>>; // e.g., &P<ast::Block>
|
||||||
|
type InlineAsm: Clone+Debug+Eq+Hash; // e.g., ast::InlineAsm
|
||||||
|
|
||||||
|
/// Normalizes `ast` into the appropriate `mirror` type.
|
||||||
|
fn mirror<M:Mirror<Self>>(&mut self, ast: M) -> M::Output {
|
||||||
|
ast.make_mirror(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the unit type `()`
|
||||||
|
fn unit_ty(&mut self) -> Self::Ty;
|
||||||
|
|
||||||
|
/// Returns the type `usize`.
|
||||||
|
fn usize_ty(&mut self) -> Self::Ty;
|
||||||
|
|
||||||
|
/// Returns the type `bool`.
|
||||||
|
fn bool_ty(&mut self) -> Self::Ty;
|
||||||
|
|
||||||
|
/// Returns a reference to `PartialEq::<T,T>::eq`
|
||||||
|
fn partial_eq(&mut self, ty: Self::Ty) -> ItemRef<Self>;
|
||||||
|
|
||||||
|
/// Returns a reference to `PartialOrd::<T,T>::le`
|
||||||
|
fn partial_le(&mut self, ty: Self::Ty) -> ItemRef<Self>;
|
||||||
|
|
||||||
|
/// Returns the number of variants for the given enum
|
||||||
|
fn num_variants(&mut self, adt: Self::AdtDef) -> usize;
|
||||||
|
|
||||||
|
fn fields(&mut self, adt: Self::AdtDef, variant_index: usize) -> Vec<Field<Self>>;
|
||||||
|
|
||||||
|
/// true if a value of type `ty` (may) need to be dropped; this
|
||||||
|
/// may return false even for non-Copy types if there is no
|
||||||
|
/// destructor to execute. If correct result is not known, may be
|
||||||
|
/// approximated by returning `true`; this will result in more
|
||||||
|
/// drops but not incorrect code.
|
||||||
|
fn needs_drop(&mut self, ty: Self::Ty, span: Self::Span) -> bool;
|
||||||
|
|
||||||
|
/// Report an internal inconsistency.
|
||||||
|
fn span_bug(&mut self, span: Self::Span, message: &str) -> !;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct ItemRef<H:Hair> {
|
||||||
|
pub ty: H::Ty,
|
||||||
|
pub def_id: H::DefId,
|
||||||
|
pub substs: H::Substs,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Block<H:Hair> {
|
||||||
|
pub extent: H::CodeExtent,
|
||||||
|
pub span: H::Span,
|
||||||
|
pub stmts: Vec<StmtRef<H>>,
|
||||||
|
pub expr: Option<ExprRef<H>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum StmtRef<H:Hair> {
|
||||||
|
Hair(H::Stmt),
|
||||||
|
Mirror(Box<Stmt<H>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Stmt<H:Hair> {
|
||||||
|
pub span: H::Span,
|
||||||
|
pub kind: StmtKind<H>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum StmtKind<H:Hair> {
|
||||||
|
Expr {
|
||||||
|
/// scope for this statement; may be used as lifetime of temporaries
|
||||||
|
scope: H::CodeExtent,
|
||||||
|
|
||||||
|
/// expression being evaluated in this statement
|
||||||
|
expr: ExprRef<H>
|
||||||
|
},
|
||||||
|
|
||||||
|
Let {
|
||||||
|
/// scope for variables bound in this let; covers this and
|
||||||
|
/// remaining statements in block
|
||||||
|
remainder_scope: H::CodeExtent,
|
||||||
|
|
||||||
|
/// scope for the initialization itself; might be used as
|
||||||
|
/// lifetime of temporaries
|
||||||
|
init_scope: H::CodeExtent,
|
||||||
|
|
||||||
|
/// let <PAT> = ...
|
||||||
|
pattern: PatternRef<H>,
|
||||||
|
|
||||||
|
/// let pat = <INIT> ...
|
||||||
|
initializer: Option<ExprRef<H>>,
|
||||||
|
|
||||||
|
/// let pat = init; <STMTS>
|
||||||
|
stmts: Vec<StmtRef<H>>
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// The Hair trait implementor translates their expressions (`H::Expr`)
|
||||||
|
// into instances of this `Expr` enum. This translation can be done
|
||||||
|
// basically as lazilly or as eagerly as desired: every recursive
|
||||||
|
// reference to an expression in this enum is an `ExprRef<H>`, which
|
||||||
|
// may in turn be another instance of this enum (boxed), or else an
|
||||||
|
// untranslated `H::Expr`. Note that instances of `Expr` are very
|
||||||
|
// shortlived. They are created by `Hair::to_expr`, analyzed and
|
||||||
|
// converted into MIR, and then discarded.
|
||||||
|
//
|
||||||
|
// If you compare `Expr` to the full compiler AST, you will see it is
|
||||||
|
// a good bit simpler. In fact, a number of the more straight-forward
|
||||||
|
// MIR simplifications are already done in the impl of `Hair`. For
|
||||||
|
// example, method calls and overloaded operators are absent: they are
|
||||||
|
// expected to be converted into `Expr::Call` instances.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Expr<H:Hair> {
|
||||||
|
// type of this expression
|
||||||
|
pub ty: H::Ty,
|
||||||
|
|
||||||
|
// lifetime of this expression if it should be spilled into a
|
||||||
|
// temporary; should be None only if in a constant context
|
||||||
|
pub temp_lifetime: Option<H::CodeExtent>,
|
||||||
|
|
||||||
|
// span of the expression in the source
|
||||||
|
pub span: H::Span,
|
||||||
|
|
||||||
|
// kind of expression
|
||||||
|
pub kind: ExprKind<H>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ExprKind<H:Hair> {
|
||||||
|
Scope { extent: H::CodeExtent, value: ExprRef<H> },
|
||||||
|
Paren { arg: ExprRef<H> }, // ugh. should be able to remove this!
|
||||||
|
Box { place: Option<ExprRef<H>>, value: ExprRef<H> },
|
||||||
|
Call { fun: ExprRef<H>, args: Vec<ExprRef<H>> },
|
||||||
|
Deref { arg: ExprRef<H> }, // NOT overloaded!
|
||||||
|
Binary { op: BinOp, lhs: ExprRef<H>, rhs: ExprRef<H> }, // NOT overloaded!
|
||||||
|
LogicalOp { op: LogicalOp, lhs: ExprRef<H>, rhs: ExprRef<H> },
|
||||||
|
Unary { op: UnOp, arg: ExprRef<H> }, // NOT overloaded!
|
||||||
|
Cast { source: ExprRef<H> },
|
||||||
|
ReifyFnPointer { source: ExprRef<H> },
|
||||||
|
UnsafeFnPointer { source: ExprRef<H> },
|
||||||
|
Unsize { source: ExprRef<H> },
|
||||||
|
If { condition: ExprRef<H>, then: ExprRef<H>, otherwise: Option<ExprRef<H>> },
|
||||||
|
Loop { condition: Option<ExprRef<H>>, body: ExprRef<H>, },
|
||||||
|
Match { discriminant: ExprRef<H>, arms: Vec<Arm<H>> },
|
||||||
|
Block { body: H::Block },
|
||||||
|
Assign { lhs: ExprRef<H>, rhs: ExprRef<H> },
|
||||||
|
AssignOp { op: BinOp, lhs: ExprRef<H>, rhs: ExprRef<H> },
|
||||||
|
Field { lhs: ExprRef<H>, name: Field<H> },
|
||||||
|
Index { lhs: ExprRef<H>, index: ExprRef<H> },
|
||||||
|
VarRef { id: H::VarId },
|
||||||
|
SelfRef, // first argument, used for self in a closure
|
||||||
|
StaticRef { id: H::DefId },
|
||||||
|
Borrow { region: H::Region, borrow_kind: BorrowKind, arg: ExprRef<H> },
|
||||||
|
Break { label: Option<H::CodeExtent> },
|
||||||
|
Continue { label: Option<H::CodeExtent> },
|
||||||
|
Return { value: Option<ExprRef<H>> },
|
||||||
|
Repeat { value: ExprRef<H>, count: ExprRef<H> },
|
||||||
|
Vec { fields: Vec<ExprRef<H>> },
|
||||||
|
Tuple { fields: Vec<ExprRef<H>> },
|
||||||
|
Adt { adt_def: H::AdtDef,
|
||||||
|
variant_index: usize,
|
||||||
|
substs: H::Substs,
|
||||||
|
fields: Vec<FieldExprRef<H>>,
|
||||||
|
base: Option<ExprRef<H>> },
|
||||||
|
Closure { closure_id: H::DefId, substs: H::ClosureSubsts,
|
||||||
|
upvars: Vec<ExprRef<H>> },
|
||||||
|
Literal { literal: Literal<H> },
|
||||||
|
InlineAsm { asm: H::InlineAsm },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum ExprRef<H:Hair> {
|
||||||
|
Hair(H::Expr),
|
||||||
|
Mirror(Box<Expr<H>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct FieldExprRef<H:Hair> {
|
||||||
|
pub name: Field<H>,
|
||||||
|
pub expr: ExprRef<H>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Arm<H:Hair> {
|
||||||
|
pub patterns: Vec<PatternRef<H>>,
|
||||||
|
pub guard: Option<ExprRef<H>>,
|
||||||
|
pub body: ExprRef<H>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Pattern<H:Hair> {
|
||||||
|
pub ty: H::Ty,
|
||||||
|
pub span: H::Span,
|
||||||
|
pub kind: PatternKind<H>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum LogicalOp {
|
||||||
|
And,
|
||||||
|
Or
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum PatternKind<H:Hair> {
|
||||||
|
Wild,
|
||||||
|
|
||||||
|
// x, ref x, x @ P, etc
|
||||||
|
Binding { mutability: Mutability,
|
||||||
|
name: H::Ident,
|
||||||
|
mode: BindingMode<H>,
|
||||||
|
var: H::VarId,
|
||||||
|
ty: H::Ty,
|
||||||
|
subpattern: Option<PatternRef<H>> },
|
||||||
|
|
||||||
|
// Foo(...) or Foo{...} or Foo, where `Foo` is a variant name from an adt with >1 variants
|
||||||
|
Variant { adt_def: H::AdtDef, variant_index: usize, subpatterns: Vec<FieldPatternRef<H>> },
|
||||||
|
|
||||||
|
// (...), Foo(...), Foo{...}, or Foo, where `Foo` is a variant name from an adt with 1 variant
|
||||||
|
Leaf { subpatterns: Vec<FieldPatternRef<H>> },
|
||||||
|
|
||||||
|
Deref { subpattern: PatternRef<H> }, // box P, &P, &mut P, etc
|
||||||
|
|
||||||
|
Constant { expr: ExprRef<H> },
|
||||||
|
|
||||||
|
Range { lo: ExprRef<H>, hi: ExprRef<H> },
|
||||||
|
|
||||||
|
// matches against a slice, checking the length and extracting elements
|
||||||
|
Slice { prefix: Vec<PatternRef<H>>,
|
||||||
|
slice: Option<PatternRef<H>>,
|
||||||
|
suffix: Vec<PatternRef<H>> },
|
||||||
|
|
||||||
|
// fixed match against an array, irrefutable
|
||||||
|
Array { prefix: Vec<PatternRef<H>>,
|
||||||
|
slice: Option<PatternRef<H>>,
|
||||||
|
suffix: Vec<PatternRef<H>> },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub enum BindingMode<H:Hair> {
|
||||||
|
ByValue,
|
||||||
|
ByRef(H::Region, BorrowKind),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum PatternRef<H:Hair> {
|
||||||
|
Hair(H::Pattern),
|
||||||
|
Mirror(Box<Pattern<H>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct FieldPatternRef<H:Hair> {
|
||||||
|
pub field: Field<H>,
|
||||||
|
pub pattern: PatternRef<H>,
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// The Mirror trait
|
||||||
|
|
||||||
|
/// "Mirroring" is the process of converting from a Hair type into one
|
||||||
|
/// of the types in this file. For example, the mirror of a `H::Expr`
|
||||||
|
/// is an `Expr<H>`. Mirroring is the point at which the actual IR is
|
||||||
|
/// converting into the more idealized representation described in
|
||||||
|
/// this file. Mirroring is gradual: when you mirror an outer
|
||||||
|
/// expression like `e1 + e2`, the references to the inner expressions
|
||||||
|
/// `e1` and `e2` are `ExprRef<H>` instances, and they may or may not
|
||||||
|
/// be eagerly mirrored. This allows a single AST node from the
|
||||||
|
/// compiler to expand into one or more Hair nodes, which lets the Hair
|
||||||
|
/// nodes be simpler.
|
||||||
|
pub trait Mirror<H:Hair> {
|
||||||
|
type Output;
|
||||||
|
|
||||||
|
fn make_mirror(self, hir: &mut H) -> Self::Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Mirror<H> for Expr<H> {
|
||||||
|
type Output = Expr<H>;
|
||||||
|
|
||||||
|
fn make_mirror(self, _: &mut H) -> Expr<H> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Mirror<H> for ExprRef<H> {
|
||||||
|
type Output = Expr<H>;
|
||||||
|
|
||||||
|
fn make_mirror(self, hir: &mut H) -> Expr<H> {
|
||||||
|
match self {
|
||||||
|
ExprRef::Hair(h) => h.make_mirror(hir),
|
||||||
|
ExprRef::Mirror(m) => *m,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Mirror<H> for Stmt<H> {
|
||||||
|
type Output = Stmt<H>;
|
||||||
|
|
||||||
|
fn make_mirror(self, _: &mut H) -> Stmt<H> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Mirror<H> for StmtRef<H> {
|
||||||
|
type Output = Stmt<H>;
|
||||||
|
|
||||||
|
fn make_mirror(self, hir: &mut H) -> Stmt<H> {
|
||||||
|
match self {
|
||||||
|
StmtRef::Hair(h) => h.make_mirror(hir),
|
||||||
|
StmtRef::Mirror(m) => *m,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Mirror<H> for Pattern<H> {
|
||||||
|
type Output = Pattern<H>;
|
||||||
|
|
||||||
|
fn make_mirror(self, _: &mut H) -> Pattern<H> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Mirror<H> for PatternRef<H> {
|
||||||
|
type Output = Pattern<H>;
|
||||||
|
|
||||||
|
fn make_mirror(self, hir: &mut H) -> Pattern<H> {
|
||||||
|
match self {
|
||||||
|
PatternRef::Hair(h) => h.make_mirror(hir),
|
||||||
|
PatternRef::Mirror(m) => *m,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Mirror<H> for Block<H> {
|
||||||
|
type Output = Block<H>;
|
||||||
|
|
||||||
|
fn make_mirror(self, _: &mut H) -> Block<H> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
/*!
|
||||||
|
|
||||||
|
Rust MIR: a lowered representation of Rust. Also: an experiment!
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#![crate_name = "rustc_mir"]
|
||||||
|
#![crate_type = "rlib"]
|
||||||
|
#![crate_type = "dylib"]
|
||||||
|
|
||||||
|
#![feature(ref_slice)]
|
||||||
|
#![feature(rustc_private)]
|
||||||
|
#![feature(into_cow)]
|
||||||
|
|
||||||
|
#[macro_use] extern crate log;
|
||||||
|
extern crate graphviz as dot;
|
||||||
|
extern crate rustc_data_structures;
|
||||||
|
|
||||||
|
pub mod build;
|
||||||
|
pub mod dump;
|
||||||
|
pub mod hair;
|
||||||
|
pub mod repr;
|
||||||
|
mod graphviz;
|
||||||
|
mod tcx;
|
|
@ -0,0 +1,676 @@
|
||||||
|
// Copyright 2014 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.
|
||||||
|
|
||||||
|
use hair::Hair;
|
||||||
|
use rustc_data_structures::fnv::FnvHashMap;
|
||||||
|
use std::fmt::{Debug, Formatter, Error};
|
||||||
|
use std::slice;
|
||||||
|
use std::u32;
|
||||||
|
|
||||||
|
/// Lowered representation of a single function.
|
||||||
|
pub struct Mir<H:Hair> {
|
||||||
|
pub basic_blocks: Vec<BasicBlockData<H>>,
|
||||||
|
|
||||||
|
// for every node id
|
||||||
|
pub extents: FnvHashMap<H::CodeExtent, Vec<GraphExtent>>,
|
||||||
|
|
||||||
|
pub var_decls: Vec<VarDecl<H>>,
|
||||||
|
pub arg_decls: Vec<ArgDecl<H>>,
|
||||||
|
pub temp_decls: Vec<TempDecl<H>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// where execution begins
|
||||||
|
pub const START_BLOCK: BasicBlock = BasicBlock(0);
|
||||||
|
|
||||||
|
/// where execution ends, on normal return
|
||||||
|
pub const END_BLOCK: BasicBlock = BasicBlock(1);
|
||||||
|
|
||||||
|
/// where execution ends, on panic
|
||||||
|
pub const DIVERGE_BLOCK: BasicBlock = BasicBlock(2);
|
||||||
|
|
||||||
|
impl<H:Hair> Mir<H> {
|
||||||
|
pub fn all_basic_blocks(&self) -> Vec<BasicBlock> {
|
||||||
|
(0..self.basic_blocks.len())
|
||||||
|
.map(|i| BasicBlock::new(i))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn basic_block_data(&self, bb: BasicBlock) -> &BasicBlockData<H> {
|
||||||
|
&self.basic_blocks[bb.index()]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn basic_block_data_mut(&mut self, bb: BasicBlock) -> &mut BasicBlockData<H> {
|
||||||
|
&mut self.basic_blocks[bb.index()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Mutability and borrow kinds
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum Mutability {
|
||||||
|
Mut,
|
||||||
|
Not,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum BorrowKind {
|
||||||
|
/// Data must be immutable and is aliasable.
|
||||||
|
Shared,
|
||||||
|
|
||||||
|
/// Data must be immutable but not aliasable. This kind of borrow
|
||||||
|
/// cannot currently be expressed by the user and is used only in
|
||||||
|
/// implicit closure bindings. It is needed when you the closure
|
||||||
|
/// is borrowing or mutating a mutable referent, e.g.:
|
||||||
|
///
|
||||||
|
/// let x: &mut isize = ...;
|
||||||
|
/// let y = || *x += 5;
|
||||||
|
///
|
||||||
|
/// If we were to try to translate this closure into a more explicit
|
||||||
|
/// form, we'd encounter an error with the code as written:
|
||||||
|
///
|
||||||
|
/// struct Env { x: & &mut isize }
|
||||||
|
/// let x: &mut isize = ...;
|
||||||
|
/// let y = (&mut Env { &x }, fn_ptr); // Closure is pair of env and fn
|
||||||
|
/// fn fn_ptr(env: &mut Env) { **env.x += 5; }
|
||||||
|
///
|
||||||
|
/// This is then illegal because you cannot mutate a `&mut` found
|
||||||
|
/// in an aliasable location. To solve, you'd have to translate with
|
||||||
|
/// an `&mut` borrow:
|
||||||
|
///
|
||||||
|
/// struct Env { x: & &mut isize }
|
||||||
|
/// let x: &mut isize = ...;
|
||||||
|
/// let y = (&mut Env { &mut x }, fn_ptr); // changed from &x to &mut x
|
||||||
|
/// fn fn_ptr(env: &mut Env) { **env.x += 5; }
|
||||||
|
///
|
||||||
|
/// Now the assignment to `**env.x` is legal, but creating a
|
||||||
|
/// mutable pointer to `x` is not because `x` is not mutable. We
|
||||||
|
/// could fix this by declaring `x` as `let mut x`. This is ok in
|
||||||
|
/// user code, if awkward, but extra weird for closures, since the
|
||||||
|
/// borrow is hidden.
|
||||||
|
///
|
||||||
|
/// So we introduce a "unique imm" borrow -- the referent is
|
||||||
|
/// immutable, but not aliasable. This solves the problem. For
|
||||||
|
/// simplicity, we don't give users the way to express this
|
||||||
|
/// borrow, it's just used when translating closures.
|
||||||
|
Unique,
|
||||||
|
|
||||||
|
/// Data is mutable and not aliasable.
|
||||||
|
Mut
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Variables and temps
|
||||||
|
|
||||||
|
// A "variable" is a binding declared by the user as part of the fn
|
||||||
|
// decl, a let, etc.
|
||||||
|
pub struct VarDecl<H:Hair> {
|
||||||
|
pub mutability: Mutability,
|
||||||
|
pub name: H::Ident,
|
||||||
|
pub ty: H::Ty,
|
||||||
|
}
|
||||||
|
|
||||||
|
// A "temp" is a temporary that we place on the stack. They are
|
||||||
|
// anonymous, always mutable, and have only a type.
|
||||||
|
pub struct TempDecl<H:Hair> {
|
||||||
|
pub ty: H::Ty,
|
||||||
|
}
|
||||||
|
|
||||||
|
// A "arg" is one of the function's formal arguments. These are
|
||||||
|
// anonymous and distinct from the bindings that the user declares.
|
||||||
|
//
|
||||||
|
// For example, in this function:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// fn foo((x, y): (i32, u32)) { ... }
|
||||||
|
// ```
|
||||||
|
//
|
||||||
|
// there is only one argument, of type `(i32, u32)`, but two bindings
|
||||||
|
// (`x` and `y`).
|
||||||
|
pub struct ArgDecl<H:Hair> {
|
||||||
|
pub ty: H::Ty,
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Graph extents
|
||||||
|
|
||||||
|
/// A moment in the flow of execution. It corresponds to a point in
|
||||||
|
/// between two statements:
|
||||||
|
///
|
||||||
|
/// BB[block]:
|
||||||
|
/// <--- if statement == 0
|
||||||
|
/// STMT[0]
|
||||||
|
/// <--- if statement == 1
|
||||||
|
/// STMT[1]
|
||||||
|
/// ...
|
||||||
|
/// <--- if statement == n-1
|
||||||
|
/// STMT[n-1]
|
||||||
|
/// <--- if statement == n
|
||||||
|
///
|
||||||
|
/// where the block has `n` statements.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub struct ExecutionPoint {
|
||||||
|
pub block: BasicBlock,
|
||||||
|
pub statement: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A single-entry-multiple-exit region in the graph. We build one of
|
||||||
|
/// these for every node-id during MIR construction. By construction
|
||||||
|
/// we are assured that the entry dominates all points within, and
|
||||||
|
/// that, for every interior point X, it is postdominated by some exit.
|
||||||
|
pub struct GraphExtent {
|
||||||
|
pub entry: ExecutionPoint,
|
||||||
|
pub exit: GraphExtentExit,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum GraphExtentExit {
|
||||||
|
/// `Statement(X)`: a very common special case covering a span
|
||||||
|
/// that is local to a single block. It starts at the entry point
|
||||||
|
/// and extends until the start of statement `X` (non-inclusive).
|
||||||
|
Statement(u32),
|
||||||
|
|
||||||
|
/// The more general case where the exits are a set of points.
|
||||||
|
Points(Vec<ExecutionPoint>),
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// BasicBlock
|
||||||
|
|
||||||
|
/// The index of a particular basic block. The index is into the `basic_blocks`
|
||||||
|
/// list of the `Mir`.
|
||||||
|
///
|
||||||
|
/// (We use a `u32` internally just to save memory.)
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
pub struct BasicBlock(u32);
|
||||||
|
|
||||||
|
impl BasicBlock {
|
||||||
|
pub fn new(index: usize) -> BasicBlock {
|
||||||
|
assert!(index < (u32::MAX as usize));
|
||||||
|
BasicBlock(index as u32)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extract the index.
|
||||||
|
pub fn index(self) -> usize {
|
||||||
|
self.0 as usize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for BasicBlock {
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
|
||||||
|
write!(fmt, "BB({})", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// BasicBlock and Terminator
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct BasicBlockData<H:Hair> {
|
||||||
|
pub statements: Vec<Statement<H>>,
|
||||||
|
pub terminator: Terminator<H>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum Terminator<H:Hair> {
|
||||||
|
/// block should have one successor in the graph; we jump there
|
||||||
|
Goto { target: BasicBlock },
|
||||||
|
|
||||||
|
/// block should initiate unwinding; should be one successor
|
||||||
|
/// that does cleanup and branches to DIVERGE_BLOCK
|
||||||
|
Panic { target: BasicBlock },
|
||||||
|
|
||||||
|
/// jump to branch 0 if this lvalue evaluates to true
|
||||||
|
If { cond: Operand<H>, targets: [BasicBlock; 2] },
|
||||||
|
|
||||||
|
/// lvalue evaluates to some enum; jump depending on the branch
|
||||||
|
Switch { discr: Lvalue<H>, targets: Vec<BasicBlock> },
|
||||||
|
|
||||||
|
/// Indicates that the last statement in the block panics, aborts,
|
||||||
|
/// etc. No successors. This terminator appears on exactly one
|
||||||
|
/// basic block which we create in advance. However, during
|
||||||
|
/// construction, we use this value as a sentinel for "terminator
|
||||||
|
/// not yet assigned", and assert at the end that only the
|
||||||
|
/// well-known diverging block actually diverges.
|
||||||
|
Diverge,
|
||||||
|
|
||||||
|
/// Indicates a normal return. The ReturnPointer lvalue should
|
||||||
|
/// have been filled in by now. This should only occur in the
|
||||||
|
/// `END_BLOCK`.
|
||||||
|
Return,
|
||||||
|
|
||||||
|
/// block ends with a call; it should have two successors. The
|
||||||
|
/// first successor indicates normal return. The second indicates
|
||||||
|
/// unwinding.
|
||||||
|
Call { data: CallData<H>, targets: [BasicBlock; 2] },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Terminator<H> {
|
||||||
|
pub fn successors(&self) -> &[BasicBlock] {
|
||||||
|
use self::Terminator::*;
|
||||||
|
match *self {
|
||||||
|
Goto { target: ref b } => slice::ref_slice(b),
|
||||||
|
Panic { target: ref b } => slice::ref_slice(b),
|
||||||
|
If { cond: _, targets: ref b } => b,
|
||||||
|
Switch { discr: _, targets: ref b } => b,
|
||||||
|
Diverge => &[],
|
||||||
|
Return => &[],
|
||||||
|
Call { data: _, targets: ref b } => b,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CallData<H:Hair> {
|
||||||
|
/// where the return value is written to
|
||||||
|
pub destination: Lvalue<H>,
|
||||||
|
|
||||||
|
/// the fn being called
|
||||||
|
pub func: Lvalue<H>,
|
||||||
|
|
||||||
|
/// the arguments
|
||||||
|
pub args: Vec<Lvalue<H>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> BasicBlockData<H> {
|
||||||
|
pub fn new(terminator: Terminator<H>) -> BasicBlockData<H> {
|
||||||
|
BasicBlockData {
|
||||||
|
statements: vec![],
|
||||||
|
terminator: terminator,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Debug for Terminator<H> {
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
|
||||||
|
use self::Terminator::*;
|
||||||
|
match *self {
|
||||||
|
Goto { target } =>
|
||||||
|
write!(fmt, "goto -> {:?}", target),
|
||||||
|
Panic { target } =>
|
||||||
|
write!(fmt, "panic -> {:?}", target),
|
||||||
|
If { cond: ref lv, ref targets } =>
|
||||||
|
write!(fmt, "if({:?}) -> {:?}", lv, targets),
|
||||||
|
Switch { discr: ref lv, ref targets } =>
|
||||||
|
write!(fmt, "switch({:?}) -> {:?}", lv, targets),
|
||||||
|
Diverge =>
|
||||||
|
write!(fmt, "diverge"),
|
||||||
|
Return =>
|
||||||
|
write!(fmt, "return"),
|
||||||
|
Call { data: ref c, targets } => {
|
||||||
|
try!(write!(fmt, "{:?} = {:?}(", c.destination, c.func));
|
||||||
|
for (index, arg) in c.args.iter().enumerate() {
|
||||||
|
if index > 0 { try!(write!(fmt, ", ")); }
|
||||||
|
try!(write!(fmt, "{:?}", arg));
|
||||||
|
}
|
||||||
|
write!(fmt, ") -> {:?}", targets)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Statements
|
||||||
|
|
||||||
|
pub struct Statement<H:Hair> {
|
||||||
|
pub span: H::Span,
|
||||||
|
pub kind: StatementKind<H>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum StatementKind<H:Hair> {
|
||||||
|
Assign(Lvalue<H>, Rvalue<H>),
|
||||||
|
Drop(DropKind, Lvalue<H>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum DropKind {
|
||||||
|
Shallow,
|
||||||
|
Deep
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Debug for Statement<H> {
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
|
||||||
|
use self::StatementKind::*;
|
||||||
|
match self.kind {
|
||||||
|
Assign(ref lv, ref rv) => write!(fmt, "{:?} = {:?}", lv, rv),
|
||||||
|
Drop(DropKind::Shallow, ref lv) => write!(fmt, "shallow_drop {:?}", lv),
|
||||||
|
Drop(DropKind::Deep, ref lv) => write!(fmt, "drop {:?}", lv),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Lvalues
|
||||||
|
|
||||||
|
/// A path to a value; something that can be evaluated without
|
||||||
|
/// changing or disturbing program state.
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum Lvalue<H:Hair> {
|
||||||
|
/// local variable declared by the user
|
||||||
|
Var(u32),
|
||||||
|
|
||||||
|
/// temporary introduced during lowering into MIR
|
||||||
|
Temp(u32),
|
||||||
|
|
||||||
|
/// formal parameter of the function; note that these are NOT the
|
||||||
|
/// bindings that the user declares, which are vars
|
||||||
|
Arg(u32),
|
||||||
|
|
||||||
|
/// static or static mut variable
|
||||||
|
Static(H::DefId),
|
||||||
|
|
||||||
|
/// the return pointer of the fn
|
||||||
|
ReturnPointer,
|
||||||
|
|
||||||
|
/// projection out of an lvalue (access a field, deref a pointer, etc)
|
||||||
|
Projection(Box<LvalueProjection<H>>)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The `Projection` data structure defines things of the form `B.x`
|
||||||
|
/// or `*B` or `B[index]`. Note that it is parameterized because it is
|
||||||
|
/// shared between `Constant` and `Lvalue`. See the aliases
|
||||||
|
/// `LvalueProjection` etc below.
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct Projection<H:Hair,B,V> {
|
||||||
|
pub base: B,
|
||||||
|
pub elem: ProjectionElem<H,V>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum ProjectionElem<H:Hair,V> {
|
||||||
|
Deref,
|
||||||
|
Field(Field<H>),
|
||||||
|
Index(V),
|
||||||
|
|
||||||
|
// These indices are generated by slice patterns. Easiest to explain
|
||||||
|
// by example:
|
||||||
|
//
|
||||||
|
// ```
|
||||||
|
// [X, _, .._, _, _] => { offset: 0, min_length: 4, from_end: false },
|
||||||
|
// [_, X, .._, _, _] => { offset: 1, min_length: 4, from_end: false },
|
||||||
|
// [_, _, .._, X, _] => { offset: 2, min_length: 4, from_end: true },
|
||||||
|
// [_, _, .._, _, X] => { offset: 1, min_length: 4, from_end: true },
|
||||||
|
// ```
|
||||||
|
ConstantIndex {
|
||||||
|
offset: u32, // index or -index (in Python terms), depending on from_end
|
||||||
|
min_length: u32, // thing being indexed must be at least this long
|
||||||
|
from_end: bool, // counting backwards from end?
|
||||||
|
},
|
||||||
|
|
||||||
|
// "Downcast" to a variant of an ADT. Currently, we only introduce
|
||||||
|
// this for ADTs with more than one variant. It may be better to
|
||||||
|
// just introduce it always, or always for enums.
|
||||||
|
Downcast(H::AdtDef, usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Alias for projections as they appear in lvalues, where the base is an lvalue
|
||||||
|
/// and the index is an operand.
|
||||||
|
pub type LvalueProjection<H> =
|
||||||
|
Projection<H,Lvalue<H>,Operand<H>>;
|
||||||
|
|
||||||
|
/// Alias for projections as they appear in lvalues, where the base is an lvalue
|
||||||
|
/// and the index is an operand.
|
||||||
|
pub type LvalueElem<H> =
|
||||||
|
ProjectionElem<H,Operand<H>>;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum Field<H:Hair> {
|
||||||
|
Named(H::Name),
|
||||||
|
Indexed(usize),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Lvalue<H> {
|
||||||
|
pub fn field(self, f: Field<H>) -> Lvalue<H> {
|
||||||
|
self.elem(ProjectionElem::Field(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deref(self) -> Lvalue<H> {
|
||||||
|
self.elem(ProjectionElem::Deref)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn index(self, index: Operand<H>) -> Lvalue<H> {
|
||||||
|
self.elem(ProjectionElem::Index(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn elem(self, elem: LvalueElem<H>) -> Lvalue<H> {
|
||||||
|
Lvalue::Projection(Box::new(LvalueProjection { base: self, elem: elem }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Debug for Lvalue<H> {
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
|
||||||
|
use self::Lvalue::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Var(id) =>
|
||||||
|
write!(fmt,"Var({:?})", id),
|
||||||
|
Arg(id) =>
|
||||||
|
write!(fmt,"Arg({:?})", id),
|
||||||
|
Temp(id) =>
|
||||||
|
write!(fmt,"Temp({:?})", id),
|
||||||
|
Static(id) =>
|
||||||
|
write!(fmt,"Static({:?})", id),
|
||||||
|
ReturnPointer =>
|
||||||
|
write!(fmt,"ReturnPointer"),
|
||||||
|
Projection(ref data) =>
|
||||||
|
match data.elem {
|
||||||
|
ProjectionElem::Downcast(_, variant_index) =>
|
||||||
|
write!(fmt,"({:?} as {:?})", data.base, variant_index),
|
||||||
|
ProjectionElem::Deref =>
|
||||||
|
write!(fmt,"(*{:?})", data.base),
|
||||||
|
ProjectionElem::Field(Field::Named(name)) =>
|
||||||
|
write!(fmt,"{:?}.{:?}", data.base, name),
|
||||||
|
ProjectionElem::Field(Field::Indexed(index)) =>
|
||||||
|
write!(fmt,"{:?}.{:?}", data.base, index),
|
||||||
|
ProjectionElem::Index(ref index) =>
|
||||||
|
write!(fmt,"{:?}[{:?}]", data.base, index),
|
||||||
|
ProjectionElem::ConstantIndex { offset, min_length, from_end: false } =>
|
||||||
|
write!(fmt,"{:?}[{:?}; {:?}]", data.base, offset, min_length),
|
||||||
|
ProjectionElem::ConstantIndex { offset, min_length, from_end: true } =>
|
||||||
|
write!(fmt,"{:?}[-{:?}; {:?}]", data.base, offset, min_length),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Operands
|
||||||
|
//
|
||||||
|
// These are values that can appear inside an rvalue (or an index
|
||||||
|
// lvalue). They are intentionally limited to prevent rvalues from
|
||||||
|
// being nested in one another.
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub enum Operand<H:Hair> {
|
||||||
|
Consume(Lvalue<H>),
|
||||||
|
Constant(Constant<H>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Debug for Operand<H> {
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
|
||||||
|
use self::Operand::*;
|
||||||
|
match *self {
|
||||||
|
Constant(ref a) => write!(fmt, "{:?}", a),
|
||||||
|
Consume(ref lv) => write!(fmt, "{:?}", lv),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Rvalues
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum Rvalue<H:Hair> {
|
||||||
|
// x (either a move or copy, depending on type of x)
|
||||||
|
Use(Operand<H>),
|
||||||
|
|
||||||
|
// [x; 32]
|
||||||
|
Repeat(Operand<H>, Operand<H>),
|
||||||
|
|
||||||
|
// &x or &mut x
|
||||||
|
Ref(H::Region, BorrowKind, Lvalue<H>),
|
||||||
|
|
||||||
|
// length of a [X] or [X;n] value
|
||||||
|
Len(Lvalue<H>),
|
||||||
|
|
||||||
|
Cast(CastKind, Operand<H>, H::Ty),
|
||||||
|
|
||||||
|
BinaryOp(BinOp, Operand<H>, Operand<H>),
|
||||||
|
|
||||||
|
UnaryOp(UnOp, Operand<H>),
|
||||||
|
|
||||||
|
// Creates an *uninitialized* Box
|
||||||
|
Box(H::Ty),
|
||||||
|
|
||||||
|
// Create an aggregate value, like a tuple or struct. This is
|
||||||
|
// only needed because we want to distinguish `dest = Foo { x:
|
||||||
|
// ..., y: ... }` from `dest.x = ...; dest.y = ...;` in the case
|
||||||
|
// that `Foo` has a destructor. These rvalues can be optimized
|
||||||
|
// away after type-checking and before lowering.
|
||||||
|
Aggregate(AggregateKind<H>, Vec<Operand<H>>),
|
||||||
|
|
||||||
|
InlineAsm(H::InlineAsm),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum CastKind {
|
||||||
|
Misc,
|
||||||
|
|
||||||
|
/// Convert unique, zero-sized type for a fn to fn()
|
||||||
|
ReifyFnPointer,
|
||||||
|
|
||||||
|
/// Convert safe fn() to unsafe fn()
|
||||||
|
UnsafeFnPointer,
|
||||||
|
|
||||||
|
/// "Unsize" -- convert a thin-or-fat pointer to a fat pointer.
|
||||||
|
/// trans must figure out the details once full monomorphization
|
||||||
|
/// is known. For example, this could be used to cast from a
|
||||||
|
/// `&[i32;N]` to a `&[i32]`, or a `Box<T>` to a `Box<Trait>`
|
||||||
|
/// (presuming `T: Trait`).
|
||||||
|
Unsize,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum AggregateKind<H:Hair> {
|
||||||
|
Vec,
|
||||||
|
Tuple,
|
||||||
|
Adt(H::AdtDef, usize, H::Substs),
|
||||||
|
Closure(H::DefId, H::ClosureSubsts),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum BinOp {
|
||||||
|
/// The `+` operator (addition)
|
||||||
|
Add,
|
||||||
|
/// The `-` operator (subtraction)
|
||||||
|
Sub,
|
||||||
|
/// The `*` operator (multiplication)
|
||||||
|
Mul,
|
||||||
|
/// The `/` operator (division)
|
||||||
|
Div,
|
||||||
|
/// The `%` operator (modulus)
|
||||||
|
Rem,
|
||||||
|
/// The `^` operator (bitwise xor)
|
||||||
|
BitXor,
|
||||||
|
/// The `&` operator (bitwise and)
|
||||||
|
BitAnd,
|
||||||
|
/// The `|` operator (bitwise or)
|
||||||
|
BitOr,
|
||||||
|
/// The `<<` operator (shift left)
|
||||||
|
Shl,
|
||||||
|
/// The `>>` operator (shift right)
|
||||||
|
Shr,
|
||||||
|
/// The `==` operator (equality)
|
||||||
|
Eq,
|
||||||
|
/// The `<` operator (less than)
|
||||||
|
Lt,
|
||||||
|
/// The `<=` operator (less than or equal to)
|
||||||
|
Le,
|
||||||
|
/// The `!=` operator (not equal to)
|
||||||
|
Ne,
|
||||||
|
/// The `>=` operator (greater than or equal to)
|
||||||
|
Ge,
|
||||||
|
/// The `>` operator (greater than)
|
||||||
|
Gt,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||||
|
pub enum UnOp {
|
||||||
|
/// The `!` operator for logical inversion
|
||||||
|
Not,
|
||||||
|
/// The `-` operator for negation
|
||||||
|
Neg
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:Hair> Debug for Rvalue<H> {
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
|
||||||
|
use self::Rvalue::*;
|
||||||
|
|
||||||
|
match *self {
|
||||||
|
Use(ref lvalue) => write!(fmt, "{:?}", lvalue),
|
||||||
|
Repeat(ref a, ref b) => write!(fmt, "[{:?}; {:?}]", a, b),
|
||||||
|
Ref(ref a, bk, ref b) => write!(fmt, "&{:?} {:?} {:?}", a, bk, b),
|
||||||
|
Len(ref a) => write!(fmt, "LEN({:?})", a),
|
||||||
|
Cast(ref kind, ref lv, ref ty) => write!(fmt, "{:?} as {:?} ({:?}", lv, ty, kind),
|
||||||
|
BinaryOp(ref op, ref a, ref b) => write!(fmt, "{:?}({:?},{:?})", op, a, b),
|
||||||
|
UnaryOp(ref op, ref a) => write!(fmt, "{:?}({:?})", op, a),
|
||||||
|
Box(ref t) => write!(fmt, "Box {:?}", t),
|
||||||
|
Aggregate(ref kind, ref lvs) => write!(fmt, "Aggregate<{:?}>({:?})", kind, lvs),
|
||||||
|
InlineAsm(ref asm) => write!(fmt, "InlineAsm({:?})", asm),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constants
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub struct Constant<H:Hair> {
|
||||||
|
pub span: H::Span,
|
||||||
|
pub kind: ConstantKind<H>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum ConstantKind<H:Hair> {
|
||||||
|
Literal(Literal<H>),
|
||||||
|
Aggregate(AggregateKind<H>, Vec<Constant<H>>),
|
||||||
|
Call(Box<Constant<H>>, Vec<Constant<H>>),
|
||||||
|
Cast(Box<Constant<H>>, H::Ty),
|
||||||
|
Repeat(Box<Constant<H>>, Box<Constant<H>>),
|
||||||
|
Ref(BorrowKind, Box<Constant<H>>),
|
||||||
|
BinaryOp(BinOp, Box<Constant<H>>, Box<Constant<H>>),
|
||||||
|
UnaryOp(UnOp, Box<Constant<H>>),
|
||||||
|
Projection(Box<ConstantProjection<H>>)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ConstantProjection<H> =
|
||||||
|
Projection<H,Constant<H>,Constant<H>>;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
pub enum Literal<H:Hair> {
|
||||||
|
Item { def_id: H::DefId, substs: H::Substs },
|
||||||
|
Projection { projection: H::Projection },
|
||||||
|
Int { bits: IntegralBits, value: i64 },
|
||||||
|
Uint { bits: IntegralBits, value: u64 },
|
||||||
|
Float { bits: FloatBits, value: f64 },
|
||||||
|
Char { c: char },
|
||||||
|
Bool { value: bool },
|
||||||
|
Bytes { value: H::Bytes },
|
||||||
|
String { value: H::InternedString },
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
pub enum IntegralBits {
|
||||||
|
B8, B16, B32, B64, BSize
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
|
||||||
|
pub enum FloatBits {
|
||||||
|
F32, F64
|
||||||
|
}
|
|
@ -0,0 +1,114 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use hair::*;
|
||||||
|
|
||||||
|
use tcx::Cx;
|
||||||
|
use tcx::pattern::PatNode;
|
||||||
|
use tcx::rustc::middle::region::{BlockRemainder, CodeExtentData};
|
||||||
|
use tcx::rustc_front::hir;
|
||||||
|
use tcx::syntax::ast;
|
||||||
|
use tcx::syntax::ptr::P;
|
||||||
|
use tcx::to_ref::ToRef;
|
||||||
|
|
||||||
|
impl<'a,'tcx:'a> Mirror<Cx<'a,'tcx>> for &'tcx hir::Block {
|
||||||
|
type Output = Block<Cx<'a,'tcx>>;
|
||||||
|
|
||||||
|
fn make_mirror(self, cx: &mut Cx<'a,'tcx>) -> Block<Cx<'a,'tcx>> {
|
||||||
|
// We have to eagerly translate the "spine" of the statements
|
||||||
|
// in order to get the lexical scoping correctly.
|
||||||
|
let stmts = mirror_stmts(cx, self.id, self.stmts.iter().enumerate());
|
||||||
|
Block {
|
||||||
|
extent: cx.tcx.region_maps.node_extent(self.id),
|
||||||
|
span: self.span,
|
||||||
|
stmts: stmts,
|
||||||
|
expr: self.expr.to_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'tcx:'a> Mirror<Cx<'a,'tcx>> for &'tcx hir::Stmt {
|
||||||
|
type Output = Stmt<Cx<'a,'tcx>>;
|
||||||
|
|
||||||
|
fn make_mirror(self, _cx: &mut Cx<'a,'tcx>) -> Stmt<Cx<'a,'tcx>> {
|
||||||
|
// In order to get the scoping correct, we eagerly mirror
|
||||||
|
// statements when we translate the enclosing block, so we
|
||||||
|
// should in fact never get to this point.
|
||||||
|
panic!("statements are eagerly mirrored");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mirror_stmts<'a,'tcx:'a,STMTS>(cx: &mut Cx<'a,'tcx>,
|
||||||
|
block_id: ast::NodeId,
|
||||||
|
mut stmts: STMTS)
|
||||||
|
-> Vec<StmtRef<Cx<'a,'tcx>>>
|
||||||
|
where STMTS: Iterator<Item=(usize, &'tcx P<hir::Stmt>)>
|
||||||
|
{
|
||||||
|
let mut result = vec![];
|
||||||
|
while let Some((index, stmt)) = stmts.next() {
|
||||||
|
match stmt.node {
|
||||||
|
hir::StmtExpr(ref expr, id) | hir::StmtSemi(ref expr, id) =>
|
||||||
|
result.push(
|
||||||
|
StmtRef::Mirror(
|
||||||
|
Box::new(Stmt { span: stmt.span,
|
||||||
|
kind: StmtKind::Expr {
|
||||||
|
scope: cx.tcx.region_maps.node_extent(id),
|
||||||
|
expr: expr.to_ref() } }))),
|
||||||
|
|
||||||
|
hir::StmtDecl(ref decl, id) => {
|
||||||
|
match decl.node {
|
||||||
|
hir::DeclItem(..) => { /* ignore for purposes of the MIR */ }
|
||||||
|
hir::DeclLocal(ref local) => {
|
||||||
|
let remainder_extent = CodeExtentData::Remainder(BlockRemainder {
|
||||||
|
block: block_id,
|
||||||
|
first_statement_index: index as u32
|
||||||
|
});
|
||||||
|
let remainder_extent =
|
||||||
|
cx.tcx.region_maps.lookup_code_extent(remainder_extent);
|
||||||
|
|
||||||
|
// pull in all following statements, since
|
||||||
|
// they are within the scope of this let:
|
||||||
|
let following_stmts = mirror_stmts(cx, block_id, stmts);
|
||||||
|
|
||||||
|
result.push(
|
||||||
|
StmtRef::Mirror(
|
||||||
|
Box::new(Stmt {
|
||||||
|
span: stmt.span,
|
||||||
|
kind: StmtKind::Let {
|
||||||
|
remainder_scope: remainder_extent,
|
||||||
|
init_scope: cx.tcx.region_maps.node_extent(id),
|
||||||
|
pattern: PatNode::irrefutable(&local.pat).to_ref(),
|
||||||
|
initializer: local.init.to_ref(),
|
||||||
|
stmts: following_stmts
|
||||||
|
}
|
||||||
|
})));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_expr_ref<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
|
||||||
|
block: &'tcx hir::Block)
|
||||||
|
-> ExprRef<Cx<'a, 'tcx>> {
|
||||||
|
let block_ty = cx.tcx.node_id_to_type(block.id);
|
||||||
|
let temp_lifetime = cx.tcx.region_maps.temporary_scope(block.id);
|
||||||
|
let expr = Expr {
|
||||||
|
ty: block_ty,
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
span: block.span,
|
||||||
|
kind: ExprKind::Block { body: block }
|
||||||
|
};
|
||||||
|
expr.to_ref()
|
||||||
|
}
|
|
@ -0,0 +1,870 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
use rustc_data_structures::fnv::FnvHashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use tcx::Cx;
|
||||||
|
use tcx::block;
|
||||||
|
use tcx::pattern::PatNode;
|
||||||
|
use tcx::rustc::front::map;
|
||||||
|
use tcx::rustc::middle::def;
|
||||||
|
use tcx::rustc::middle::def_id::DefId;
|
||||||
|
use tcx::rustc::middle::region::CodeExtent;
|
||||||
|
use tcx::rustc::middle::pat_util;
|
||||||
|
use tcx::rustc::middle::ty::{self, Ty};
|
||||||
|
use tcx::rustc_front::hir;
|
||||||
|
use tcx::rustc_front::util as hir_util;
|
||||||
|
use tcx::syntax::codemap::Span;
|
||||||
|
use tcx::syntax::parse::token;
|
||||||
|
use tcx::syntax::ptr::P;
|
||||||
|
use tcx::to_ref::ToRef;
|
||||||
|
|
||||||
|
impl<'a,'tcx:'a> Mirror<Cx<'a,'tcx>> for &'tcx hir::Expr {
|
||||||
|
type Output = Expr<Cx<'a,'tcx>>;
|
||||||
|
|
||||||
|
fn make_mirror(self, cx: &mut Cx<'a,'tcx>) -> Expr<Cx<'a,'tcx>> {
|
||||||
|
debug!("Expr::make_mirror(): id={}, span={:?}", self.id, self.span);
|
||||||
|
|
||||||
|
let expr_ty = cx.tcx.expr_ty(self); // note: no adjustments (yet)!
|
||||||
|
|
||||||
|
let kind = match self.node {
|
||||||
|
// Here comes the interesting stuff:
|
||||||
|
|
||||||
|
hir::ExprMethodCall(_, _, ref args) => {
|
||||||
|
// Rewrite a.b(c) into UFCS form like Trait::b(a, c)
|
||||||
|
let expr = method_callee(cx, self, ty::MethodCall::expr(self.id));
|
||||||
|
let args = args.iter()
|
||||||
|
.map(|e| e.to_ref())
|
||||||
|
.collect();
|
||||||
|
ExprKind::Call {
|
||||||
|
fun: expr.to_ref(),
|
||||||
|
args: args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprAddrOf(mutbl, ref expr) => {
|
||||||
|
let region = match expr_ty.sty {
|
||||||
|
ty::TyRef(r, _) => r,
|
||||||
|
_ => cx.tcx.sess.span_bug(expr.span, "type of & not region")
|
||||||
|
};
|
||||||
|
ExprKind::Borrow { region: *region,
|
||||||
|
borrow_kind: to_borrow_kind(mutbl),
|
||||||
|
arg: expr.to_ref() }
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprBlock(ref blk) => {
|
||||||
|
ExprKind::Block {
|
||||||
|
body: &**blk
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprAssign(ref lhs, ref rhs) => {
|
||||||
|
ExprKind::Assign {
|
||||||
|
lhs: lhs.to_ref(),
|
||||||
|
rhs: rhs.to_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprLit(ref lit) => {
|
||||||
|
let literal = convert_literal(cx, self.span, expr_ty, lit);
|
||||||
|
ExprKind::Literal { literal: literal }
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprBinary(op, ref lhs, ref rhs) => {
|
||||||
|
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 {
|
||||||
|
// FIXME overflow
|
||||||
|
match op.node {
|
||||||
|
hir::BinOp_::BiAnd => {
|
||||||
|
ExprKind::LogicalOp { op: LogicalOp::And,
|
||||||
|
lhs: lhs.to_ref(),
|
||||||
|
rhs: rhs.to_ref() }
|
||||||
|
}
|
||||||
|
hir::BinOp_::BiOr => {
|
||||||
|
ExprKind::LogicalOp { op: LogicalOp::Or,
|
||||||
|
lhs: lhs.to_ref(),
|
||||||
|
rhs: rhs.to_ref() }
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let op = bin_op(op.node);
|
||||||
|
ExprKind::Binary { op: op,
|
||||||
|
lhs: lhs.to_ref(),
|
||||||
|
rhs: rhs.to_ref() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprIndex(ref lhs, ref index) => {
|
||||||
|
if cx.tcx.is_method_call(self.id) {
|
||||||
|
overloaded_lvalue(cx, self, ty::MethodCall::expr(self.id),
|
||||||
|
PassArgs::ByValue, lhs.to_ref(), vec![index])
|
||||||
|
} else {
|
||||||
|
ExprKind::Index { lhs: lhs.to_ref(),
|
||||||
|
index: index.to_ref() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprUnary(hir::UnOp::UnDeref, ref arg) => {
|
||||||
|
if cx.tcx.is_method_call(self.id) {
|
||||||
|
overloaded_lvalue(cx, self, ty::MethodCall::expr(self.id),
|
||||||
|
PassArgs::ByValue, arg.to_ref(), vec![])
|
||||||
|
} else {
|
||||||
|
ExprKind::Deref { arg: arg.to_ref() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprUnary(hir::UnOp::UnUniq, ref arg) => {
|
||||||
|
assert!(!cx.tcx.is_method_call(self.id));
|
||||||
|
ExprKind::Box { place: None, value: arg.to_ref() }
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprUnary(op, ref arg) => {
|
||||||
|
if cx.tcx.is_method_call(self.id) {
|
||||||
|
overloaded_operator(cx, self, ty::MethodCall::expr(self.id),
|
||||||
|
PassArgs::ByValue, arg.to_ref(), vec![])
|
||||||
|
} else {
|
||||||
|
// FIXME overflow
|
||||||
|
let op = match op {
|
||||||
|
hir::UnOp::UnNot => UnOp::Not,
|
||||||
|
hir::UnOp::UnNeg => UnOp::Neg,
|
||||||
|
hir::UnOp::UnUniq | hir::UnOp::UnDeref => {
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
self.span,
|
||||||
|
&format!("operator should have been handled elsewhere {:?}", op));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
ExprKind::Unary { op: op, arg: arg.to_ref() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprStruct(_, ref fields, ref base) => {
|
||||||
|
match expr_ty.sty {
|
||||||
|
ty::TyStruct(adt, substs) => {
|
||||||
|
ExprKind::Adt {
|
||||||
|
adt_def: adt,
|
||||||
|
variant_index: 0,
|
||||||
|
substs: substs,
|
||||||
|
fields: fields.to_ref(),
|
||||||
|
base: base.to_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::TyEnum(adt, substs) => {
|
||||||
|
match cx.tcx.def_map.borrow()[&self.id].full_def() {
|
||||||
|
def::DefVariant(enum_id, variant_id, true) => {
|
||||||
|
debug_assert!(adt.did == enum_id);
|
||||||
|
let index = adt.variant_index_with_id(variant_id);
|
||||||
|
ExprKind::Adt {
|
||||||
|
adt_def: adt,
|
||||||
|
variant_index: index,
|
||||||
|
substs: substs,
|
||||||
|
fields: fields.to_ref(),
|
||||||
|
base: base.to_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ref def => {
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
self.span,
|
||||||
|
&format!("unexpected def: {:?}", def));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
self.span,
|
||||||
|
&format!("unexpected type for struct literal: {:?}", expr_ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprClosure(..) => {
|
||||||
|
let closure_ty = cx.tcx.expr_ty(self);
|
||||||
|
let (def_id, substs) = match closure_ty.sty {
|
||||||
|
ty::TyClosure(def_id, ref substs) => (def_id, substs),
|
||||||
|
_ => {
|
||||||
|
cx.tcx.sess.span_bug(self.span,
|
||||||
|
&format!("closure expr w/o closure type: {:?}",
|
||||||
|
closure_ty));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let upvars = cx.tcx.with_freevars(self.id, |freevars| {
|
||||||
|
freevars.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, fv)| capture_freevar(cx, self, fv, substs.upvar_tys[i]))
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
ExprKind::Closure {
|
||||||
|
closure_id: def_id,
|
||||||
|
substs: &**substs,
|
||||||
|
upvars: upvars,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprRange(ref start, ref end) => {
|
||||||
|
let range_ty = cx.tcx.expr_ty(self);
|
||||||
|
let (adt_def, substs) = match range_ty.sty {
|
||||||
|
ty::TyStruct(adt_def, substs) => (adt_def, substs),
|
||||||
|
_ => {
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
self.span,
|
||||||
|
&format!("unexpanded ast"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let field_expr_ref = |s: &'tcx P<hir::Expr>, nm: &str| {
|
||||||
|
FieldExprRef { name: Field::Named(token::intern(nm)),
|
||||||
|
expr: s.to_ref() }
|
||||||
|
};
|
||||||
|
|
||||||
|
let start_field = start.as_ref()
|
||||||
|
.into_iter()
|
||||||
|
.map(|s| field_expr_ref(s, "start"));
|
||||||
|
|
||||||
|
let end_field = end.as_ref()
|
||||||
|
.into_iter()
|
||||||
|
.map(|e| field_expr_ref(e, "end"));
|
||||||
|
|
||||||
|
ExprKind::Adt { adt_def: adt_def,
|
||||||
|
variant_index: 0,
|
||||||
|
substs: substs,
|
||||||
|
fields: start_field.chain(end_field).collect(),
|
||||||
|
base: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprPath(..) => {
|
||||||
|
convert_path_expr(cx, self)
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::ExprInlineAsm(ref asm) => {
|
||||||
|
ExprKind::InlineAsm { asm: asm }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now comes the rote stuff:
|
||||||
|
|
||||||
|
hir::ExprParen(ref p) =>
|
||||||
|
ExprKind::Paren { arg: p.to_ref() },
|
||||||
|
hir::ExprRepeat(ref v, ref c) =>
|
||||||
|
ExprKind::Repeat { value: v.to_ref(), count: c.to_ref() },
|
||||||
|
hir::ExprRet(ref v) =>
|
||||||
|
ExprKind::Return { value: v.to_ref() },
|
||||||
|
hir::ExprBreak(label) =>
|
||||||
|
ExprKind::Break { label: label.map(|_| loop_label(cx, self)) },
|
||||||
|
hir::ExprAgain(label) =>
|
||||||
|
ExprKind::Continue { label: label.map(|_| loop_label(cx, self)) },
|
||||||
|
hir::ExprMatch(ref discr, ref arms, _) =>
|
||||||
|
ExprKind::Match { discriminant: discr.to_ref(),
|
||||||
|
arms: arms.iter().map(|a| convert_arm(cx, a)).collect() },
|
||||||
|
hir::ExprIf(ref cond, ref then, ref otherwise) =>
|
||||||
|
ExprKind::If { condition: cond.to_ref(),
|
||||||
|
then: block::to_expr_ref(cx, then),
|
||||||
|
otherwise: otherwise.to_ref() },
|
||||||
|
hir::ExprWhile(ref cond, ref body, _) =>
|
||||||
|
ExprKind::Loop { condition: Some(cond.to_ref()),
|
||||||
|
body: block::to_expr_ref(cx, body) },
|
||||||
|
hir::ExprLoop(ref body, _) =>
|
||||||
|
ExprKind::Loop { condition: None,
|
||||||
|
body: block::to_expr_ref(cx, body) },
|
||||||
|
hir::ExprField(ref source, ident) =>
|
||||||
|
ExprKind::Field { lhs: source.to_ref(),
|
||||||
|
name: Field::Named(ident.node.name) },
|
||||||
|
hir::ExprTupField(ref source, ident) =>
|
||||||
|
ExprKind::Field { lhs: source.to_ref(),
|
||||||
|
name: Field::Indexed(ident.node) },
|
||||||
|
hir::ExprCast(ref source, _) =>
|
||||||
|
ExprKind::Cast { source: source.to_ref() },
|
||||||
|
hir::ExprBox(ref place, ref value) =>
|
||||||
|
ExprKind::Box { place: place.to_ref(), value: value.to_ref() },
|
||||||
|
hir::ExprVec(ref fields) =>
|
||||||
|
ExprKind::Vec { fields: fields.to_ref() },
|
||||||
|
hir::ExprTup(ref fields) =>
|
||||||
|
ExprKind::Tuple { fields: fields.to_ref() },
|
||||||
|
hir::ExprCall(ref fun, ref args) =>
|
||||||
|
ExprKind::Call { fun: fun.to_ref(), args: args.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,
|
||||||
|
span: self.span,
|
||||||
|
kind: kind,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Now apply adjustments, if any.
|
||||||
|
match cx.tcx.tables.borrow().adjustments.get(&self.id) {
|
||||||
|
None => { }
|
||||||
|
Some(&ty::AdjustReifyFnPointer) => {
|
||||||
|
let adjusted_ty = cx.tcx.expr_ty_adjusted(self);
|
||||||
|
expr = Expr {
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
ty: adjusted_ty,
|
||||||
|
span: self.span,
|
||||||
|
kind: ExprKind::ReifyFnPointer { source: expr.to_ref() },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Some(&ty::AdjustUnsafeFnPointer) => {
|
||||||
|
let adjusted_ty = cx.tcx.expr_ty_adjusted(self);
|
||||||
|
expr = Expr {
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
ty: adjusted_ty,
|
||||||
|
span: self.span,
|
||||||
|
kind: ExprKind::UnsafeFnPointer { source: expr.to_ref() },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Some(&ty::AdjustDerefRef(ref adj)) => {
|
||||||
|
for i in 0..adj.autoderefs {
|
||||||
|
let i = i as u32;
|
||||||
|
let adjusted_ty =
|
||||||
|
expr.ty.adjust_for_autoderef(
|
||||||
|
cx.tcx,
|
||||||
|
self.id,
|
||||||
|
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![])
|
||||||
|
} else {
|
||||||
|
ExprKind::Deref { arg: expr.to_ref() }
|
||||||
|
};
|
||||||
|
expr = Expr {
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
ty: adjusted_ty,
|
||||||
|
span: self.span,
|
||||||
|
kind: kind
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(target) = adj.unsize {
|
||||||
|
expr = Expr {
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
ty: target,
|
||||||
|
span: self.span,
|
||||||
|
kind: ExprKind::Unsize { source: expr.to_ref() }
|
||||||
|
};
|
||||||
|
} else if let Some(autoref) = adj.autoref {
|
||||||
|
let adjusted_ty = expr.ty.adjust_for_autoref(cx.tcx, Some(autoref));
|
||||||
|
match autoref {
|
||||||
|
ty::AutoPtr(r, m) => {
|
||||||
|
expr = Expr {
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
ty: adjusted_ty,
|
||||||
|
span: self.span,
|
||||||
|
kind: ExprKind::Borrow { region: *r,
|
||||||
|
borrow_kind: to_borrow_kind(m),
|
||||||
|
arg: expr.to_ref() }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
ty::AutoUnsafe(m) => {
|
||||||
|
// Convert this to a suitable `&foo` and
|
||||||
|
// then an unsafe coercion. Limit the region to be just this
|
||||||
|
// expression.
|
||||||
|
let region = ty::ReScope(expr_extent);
|
||||||
|
let region = cx.tcx.mk_region(region);
|
||||||
|
expr = Expr {
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
ty: cx.tcx.mk_ref(region, ty::TypeAndMut { ty: expr.ty, mutbl: m }),
|
||||||
|
span: self.span,
|
||||||
|
kind: ExprKind::Borrow { region: *region,
|
||||||
|
borrow_kind: to_borrow_kind(m),
|
||||||
|
arg: expr.to_ref() }
|
||||||
|
};
|
||||||
|
expr = Expr {
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
ty: adjusted_ty,
|
||||||
|
span: self.span,
|
||||||
|
kind: ExprKind::Cast { source: expr.to_ref() }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, wrap this up in the expr's scope.
|
||||||
|
expr = Expr {
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
ty: expr.ty,
|
||||||
|
span: self.span,
|
||||||
|
kind: ExprKind::Scope { extent: expr_extent,
|
||||||
|
value: expr.to_ref() }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Finally, create a destruction scope, if any.
|
||||||
|
if let Some(extent) = cx.tcx.region_maps.opt_destruction_extent(self.id) {
|
||||||
|
expr = Expr {
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
ty: expr.ty,
|
||||||
|
span: self.span,
|
||||||
|
kind: ExprKind::Scope { extent: extent, value: expr.to_ref() }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// OK, all done!
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn method_callee<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
|
||||||
|
expr: &hir::Expr,
|
||||||
|
method_call: ty::MethodCall)
|
||||||
|
-> Expr<Cx<'a,'tcx>> {
|
||||||
|
let tables = cx.tcx.tables.borrow();
|
||||||
|
let callee = &tables.method_map[&method_call];
|
||||||
|
let temp_lifetime = cx.tcx.region_maps.temporary_scope(expr.id);
|
||||||
|
Expr {
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
ty: callee.ty,
|
||||||
|
span: expr.span,
|
||||||
|
kind: ExprKind::Literal {
|
||||||
|
literal: Literal::Item {
|
||||||
|
def_id: callee.def_id,
|
||||||
|
substs: callee.substs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_borrow_kind(m: hir::Mutability) -> BorrowKind {
|
||||||
|
match m {
|
||||||
|
hir::MutMutable => BorrowKind::Mut,
|
||||||
|
hir::MutImmutable => BorrowKind::Shared,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_literal<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
|
||||||
|
expr_span: Span,
|
||||||
|
expr_ty: Ty<'tcx>,
|
||||||
|
literal: &hir::Lit)
|
||||||
|
-> Literal<Cx<'a,'tcx>>
|
||||||
|
{
|
||||||
|
use repr::IntegralBits::*;
|
||||||
|
match (&literal.node, &expr_ty.sty) {
|
||||||
|
(&hir::LitStr(ref text, _), _) =>
|
||||||
|
Literal::String { value: text.clone() },
|
||||||
|
(&hir::LitByteStr(ref bytes), _) =>
|
||||||
|
Literal::Bytes { value: bytes.clone() },
|
||||||
|
(&hir::LitByte(c), _) =>
|
||||||
|
Literal::Uint { bits: B8, value: c as u64 },
|
||||||
|
(&hir::LitChar(c), _) =>
|
||||||
|
Literal::Char { c: c },
|
||||||
|
(&hir::LitInt(v, _), &ty::TyUint(hir::TyU8)) =>
|
||||||
|
Literal::Uint { bits: B8, value: v },
|
||||||
|
(&hir::LitInt(v, _), &ty::TyUint(hir::TyU16)) =>
|
||||||
|
Literal::Uint { bits: B16, value: v },
|
||||||
|
(&hir::LitInt(v, _), &ty::TyUint(hir::TyU32)) =>
|
||||||
|
Literal::Uint { bits: B32, value: v },
|
||||||
|
(&hir::LitInt(v, _), &ty::TyUint(hir::TyU64)) =>
|
||||||
|
Literal::Uint { bits: B64, value: v },
|
||||||
|
(&hir::LitInt(v, _), &ty::TyUint(hir::TyUs)) =>
|
||||||
|
Literal::Uint { bits: BSize, value: v },
|
||||||
|
(&hir::LitInt(v, hir::SignedIntLit(_, hir::Sign::Minus)), &ty::TyInt(hir::TyI8)) =>
|
||||||
|
Literal::Int { bits: B8, value: -(v as i64) },
|
||||||
|
(&hir::LitInt(v, hir::SignedIntLit(_, hir::Sign::Minus)), &ty::TyInt(hir::TyI16)) =>
|
||||||
|
Literal::Int { bits: B16, value: -(v as i64) },
|
||||||
|
(&hir::LitInt(v, hir::SignedIntLit(_, hir::Sign::Minus)), &ty::TyInt(hir::TyI32)) =>
|
||||||
|
Literal::Int { bits: B32, value: -(v as i64) },
|
||||||
|
(&hir::LitInt(v, hir::SignedIntLit(_, hir::Sign::Minus)), &ty::TyInt(hir::TyI64)) =>
|
||||||
|
Literal::Int { bits: B64, value: -(v as i64) },
|
||||||
|
(&hir::LitInt(v, hir::SignedIntLit(_, hir::Sign::Minus)), &ty::TyInt(hir::TyIs)) =>
|
||||||
|
Literal::Int { bits: BSize, value: -(v as i64) },
|
||||||
|
(&hir::LitInt(v, _), &ty::TyInt(hir::TyI8)) =>
|
||||||
|
Literal::Int { bits: B8, value: v as i64 },
|
||||||
|
(&hir::LitInt(v, _), &ty::TyInt(hir::TyI16)) =>
|
||||||
|
Literal::Int { bits: B16, value: v as i64 },
|
||||||
|
(&hir::LitInt(v, _), &ty::TyInt(hir::TyI32)) =>
|
||||||
|
Literal::Int { bits: B32, value: v as i64 },
|
||||||
|
(&hir::LitInt(v, _), &ty::TyInt(hir::TyI64)) =>
|
||||||
|
Literal::Int { bits: B64, value: v as i64 },
|
||||||
|
(&hir::LitInt(v, _), &ty::TyInt(hir::TyIs)) =>
|
||||||
|
Literal::Int { bits: BSize, value: v as i64 },
|
||||||
|
(&hir::LitFloat(ref v, _), &ty::TyFloat(hir::TyF32)) |
|
||||||
|
(&hir::LitFloatUnsuffixed(ref v), &ty::TyFloat(hir::TyF32)) =>
|
||||||
|
Literal::Float { bits: FloatBits::F32, value: v.parse::<f64>().unwrap() },
|
||||||
|
(&hir::LitFloat(ref v, _), &ty::TyFloat(hir::TyF64)) |
|
||||||
|
(&hir::LitFloatUnsuffixed(ref v), &ty::TyFloat(hir::TyF64)) =>
|
||||||
|
Literal::Float { bits: FloatBits::F64, value: v.parse::<f64>().unwrap() },
|
||||||
|
(&hir::LitBool(v), _) =>
|
||||||
|
Literal::Bool { value: v },
|
||||||
|
(ref l, ref t) =>
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
expr_span,
|
||||||
|
&format!("Invalid literal/type combination: {:?},{:?}", l, t))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_arm<'a,'tcx:'a>(cx: &Cx<'a,'tcx>, arm: &'tcx hir::Arm) -> Arm<Cx<'a,'tcx>> {
|
||||||
|
let map = if arm.pats.len() == 1 {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let mut map = FnvHashMap();
|
||||||
|
pat_util::pat_bindings(&cx.tcx.def_map, &arm.pats[0], |_, p_id, _, path| {
|
||||||
|
map.insert(path.node, p_id);
|
||||||
|
});
|
||||||
|
Some(Rc::new(map))
|
||||||
|
};
|
||||||
|
|
||||||
|
Arm { patterns: arm.pats.iter().map(|p| PatNode::new(p, map.clone()).to_ref()).collect(),
|
||||||
|
guard: arm.guard.to_ref(),
|
||||||
|
body: arm.body.to_ref() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_path_expr<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
|
||||||
|
expr: &'tcx hir::Expr)
|
||||||
|
-> ExprKind<Cx<'a,'tcx>>
|
||||||
|
{
|
||||||
|
let substs = cx.tcx.mk_substs(cx.tcx.node_id_item_substs(expr.id).substs);
|
||||||
|
match cx.tcx.def_map.borrow()[&expr.id].full_def() {
|
||||||
|
def::DefVariant(_, def_id, false) |
|
||||||
|
def::DefStruct(def_id) |
|
||||||
|
def::DefFn(def_id, _) |
|
||||||
|
def::DefConst(def_id) |
|
||||||
|
def::DefMethod(def_id) |
|
||||||
|
def::DefAssociatedConst(def_id) =>
|
||||||
|
ExprKind::Literal {
|
||||||
|
literal: Literal::Item { def_id: def_id, substs: substs }
|
||||||
|
},
|
||||||
|
|
||||||
|
def::DefStatic(node_id, _) =>
|
||||||
|
ExprKind::StaticRef {
|
||||||
|
id: node_id,
|
||||||
|
},
|
||||||
|
|
||||||
|
def @ def::DefLocal(..) |
|
||||||
|
def @ def::DefUpvar(..) =>
|
||||||
|
convert_var(cx, expr, def),
|
||||||
|
|
||||||
|
def =>
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
expr.span,
|
||||||
|
&format!("def `{:?}` not yet implemented", def)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_var<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
|
||||||
|
expr: &'tcx hir::Expr,
|
||||||
|
def: def::Def)
|
||||||
|
-> ExprKind<Cx<'a,'tcx>>
|
||||||
|
{
|
||||||
|
let temp_lifetime = cx.tcx.region_maps.temporary_scope(expr.id);
|
||||||
|
|
||||||
|
match def {
|
||||||
|
def::DefLocal(node_id) => {
|
||||||
|
ExprKind::VarRef {
|
||||||
|
id: node_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def::DefUpvar(id_var, index, closure_expr_id) => {
|
||||||
|
debug!("convert_var(upvar({:?}, {:?}, {:?}))", id_var, index, closure_expr_id);
|
||||||
|
let var_ty = cx.tcx.node_id_to_type(id_var);
|
||||||
|
|
||||||
|
let body_id = match cx.tcx.map.find(closure_expr_id) {
|
||||||
|
Some(map::NodeExpr(expr)) => {
|
||||||
|
match expr.node {
|
||||||
|
hir::ExprClosure(_, _, ref body) => body.id,
|
||||||
|
_ => {
|
||||||
|
cx.tcx.sess.span_bug(expr.span,
|
||||||
|
&format!("closure expr is not a closure expr"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
cx.tcx.sess.span_bug(expr.span,
|
||||||
|
&format!("ast-map has garbage for closure expr"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// FIXME free regions in closures are not right
|
||||||
|
let closure_ty =
|
||||||
|
cx.tcx.node_id_to_type(closure_expr_id);
|
||||||
|
|
||||||
|
// FIXME we're just hard-coding the idea that the
|
||||||
|
// signature will be &self or &mut self and hence will
|
||||||
|
// have a bound region with number 0
|
||||||
|
let region =
|
||||||
|
ty::Region::ReFree(
|
||||||
|
ty::FreeRegion {
|
||||||
|
scope: cx.tcx.region_maps.node_extent(body_id),
|
||||||
|
bound_region: ty::BoundRegion::BrAnon(0)
|
||||||
|
});
|
||||||
|
let region =
|
||||||
|
cx.tcx.mk_region(region);
|
||||||
|
|
||||||
|
let self_expr = match cx.tcx.closure_kind(DefId::local(closure_expr_id)) {
|
||||||
|
ty::ClosureKind::FnClosureKind => {
|
||||||
|
let ref_closure_ty =
|
||||||
|
cx.tcx.mk_ref(region,
|
||||||
|
ty::TypeAndMut { ty: closure_ty,
|
||||||
|
mutbl: hir::MutImmutable });
|
||||||
|
Expr {
|
||||||
|
ty: closure_ty,
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
span: expr.span,
|
||||||
|
kind: ExprKind::Deref {
|
||||||
|
arg: Expr {
|
||||||
|
ty: ref_closure_ty,
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
span: expr.span,
|
||||||
|
kind: ExprKind::SelfRef
|
||||||
|
}.to_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::ClosureKind::FnMutClosureKind => {
|
||||||
|
let ref_closure_ty =
|
||||||
|
cx.tcx.mk_ref(region,
|
||||||
|
ty::TypeAndMut { ty: closure_ty,
|
||||||
|
mutbl: hir::MutMutable });
|
||||||
|
Expr {
|
||||||
|
ty: closure_ty,
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
span: expr.span,
|
||||||
|
kind: ExprKind::Deref {
|
||||||
|
arg: Expr {
|
||||||
|
ty: ref_closure_ty,
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
span: expr.span,
|
||||||
|
kind: ExprKind::SelfRef
|
||||||
|
}.to_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::ClosureKind::FnOnceClosureKind => {
|
||||||
|
Expr {
|
||||||
|
ty: closure_ty,
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
span: expr.span,
|
||||||
|
kind: ExprKind::SelfRef
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// at this point we have `self.n`, which loads up the upvar
|
||||||
|
let field_kind =
|
||||||
|
ExprKind::Field { lhs: self_expr.to_ref(),
|
||||||
|
name: Field::Indexed(index) };
|
||||||
|
|
||||||
|
// ...but the upvar might be an `&T` or `&mut T` capture, at which
|
||||||
|
// point we need an implicit deref
|
||||||
|
let upvar_id = ty::UpvarId { var_id: id_var, closure_expr_id: closure_expr_id };
|
||||||
|
let upvar_capture = match cx.tcx.upvar_capture(upvar_id) {
|
||||||
|
Some(c) => c,
|
||||||
|
None => {
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
expr.span,
|
||||||
|
&format!("no upvar_capture for {:?}", upvar_id));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
match upvar_capture {
|
||||||
|
ty::UpvarCapture::ByValue => field_kind,
|
||||||
|
ty::UpvarCapture::ByRef(_) => {
|
||||||
|
ExprKind::Deref {
|
||||||
|
arg: Expr {
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
ty: var_ty,
|
||||||
|
span: expr.span,
|
||||||
|
kind: field_kind,
|
||||||
|
}.to_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => cx.tcx.sess.span_bug(expr.span, "type of & not region")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn bin_op(op: hir::BinOp_) -> BinOp {
|
||||||
|
match op {
|
||||||
|
hir::BinOp_::BiAdd => BinOp::Add,
|
||||||
|
hir::BinOp_::BiSub => BinOp::Sub,
|
||||||
|
hir::BinOp_::BiMul => BinOp::Mul,
|
||||||
|
hir::BinOp_::BiDiv => BinOp::Div,
|
||||||
|
hir::BinOp_::BiRem => BinOp::Rem,
|
||||||
|
hir::BinOp_::BiBitXor => BinOp::BitXor,
|
||||||
|
hir::BinOp_::BiBitAnd => BinOp::BitAnd,
|
||||||
|
hir::BinOp_::BiBitOr => BinOp::BitOr,
|
||||||
|
hir::BinOp_::BiShl => BinOp::Shl,
|
||||||
|
hir::BinOp_::BiShr => BinOp::Shr,
|
||||||
|
hir::BinOp_::BiEq => BinOp::Eq,
|
||||||
|
hir::BinOp_::BiLt => BinOp::Lt,
|
||||||
|
hir::BinOp_::BiLe => BinOp::Le,
|
||||||
|
hir::BinOp_::BiNe => BinOp::Ne,
|
||||||
|
hir::BinOp_::BiGe => BinOp::Ge,
|
||||||
|
hir::BinOp_::BiGt => BinOp::Gt,
|
||||||
|
_ => panic!("no equivalent for ast binop {:?}", op)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum PassArgs {
|
||||||
|
ByValue,
|
||||||
|
ByRef
|
||||||
|
}
|
||||||
|
|
||||||
|
fn overloaded_operator<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
|
||||||
|
expr: &'tcx hir::Expr,
|
||||||
|
method_call: ty::MethodCall,
|
||||||
|
pass_args: PassArgs,
|
||||||
|
receiver: ExprRef<Cx<'a,'tcx>>,
|
||||||
|
args: Vec<&'tcx P<hir::Expr>>)
|
||||||
|
-> ExprKind<Cx<'a,'tcx>>
|
||||||
|
{
|
||||||
|
// the receiver has all the adjustments that are needed, so we can
|
||||||
|
// just push a reference to it
|
||||||
|
let mut argrefs = vec![receiver];
|
||||||
|
|
||||||
|
// the arguments, unfortunately, do not, so if this is a ByRef
|
||||||
|
// operator, we have to gin up the autorefs (but by value is easy)
|
||||||
|
match pass_args {
|
||||||
|
PassArgs::ByValue => {
|
||||||
|
argrefs.extend(
|
||||||
|
args.iter()
|
||||||
|
.map(|arg| arg.to_ref()))
|
||||||
|
}
|
||||||
|
|
||||||
|
PassArgs::ByRef => {
|
||||||
|
let scope = cx.tcx.region_maps.node_extent(expr.id);
|
||||||
|
let region = cx.tcx.mk_region(ty::ReScope(scope));
|
||||||
|
let temp_lifetime = cx.tcx.region_maps.temporary_scope(expr.id);
|
||||||
|
argrefs.extend(
|
||||||
|
args.iter()
|
||||||
|
.map(|arg| {
|
||||||
|
let arg_ty = cx.tcx.expr_ty_adjusted(arg);
|
||||||
|
let adjusted_ty =
|
||||||
|
cx.tcx.mk_ref(region,
|
||||||
|
ty::TypeAndMut { ty: arg_ty,
|
||||||
|
mutbl: hir::MutImmutable });
|
||||||
|
Expr {
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
ty: adjusted_ty,
|
||||||
|
span: expr.span,
|
||||||
|
kind: ExprKind::Borrow { region: *region,
|
||||||
|
borrow_kind: BorrowKind::Shared,
|
||||||
|
arg: arg.to_ref() }
|
||||||
|
}.to_ref()
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now create the call itself
|
||||||
|
let fun = method_callee(cx, expr, method_call);
|
||||||
|
ExprKind::Call {
|
||||||
|
fun: fun.to_ref(),
|
||||||
|
args: argrefs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn overloaded_lvalue<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
|
||||||
|
expr: &'tcx hir::Expr,
|
||||||
|
method_call: ty::MethodCall,
|
||||||
|
pass_args: PassArgs,
|
||||||
|
receiver: ExprRef<Cx<'a,'tcx>>,
|
||||||
|
args: Vec<&'tcx P<hir::Expr>>)
|
||||||
|
-> ExprKind<Cx<'a,'tcx>>
|
||||||
|
{
|
||||||
|
// For an overloaded *x or x[y] expression of type T, the method
|
||||||
|
// call returns an &T and we must add the deref so that the types
|
||||||
|
// line up (this is because `*x` and `x[y]` represent lvalues):
|
||||||
|
|
||||||
|
// to find the type &T of the content returned by the method;
|
||||||
|
let tables = cx.tcx.tables.borrow();
|
||||||
|
let callee = &tables.method_map[&method_call];
|
||||||
|
let ref_ty = callee.ty.fn_ret();
|
||||||
|
let ref_ty = cx.tcx.no_late_bound_regions(&ref_ty).unwrap().unwrap();
|
||||||
|
// 1~~~~~ 2~~~~~
|
||||||
|
// (1) callees always have all late-bound regions fully instantiated,
|
||||||
|
// (2) overloaded methods don't return `!`
|
||||||
|
|
||||||
|
// construct the complete expression `foo()` for the overloaded call,
|
||||||
|
// which will yield the &T type
|
||||||
|
let temp_lifetime = cx.tcx.region_maps.temporary_scope(expr.id);
|
||||||
|
let ref_kind = overloaded_operator(cx, expr, method_call, pass_args, receiver, args);
|
||||||
|
let ref_expr = Expr {
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
ty: ref_ty,
|
||||||
|
span: expr.span,
|
||||||
|
kind: ref_kind,
|
||||||
|
};
|
||||||
|
|
||||||
|
// construct and return a deref wrapper `*foo()`
|
||||||
|
ExprKind::Deref { arg: ref_expr.to_ref() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn capture_freevar<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
|
||||||
|
closure_expr: &'tcx hir::Expr,
|
||||||
|
freevar: &ty::Freevar,
|
||||||
|
freevar_ty: Ty<'tcx>)
|
||||||
|
-> ExprRef<Cx<'a,'tcx>> {
|
||||||
|
let id_var = freevar.def.def_id().node;
|
||||||
|
let upvar_id = ty::UpvarId { var_id: id_var, closure_expr_id: closure_expr.id };
|
||||||
|
let upvar_capture = cx.tcx.upvar_capture(upvar_id).unwrap();
|
||||||
|
let temp_lifetime = cx.tcx.region_maps.temporary_scope(closure_expr.id);
|
||||||
|
let var_ty = cx.tcx.node_id_to_type(id_var);
|
||||||
|
let captured_var = Expr { temp_lifetime: temp_lifetime,
|
||||||
|
ty: var_ty,
|
||||||
|
span: closure_expr.span,
|
||||||
|
kind: convert_var(cx, closure_expr, freevar.def) };
|
||||||
|
match upvar_capture {
|
||||||
|
ty::UpvarCapture::ByValue => {
|
||||||
|
captured_var.to_ref()
|
||||||
|
}
|
||||||
|
ty::UpvarCapture::ByRef(upvar_borrow) => {
|
||||||
|
let borrow_kind = match upvar_borrow.kind {
|
||||||
|
ty::BorrowKind::ImmBorrow => BorrowKind::Shared,
|
||||||
|
ty::BorrowKind::UniqueImmBorrow => BorrowKind::Unique,
|
||||||
|
ty::BorrowKind::MutBorrow => BorrowKind::Mut,
|
||||||
|
};
|
||||||
|
Expr {
|
||||||
|
temp_lifetime: temp_lifetime,
|
||||||
|
ty: freevar_ty,
|
||||||
|
span: closure_expr.span,
|
||||||
|
kind: ExprKind::Borrow { region: upvar_borrow.region,
|
||||||
|
borrow_kind: borrow_kind,
|
||||||
|
arg: captured_var.to_ref() }
|
||||||
|
}.to_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn loop_label<'a,'tcx:'a>(cx: &mut Cx<'a,'tcx>,
|
||||||
|
expr: &'tcx hir::Expr)
|
||||||
|
-> CodeExtent
|
||||||
|
{
|
||||||
|
match cx.tcx.def_map.borrow().get(&expr.id).map(|d| d.full_def()) {
|
||||||
|
Some(def::DefLabel(loop_id)) => cx.tcx.region_maps.node_extent(loop_id),
|
||||||
|
d => {
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
expr.span,
|
||||||
|
&format!("loop scope resolved to {:?}", d));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,184 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
use std::fmt::{Debug, Formatter, Error};
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use self::rustc::middle::def_id::DefId;
|
||||||
|
use self::rustc::middle::infer::InferCtxt;
|
||||||
|
use self::rustc::middle::region::CodeExtent;
|
||||||
|
use self::rustc::middle::subst::{self, Subst, Substs};
|
||||||
|
use self::rustc::middle::ty::{self, Ty};
|
||||||
|
use self::rustc_front::hir;
|
||||||
|
use self::syntax::ast;
|
||||||
|
use self::syntax::codemap::Span;
|
||||||
|
use self::syntax::parse::token::{self, special_idents, InternedString};
|
||||||
|
|
||||||
|
extern crate rustc;
|
||||||
|
extern crate rustc_front;
|
||||||
|
extern crate syntax;
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct Cx<'a,'tcx:'a> {
|
||||||
|
pub tcx: &'a ty::ctxt<'tcx>,
|
||||||
|
pub infcx: &'a InferCtxt<'a,'tcx>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'tcx> Cx<'a,'tcx> {
|
||||||
|
pub fn new(infcx: &'a InferCtxt<'a,'tcx>) -> Cx<'a,'tcx> {
|
||||||
|
Cx { tcx: infcx.tcx, infcx: infcx }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use self::pattern::PatNode;
|
||||||
|
|
||||||
|
impl<'a,'tcx:'a> Hair for Cx<'a, 'tcx> {
|
||||||
|
type VarId = ast::NodeId;
|
||||||
|
type DefId = DefId;
|
||||||
|
type AdtDef = ty::AdtDef<'tcx>;
|
||||||
|
type Name = ast::Name;
|
||||||
|
type Ident = ast::Ident;
|
||||||
|
type InternedString = InternedString;
|
||||||
|
type Bytes = Rc<Vec<u8>>;
|
||||||
|
type Span = Span;
|
||||||
|
type Projection = ty::ProjectionTy<'tcx>;
|
||||||
|
type Substs = &'tcx subst::Substs<'tcx>;
|
||||||
|
type ClosureSubsts = &'tcx ty::ClosureSubsts<'tcx>;
|
||||||
|
type Ty = Ty<'tcx>;
|
||||||
|
type Region = ty::Region;
|
||||||
|
type CodeExtent = CodeExtent;
|
||||||
|
type Pattern = PatNode<'tcx>;
|
||||||
|
type Expr = &'tcx hir::Expr;
|
||||||
|
type Stmt = &'tcx hir::Stmt;
|
||||||
|
type Block = &'tcx hir::Block;
|
||||||
|
type InlineAsm = &'tcx hir::InlineAsm;
|
||||||
|
|
||||||
|
fn unit_ty(&mut self) -> Ty<'tcx> {
|
||||||
|
self.tcx.mk_nil()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usize_ty(&mut self) -> Ty<'tcx> {
|
||||||
|
self.tcx.types.usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bool_ty(&mut self) -> Ty<'tcx> {
|
||||||
|
self.tcx.types.bool
|
||||||
|
}
|
||||||
|
|
||||||
|
fn partial_eq(&mut self, ty: Ty<'tcx>) -> ItemRef<Self> {
|
||||||
|
let eq_def_id = self.tcx.lang_items.eq_trait().unwrap();
|
||||||
|
self.cmp_method_ref(eq_def_id, "eq", ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn partial_le(&mut self, ty: Ty<'tcx>) -> ItemRef<Self> {
|
||||||
|
let ord_def_id = self.tcx.lang_items.ord_trait().unwrap();
|
||||||
|
self.cmp_method_ref(ord_def_id, "le", ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_variants(&mut self, adt_def: ty::AdtDef<'tcx>) -> usize {
|
||||||
|
adt_def.variants.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fields(&mut self, adt_def: ty::AdtDef<'tcx>, variant_index: usize) -> Vec<Field<Self>> {
|
||||||
|
adt_def.variants[variant_index]
|
||||||
|
.fields
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, field)| {
|
||||||
|
if field.name == special_idents::unnamed_field.name {
|
||||||
|
Field::Indexed(index)
|
||||||
|
} else {
|
||||||
|
Field::Named(field.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn needs_drop(&mut self, ty: Ty<'tcx>, span: Self::Span) -> bool {
|
||||||
|
if self.infcx.type_moves_by_default(ty, span) {
|
||||||
|
// TODO we should do an add'l check here to determine if
|
||||||
|
// any dtor will execute, but the relevant fn
|
||||||
|
// (`type_needs_drop`) is currently factored into
|
||||||
|
// `librustc_trans`, so we can't easily do so.
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
// if type implements Copy, cannot require drop
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn span_bug(&mut self, span: Self::Span, message: &str) -> ! {
|
||||||
|
self.tcx.sess.span_bug(span, message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'tcx:'a> Cx<'a,'tcx> {
|
||||||
|
fn cmp_method_ref(&mut self,
|
||||||
|
trait_def_id: DefId,
|
||||||
|
method_name: &str,
|
||||||
|
arg_ty: Ty<'tcx>)
|
||||||
|
-> ItemRef<Cx<'a,'tcx>> {
|
||||||
|
let method_name = token::intern(method_name);
|
||||||
|
let substs = Substs::new_trait(vec![arg_ty], vec![], arg_ty);
|
||||||
|
for trait_item in self.tcx.trait_items(trait_def_id).iter() {
|
||||||
|
match *trait_item {
|
||||||
|
ty::ImplOrTraitItem::MethodTraitItem(ref method) => {
|
||||||
|
if method.name == method_name {
|
||||||
|
let method_ty = self.tcx.lookup_item_type(method.def_id);
|
||||||
|
let method_ty = method_ty.ty.subst(self.tcx, &substs);
|
||||||
|
return ItemRef {
|
||||||
|
ty: method_ty,
|
||||||
|
def_id: method.def_id,
|
||||||
|
substs: self.tcx.mk_substs(substs),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ty::ImplOrTraitItem::ConstTraitItem(..) |
|
||||||
|
ty::ImplOrTraitItem::TypeTraitItem(..) => {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tcx.sess.bug(
|
||||||
|
&format!("found no method `{}` in `{:?}`", method_name, trait_def_id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only need this impl so that we do deriving for things that are
|
||||||
|
// defined relative to the `Hair` trait. See `Hair` trait for more
|
||||||
|
// details.
|
||||||
|
impl<'a,'tcx> PartialEq for Cx<'a,'tcx> {
|
||||||
|
fn eq(&self, _: &Cx<'a,'tcx>) -> bool {
|
||||||
|
panic!("Cx should never ACTUALLY be compared for equality")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'tcx> Eq for Cx<'a,'tcx> { }
|
||||||
|
|
||||||
|
impl<'a,'tcx> Hash for Cx<'a,'tcx> {
|
||||||
|
fn hash<H: Hasher>(&self, _: &mut H) {
|
||||||
|
panic!("Cx should never ACTUALLY be hashed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'tcx> Debug for Cx<'a,'tcx> {
|
||||||
|
fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> {
|
||||||
|
write!(fmt, "Tcx")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod block;
|
||||||
|
mod expr;
|
||||||
|
mod pattern;
|
||||||
|
mod to_ref;
|
||||||
|
|
|
@ -0,0 +1,291 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
use rustc_data_structures::fnv::FnvHashMap;
|
||||||
|
use std::rc::Rc;
|
||||||
|
use tcx::Cx;
|
||||||
|
use tcx::rustc::middle::const_eval::lookup_const_by_id;
|
||||||
|
use tcx::rustc::middle::def;
|
||||||
|
use tcx::rustc::middle::pat_util::{pat_is_resolved_const, pat_is_binding};
|
||||||
|
use tcx::rustc::middle::ty::{self, Ty};
|
||||||
|
use tcx::rustc_front::hir;
|
||||||
|
use tcx::syntax::ast;
|
||||||
|
use tcx::syntax::ptr::P;
|
||||||
|
use tcx::to_ref::ToRef;
|
||||||
|
|
||||||
|
/// When there are multiple patterns in a single arm, each one has its
|
||||||
|
/// own node-ids for the bindings. References to the variables always
|
||||||
|
/// use the node-ids from the first pattern in the arm, so we just
|
||||||
|
/// remap the ids for all subsequent bindings to the first one.
|
||||||
|
///
|
||||||
|
/// Example:
|
||||||
|
/// ```
|
||||||
|
/// match foo {
|
||||||
|
/// Test1(flavor /* def 1 */) |
|
||||||
|
/// Test2(flavor /* def 2 */) if flavor /* ref 1 */.is_tasty() => { ... }
|
||||||
|
/// _ => { ... }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct PatNode<'tcx> {
|
||||||
|
pat: &'tcx hir::Pat,
|
||||||
|
binding_map: Option<Rc<FnvHashMap<ast::Ident, ast::NodeId>>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> PatNode<'tcx> {
|
||||||
|
pub fn new(pat: &'tcx hir::Pat,
|
||||||
|
binding_map: Option<Rc<FnvHashMap<ast::Ident, ast::NodeId>>>)
|
||||||
|
-> PatNode<'tcx> {
|
||||||
|
PatNode {
|
||||||
|
pat: pat,
|
||||||
|
binding_map: binding_map,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn irrefutable(pat: &'tcx hir::Pat)
|
||||||
|
-> PatNode<'tcx> {
|
||||||
|
PatNode::new(pat, None)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pat_ref<'a>(&self, pat: &'tcx hir::Pat) -> PatternRef<Cx<'a,'tcx>> {
|
||||||
|
PatNode::new(pat, self.binding_map.clone()).to_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pat_refs<'a>(&self, pats: &'tcx Vec<P<hir::Pat>>) -> Vec<PatternRef<Cx<'a,'tcx>>> {
|
||||||
|
pats.iter().map(|p| self.pat_ref(p)).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn opt_pat_ref<'a>(&self, pat: &'tcx Option<P<hir::Pat>>) -> Option<PatternRef<Cx<'a,'tcx>>> {
|
||||||
|
pat.as_ref().map(|p| self.pat_ref(p))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slice_or_array_pattern<'a>(&self,
|
||||||
|
cx: &mut Cx<'a, 'tcx>,
|
||||||
|
ty: Ty<'tcx>,
|
||||||
|
prefix: &'tcx Vec<P<hir::Pat>>,
|
||||||
|
slice: &'tcx Option<P<hir::Pat>>,
|
||||||
|
suffix: &'tcx Vec<P<hir::Pat>>)
|
||||||
|
-> PatternKind<Cx<'a,'tcx>>
|
||||||
|
{
|
||||||
|
match ty.sty {
|
||||||
|
ty::TySlice(..) =>
|
||||||
|
// matching a slice or fixed-length array
|
||||||
|
PatternKind::Slice {
|
||||||
|
prefix: self.pat_refs(prefix),
|
||||||
|
slice: self.opt_pat_ref(slice),
|
||||||
|
suffix: self.pat_refs(suffix),
|
||||||
|
},
|
||||||
|
|
||||||
|
ty::TyArray(_, len) => {
|
||||||
|
// fixed-length array
|
||||||
|
assert!(len >= prefix.len() + suffix.len());
|
||||||
|
PatternKind::Array {
|
||||||
|
prefix: self.pat_refs(prefix),
|
||||||
|
slice: self.opt_pat_ref(slice),
|
||||||
|
suffix: self.pat_refs(suffix),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
self.pat.span,
|
||||||
|
"unexpanded macro or bad constant etc");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn variant_or_leaf<'a>(&self,
|
||||||
|
cx: &mut Cx<'a, 'tcx>,
|
||||||
|
subpatterns: Vec<FieldPatternRef<Cx<'a,'tcx>>>)
|
||||||
|
-> PatternKind<Cx<'a,'tcx>>
|
||||||
|
{
|
||||||
|
let def = cx.tcx.def_map.borrow().get(&self.pat.id).unwrap().full_def();
|
||||||
|
match def {
|
||||||
|
def::DefVariant(enum_id, variant_id, _) => {
|
||||||
|
let adt_def = cx.tcx.lookup_adt_def(enum_id);
|
||||||
|
if adt_def.variants.len() > 1 {
|
||||||
|
PatternKind::Variant { adt_def: adt_def,
|
||||||
|
variant_index: adt_def.variant_index_with_id(variant_id),
|
||||||
|
subpatterns: subpatterns }
|
||||||
|
} else {
|
||||||
|
PatternKind::Leaf { subpatterns: subpatterns }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: resolving to DefStruct means the struct *constructor*,
|
||||||
|
// not the struct as a type.
|
||||||
|
def::DefStruct(..) | def::DefTy(..) => {
|
||||||
|
PatternKind::Leaf { subpatterns: subpatterns }
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
self.pat.span,
|
||||||
|
&format!("inappropriate def for pattern: {:?}", def));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'tcx:'a> Mirror<Cx<'a,'tcx>> for PatNode<'tcx> {
|
||||||
|
type Output = Pattern<Cx<'a,'tcx>>;
|
||||||
|
|
||||||
|
fn make_mirror(self, cx: &mut Cx<'a,'tcx>) -> Pattern<Cx<'a,'tcx>> {
|
||||||
|
let kind = match self.pat.node {
|
||||||
|
hir::PatWild(..) =>
|
||||||
|
PatternKind::Wild,
|
||||||
|
|
||||||
|
hir::PatLit(ref lt) =>
|
||||||
|
PatternKind::Constant { expr: lt.to_ref() },
|
||||||
|
|
||||||
|
hir::PatRange(ref begin, ref end) =>
|
||||||
|
PatternKind::Range { lo: begin.to_ref(),
|
||||||
|
hi: end.to_ref() },
|
||||||
|
|
||||||
|
hir::PatEnum(..) | hir::PatIdent(..) | hir::PatQPath(..)
|
||||||
|
if pat_is_resolved_const(&cx.tcx.def_map, self.pat) =>
|
||||||
|
{
|
||||||
|
let def = cx.tcx.def_map.borrow().get(&self.pat.id).unwrap().full_def();
|
||||||
|
match def {
|
||||||
|
def::DefConst(def_id) | def::DefAssociatedConst(def_id) =>
|
||||||
|
match lookup_const_by_id(cx.tcx, def_id, Some(self.pat.id)) {
|
||||||
|
Some(const_expr) =>
|
||||||
|
PatternKind::Constant { expr: const_expr.to_ref() },
|
||||||
|
None =>
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
self.pat.span,
|
||||||
|
&format!("cannot eval constant: {:?}", def_id)),
|
||||||
|
},
|
||||||
|
_ =>
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
self.pat.span,
|
||||||
|
&format!("def not a constant: {:?}", def)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::PatRegion(ref subpattern, _) |
|
||||||
|
hir::PatBox(ref subpattern) => {
|
||||||
|
PatternKind::Deref { subpattern: self.pat_ref(subpattern) }
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::PatVec(ref prefix, ref slice, ref suffix) => {
|
||||||
|
let ty = cx.tcx.node_id_to_type(self.pat.id);
|
||||||
|
match ty.sty {
|
||||||
|
ty::TyRef(_, mt) =>
|
||||||
|
PatternKind::Deref {
|
||||||
|
subpattern: Pattern {
|
||||||
|
ty: mt.ty,
|
||||||
|
span: self.pat.span,
|
||||||
|
kind: self.slice_or_array_pattern(cx, mt.ty, prefix,
|
||||||
|
slice, suffix),
|
||||||
|
}.to_ref()
|
||||||
|
},
|
||||||
|
|
||||||
|
ty::TySlice(..) |
|
||||||
|
ty::TyArray(..) =>
|
||||||
|
self.slice_or_array_pattern(cx, ty, prefix, slice, suffix),
|
||||||
|
|
||||||
|
ref sty =>
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
self.pat.span,
|
||||||
|
&format!("unexpanded type for vector pattern: {:?}", sty)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::PatTup(ref subpatterns) => {
|
||||||
|
let subpatterns =
|
||||||
|
subpatterns.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, subpattern)| FieldPatternRef {
|
||||||
|
field: Field::Indexed(i),
|
||||||
|
pattern: self.pat_ref(subpattern),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
PatternKind::Leaf { subpatterns: subpatterns }
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::PatIdent(bm, ref ident, ref sub)
|
||||||
|
if pat_is_binding(&cx.tcx.def_map, self.pat) =>
|
||||||
|
{
|
||||||
|
let id = match self.binding_map {
|
||||||
|
None => self.pat.id,
|
||||||
|
Some(ref map) => map[&ident.node],
|
||||||
|
};
|
||||||
|
let var_ty = cx.tcx.node_id_to_type(self.pat.id);
|
||||||
|
let region = match var_ty.sty {
|
||||||
|
ty::TyRef(&r, _) => Some(r),
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
let (mutability, mode) = match bm {
|
||||||
|
hir::BindByValue(hir::MutMutable) =>
|
||||||
|
(Mutability::Mut, BindingMode::ByValue),
|
||||||
|
hir::BindByValue(hir::MutImmutable) =>
|
||||||
|
(Mutability::Not, BindingMode::ByValue),
|
||||||
|
hir::BindByRef(hir::MutMutable) =>
|
||||||
|
(Mutability::Not, BindingMode::ByRef(region.unwrap(), BorrowKind::Mut)),
|
||||||
|
hir::BindByRef(hir::MutImmutable) =>
|
||||||
|
(Mutability::Not, BindingMode::ByRef(region.unwrap(), BorrowKind::Shared)),
|
||||||
|
};
|
||||||
|
PatternKind::Binding {
|
||||||
|
mutability: mutability,
|
||||||
|
mode: mode,
|
||||||
|
name: ident.node,
|
||||||
|
var: id,
|
||||||
|
ty: var_ty,
|
||||||
|
subpattern: self.opt_pat_ref(sub),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::PatIdent(..) => {
|
||||||
|
self.variant_or_leaf(cx, vec![])
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::PatEnum(_, ref opt_subpatterns) => {
|
||||||
|
let subpatterns =
|
||||||
|
opt_subpatterns.iter()
|
||||||
|
.flat_map(|v| v.iter())
|
||||||
|
.enumerate()
|
||||||
|
.map(|(i, field)| FieldPatternRef {
|
||||||
|
field: Field::Indexed(i),
|
||||||
|
pattern: self.pat_ref(field),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
self.variant_or_leaf(cx, subpatterns)
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::PatStruct(_, ref fields, _) => {
|
||||||
|
let subpatterns =
|
||||||
|
fields.iter()
|
||||||
|
.map(|field| FieldPatternRef {
|
||||||
|
field: Field::Named(field.node.ident.name),
|
||||||
|
pattern: self.pat_ref(&field.node.pat),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
self.variant_or_leaf(cx, subpatterns)
|
||||||
|
}
|
||||||
|
|
||||||
|
hir::PatQPath(..) => {
|
||||||
|
cx.tcx.sess.span_bug(
|
||||||
|
self.pat.span,
|
||||||
|
"unexpanded macro or bad constant etc");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let ty = cx.tcx.node_id_to_type(self.pat.id);
|
||||||
|
|
||||||
|
Pattern { span: self.pat.span,
|
||||||
|
ty: ty,
|
||||||
|
kind: kind }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
// 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.
|
||||||
|
|
||||||
|
use hair::*;
|
||||||
|
use repr::*;
|
||||||
|
|
||||||
|
use tcx::Cx;
|
||||||
|
use tcx::pattern::PatNode;
|
||||||
|
use tcx::rustc_front::hir;
|
||||||
|
use tcx::syntax::ptr::P;
|
||||||
|
|
||||||
|
pub trait ToRef<H> {
|
||||||
|
type Output;
|
||||||
|
fn to_ref(self) -> Self::Output;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'tcx:'a> ToRef<Cx<'a,'tcx>> for &'tcx hir::Expr {
|
||||||
|
type Output = ExprRef<Cx<'a,'tcx>>;
|
||||||
|
|
||||||
|
fn to_ref(self) -> ExprRef<Cx<'a,'tcx>> {
|
||||||
|
ExprRef::Hair(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'tcx:'a> ToRef<Cx<'a,'tcx>> for &'tcx P<hir::Expr> {
|
||||||
|
type Output = ExprRef<Cx<'a,'tcx>>;
|
||||||
|
|
||||||
|
fn to_ref(self) -> ExprRef<Cx<'a,'tcx>> {
|
||||||
|
ExprRef::Hair(&**self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'tcx:'a> ToRef<Cx<'a,'tcx>> for Expr<Cx<'a,'tcx>> {
|
||||||
|
type Output = ExprRef<Cx<'a,'tcx>>;
|
||||||
|
|
||||||
|
fn to_ref(self) -> ExprRef<Cx<'a,'tcx>> {
|
||||||
|
ExprRef::Mirror(Box::new(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'tcx:'a> ToRef<Cx<'a,'tcx>> for PatNode<'tcx> {
|
||||||
|
type Output = PatternRef<Cx<'a,'tcx>>;
|
||||||
|
|
||||||
|
fn to_ref(self) -> PatternRef<Cx<'a,'tcx>> {
|
||||||
|
PatternRef::Hair(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'tcx:'a> ToRef<Cx<'a,'tcx>> for Pattern<Cx<'a,'tcx>> {
|
||||||
|
type Output = PatternRef<Cx<'a,'tcx>>;
|
||||||
|
|
||||||
|
fn to_ref(self) -> PatternRef<Cx<'a,'tcx>> {
|
||||||
|
PatternRef::Mirror(Box::new(self))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'tcx:'a,T,U> ToRef<Cx<'a,'tcx>> for &'tcx Option<T>
|
||||||
|
where &'tcx T: ToRef<Cx<'a,'tcx>, Output=U>
|
||||||
|
{
|
||||||
|
type Output = Option<U>;
|
||||||
|
|
||||||
|
fn to_ref(self) -> Option<U> {
|
||||||
|
self.as_ref().map(|expr| expr.to_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'tcx:'a,T,U> ToRef<Cx<'a,'tcx>> for &'tcx Vec<T>
|
||||||
|
where &'tcx T: ToRef<Cx<'a,'tcx>, Output=U>
|
||||||
|
{
|
||||||
|
type Output = Vec<U>;
|
||||||
|
|
||||||
|
fn to_ref(self) -> Vec<U> {
|
||||||
|
self.iter().map(|expr| expr.to_ref()).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a,'tcx:'a> ToRef<Cx<'a,'tcx>> for &'tcx hir::Field {
|
||||||
|
type Output = FieldExprRef<Cx<'a,'tcx>>;
|
||||||
|
|
||||||
|
fn to_ref(self) -> FieldExprRef<Cx<'a,'tcx>> {
|
||||||
|
FieldExprRef {
|
||||||
|
name: Field::Named(self.ident.node.name),
|
||||||
|
expr: self.expr.to_ref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -272,13 +272,20 @@ pub const KNOWN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeGat
|
||||||
feature")),
|
feature")),
|
||||||
("rustc_variance", Normal, Gated("rustc_attrs",
|
("rustc_variance", Normal, Gated("rustc_attrs",
|
||||||
"the `#[rustc_variance]` attribute \
|
"the `#[rustc_variance]` attribute \
|
||||||
is an experimental feature")),
|
is just used for rustc unit tests \
|
||||||
|
and will never be stable")),
|
||||||
("rustc_error", Whitelisted, Gated("rustc_attrs",
|
("rustc_error", Whitelisted, Gated("rustc_attrs",
|
||||||
"the `#[rustc_error]` attribute \
|
"the `#[rustc_error]` attribute \
|
||||||
is an experimental feature")),
|
is just used for rustc unit tests \
|
||||||
|
and will never be stable")),
|
||||||
("rustc_move_fragments", Normal, Gated("rustc_attrs",
|
("rustc_move_fragments", Normal, Gated("rustc_attrs",
|
||||||
"the `#[rustc_move_fragments]` attribute \
|
"the `#[rustc_move_fragments]` attribute \
|
||||||
is an experimental feature")),
|
is just used for rustc unit tests \
|
||||||
|
and will never be stable")),
|
||||||
|
("rustc_mir", Normal, Gated("rustc_attrs",
|
||||||
|
"the `#[rustc_mir]` attribute \
|
||||||
|
is just used for rustc unit tests \
|
||||||
|
and will never be stable")),
|
||||||
|
|
||||||
("allow_internal_unstable", Normal, Gated("allow_internal_unstable",
|
("allow_internal_unstable", Normal, Gated("allow_internal_unstable",
|
||||||
EXPLAIN_ALLOW_INTERNAL_UNSTABLE)),
|
EXPLAIN_ALLOW_INTERNAL_UNSTABLE)),
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
#[rustc_variance] //~ ERROR the `#[rustc_variance]` attribute is just used for rustc unit tests and will never be stable
|
#[rustc_variance] //~ ERROR the `#[rustc_variance]` attribute is just used for rustc unit tests and will never be stable
|
||||||
#[rustc_error] //~ ERROR the `#[rustc_error]` attribute is just used for rustc unit tests and will never be stable
|
#[rustc_error] //~ ERROR the `#[rustc_error]` attribute is just used for rustc unit tests and will never be stable
|
||||||
#[rustc_move_fragments] //~ ERROR the `#[rustc_move_fragments]` attribute is an experimental feature
|
#[rustc_move_fragments] //~ ERROR the `#[rustc_move_fragments]` attribute is just used for rustc unit tests and will never be stable
|
||||||
#[rustc_foo]
|
#[rustc_foo]
|
||||||
//~^ ERROR unless otherwise specified, attributes with the prefix `rustc_` are reserved for internal compiler diagnostics
|
//~^ ERROR unless otherwise specified, attributes with the prefix `rustc_` are reserved for internal compiler diagnostics
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue