add MIR code (unused thus far)

This commit is contained in:
Niko Matsakis 2015-08-18 17:59:21 -04:00
parent 0e764ec5ce
commit 9bd35c07c2
33 changed files with 5856 additions and 12 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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
&lt, 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)
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

225
src/librustc_mir/dump.rs Normal file
View File

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

View File

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

382
src/librustc_mir/hair.rs Normal file
View File

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

34
src/librustc_mir/lib.rs Normal file
View File

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

676
src/librustc_mir/repr.rs Normal file
View File

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

View File

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

View File

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

184
src/librustc_mir/tcx/mod.rs Normal file
View File

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

View File

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

View File

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

View File

@ -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)),

View File

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