rust/compiler/rustc_mir_build/src/build/expr/into.rs

521 lines
23 KiB
Rust
Raw Normal View History

2015-08-18 21:59:21 +00:00
//! See docs in build/expr/mod.rs
2019-02-07 21:28:15 +00:00
use crate::build::expr::category::{Category, RvalueFunc};
2020-10-24 21:30:57 +00:00
use crate::build::scope::DropKind;
2019-02-07 21:28:15 +00:00
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
2020-07-21 09:09:27 +00:00
use crate::thir::*;
2020-04-27 17:56:11 +00:00
use rustc_ast::InlineAsmOptions;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_hir as hir;
2020-10-24 21:30:57 +00:00
use rustc_middle::middle::region;
use rustc_middle::mir::*;
use rustc_middle::ty::{self, CanonicalUserTypeAnnotation};
use rustc_span::symbol::sym;
use rustc_target::spec::abi::Abi;
use std::slice;
impl<'a, 'tcx> Builder<'a, 'tcx> {
2015-08-18 21:59:21 +00:00
/// Compile `expr`, storing the result into `destination`, which
/// is assumed to be uninitialized.
2020-10-24 21:30:57 +00:00
/// If a `drop_scope` is provided, `destination` is scheduled to be dropped
/// in `scope` once it has been initialized.
crate fn into_expr(
2018-09-06 21:34:26 +00:00
&mut self,
destination: Place<'tcx>,
2020-10-24 21:30:57 +00:00
scope: Option<region::Scope>,
2018-09-06 21:34:26 +00:00
mut block: BasicBlock,
expr: Expr<'tcx>,
) -> BlockAnd<()> {
2020-10-24 21:30:57 +00:00
debug!(
"into_expr(destination={:?}, scope={:?}, block={:?}, expr={:?})",
destination, scope, block, expr
);
2015-08-18 21:59:21 +00:00
// 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;
let source_info = this.source_info(expr_span);
2015-08-18 21:59:21 +00:00
2020-12-24 01:55:21 +00:00
let expr_is_block_or_scope = matches!(expr.kind, ExprKind::Block { .. } | ExprKind::Scope { .. });
2020-10-24 21:30:57 +00:00
let schedule_drop = move |this: &mut Self| {
if let Some(drop_scope) = scope {
let local =
destination.as_local().expect("cannot schedule drop of non-Local place");
this.schedule_drop(expr_span, drop_scope, local, DropKind::Value);
}
};
if !expr_is_block_or_scope {
this.block_context.push(BlockFrame::SubExpr);
}
let block_and = match expr.kind {
2019-12-22 22:42:04 +00:00
ExprKind::Scope { region_scope, lint_level, value } => {
let region_scope = (region_scope, source_info);
ensure_sufficient_stack(|| {
this.in_scope(region_scope, lint_level, |this| {
2020-10-24 21:30:57 +00:00
this.into(destination, scope, block, value)
})
})
2015-08-18 21:59:21 +00:00
}
ExprKind::Block { body: ast_block } => {
2020-10-24 21:30:57 +00:00
this.ast_block(destination, scope, block, ast_block, source_info)
2015-08-18 21:59:21 +00:00
}
2019-02-02 14:55:10 +00:00
ExprKind::Match { scrutinee, arms } => {
2020-10-24 21:30:57 +00:00
this.match_expr(destination, scope, expr_span, block, scrutinee, arms)
2015-08-18 21:59:21 +00:00
}
ExprKind::NeverToAny { source } => {
let source = this.hir.mirror(source);
2020-10-27 01:02:48 +00:00
let is_call = matches!(source.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. });
// (#66975) Source could be a const of type `!`, so has to
// exist in the generated MIR.
unpack!(block = this.as_temp(block, Some(this.local_scope()), source, Mutability::Mut,));
2016-06-25 10:42:52 +00:00
// This is an optimization. If the expression was a call then we already have an
// unreachable block. Don't bother to terminate it and create a new one.
2020-10-24 21:30:57 +00:00
schedule_drop(this);
if is_call {
block.unit()
} else {
2019-12-22 22:42:04 +00:00
this.cfg.terminate(block, source_info, TerminatorKind::Unreachable);
let end_block = this.cfg.start_new_block();
end_block.unit()
}
2016-06-25 10:42:52 +00:00
}
2015-08-18 21:59:21 +00:00
ExprKind::LogicalOp { op, lhs, rhs } => {
// And:
//
// [block: If(lhs)] -true-> [else_block: If(rhs)] -true-> [true_block]
// | | (false)
// +----------false-----------+------------------> [false_block]
2015-08-18 21:59:21 +00:00
//
// Or:
//
// [block: If(lhs)] -false-> [else_block: If(rhs)] -true-> [true_block]
// | (true) | (false)
// [true_block] [false_block]
2015-08-18 21:59:21 +00:00
let (true_block, false_block, mut else_block, join_block) = (
this.cfg.start_new_block(),
2018-09-06 21:34:26 +00:00
this.cfg.start_new_block(),
this.cfg.start_new_block(),
this.cfg.start_new_block(),
);
2015-08-18 21:59:21 +00:00
let lhs = unpack!(block = this.as_local_operand(block, lhs));
2015-08-18 21:59:21 +00:00
let blocks = match op {
LogicalOp::And => (else_block, false_block),
LogicalOp::Or => (true_block, else_block),
2015-08-18 21:59:21 +00:00
};
let term = TerminatorKind::if_(this.hir.tcx(), lhs, blocks.0, blocks.1);
this.cfg.terminate(block, source_info, term);
2015-08-18 21:59:21 +00:00
let rhs = unpack!(else_block = this.as_local_operand(else_block, rhs));
let term = TerminatorKind::if_(this.hir.tcx(), rhs, true_block, false_block);
this.cfg.terminate(else_block, source_info, term);
2015-08-18 21:59:21 +00:00
this.cfg.push_assign_constant(
true_block,
2018-09-06 21:34:26 +00:00
source_info,
destination,
2019-12-22 22:42:04 +00:00
Constant { span: expr_span, user_ty: None, literal: this.hir.true_literal() },
2018-09-06 21:34:26 +00:00
);
this.cfg.push_assign_constant(
false_block,
2018-09-06 21:34:26 +00:00
source_info,
destination,
2019-12-22 22:42:04 +00:00
Constant { span: expr_span, user_ty: None, literal: this.hir.false_literal() },
2018-09-06 21:34:26 +00:00
);
2015-08-18 21:59:21 +00:00
// Link up both branches:
this.cfg.goto(true_block, source_info, join_block);
this.cfg.goto(false_block, source_info, join_block);
2015-08-18 21:59:21 +00:00
join_block.unit()
}
2019-06-19 15:21:28 +00:00
ExprKind::Loop { body } => {
// [block]
// |
// [loop_block] -> [body_block] -/eval. body/-> [body_block_end]
// | ^ |
// false link | |
// | +-----------------------------------------+
// +-> [diverge_cleanup]
// The false link is required to make sure borrowck considers unwinds through the
// body, even when the exact code in the body cannot unwind
2015-08-18 21:59:21 +00:00
let loop_block = this.cfg.start_new_block();
// Start the loop.
this.cfg.goto(block, source_info, loop_block);
2015-08-18 21:59:21 +00:00
2020-10-24 21:30:57 +00:00
this.in_breakable_scope(
Some(loop_block),
destination,
scope,
expr_span,
move |this| {
// conduct the test, if necessary
let body_block = this.cfg.start_new_block();
this.cfg.terminate(
loop_block,
source_info,
TerminatorKind::FalseUnwind { real_target: body_block, unwind: None },
);
this.diverge_from(loop_block);
// The “return” value of the loop body must always be an unit. We therefore
// introduce a unit temporary as the destination for the loop body.
let tmp = this.get_unit_temp();
// Execute the body, branching back to the test.
// We don't need to provide a drop scope because `tmp`
// has type `()`.
let body_block_end = unpack!(this.into(tmp, None, body_block, body));
this.cfg.goto(body_block_end, source_info, loop_block);
schedule_drop(this);
// Loops are only exited by `break` expressions.
None
},
)
2015-08-18 21:59:21 +00:00
}
ExprKind::Call { ty, fun, args, from_hir_call, fn_span } => {
2020-08-02 22:49:11 +00:00
let intrinsic = match *ty.kind() {
2018-09-06 21:34:26 +00:00
ty::FnDef(def_id, _) => {
let f = ty.fn_sig(this.hir.tcx());
2018-09-06 21:34:26 +00:00
if f.abi() == Abi::RustIntrinsic || f.abi() == Abi::PlatformIntrinsic {
Some(this.hir.tcx().item_name(def_id))
} else {
None
}
}
2018-09-06 21:34:26 +00:00
_ => None,
};
let fun = unpack!(block = this.as_local_operand(block, fun));
if let Some(sym::move_val_init) = intrinsic {
// `move_val_init` has "magic" semantics - the second argument is
// always evaluated "directly" into the first one.
let mut args = args.into_iter();
let ptr = args.next().expect("0 arguments to `move_val_init`");
let val = args.next().expect("1 argument to `move_val_init`");
assert!(args.next().is_none(), ">2 arguments to `move_val_init`");
let ptr = this.hir.mirror(ptr);
let ptr_ty = ptr.ty;
// Create an *internal* temp for the pointer, so that unsafety
// checking won't complain about the raw pointer assignment.
2020-07-16 21:09:51 +00:00
let ptr_temp = this
.local_decls
.push(LocalDecl::with_source_info(ptr_ty, source_info).internal());
let ptr_temp = Place::from(ptr_temp);
2020-10-24 21:30:57 +00:00
// No need for a scope, ptr_temp doesn't need drop
let block = unpack!(this.into(ptr_temp, None, block, ptr));
// Maybe we should provide a scope here so that
// `move_val_init` wouldn't leak on panic even with an
// arbitrary `val` expression, but `schedule_drop`,
// borrowck and drop elaboration all prevent us from
// dropping `ptr_temp.deref()`.
this.into(this.hir.tcx().mk_place_deref(ptr_temp), None, block, val)
} else {
2018-09-06 21:34:26 +00:00
let args: Vec<_> = args
.into_iter()
.map(|arg| unpack!(block = this.as_local_call_operand(block, arg)))
2018-09-06 21:34:26 +00:00
.collect();
let success = this.cfg.start_new_block();
this.record_operands_moved(&args);
debug!("into_expr: fn_span={:?}", fn_span);
2018-09-06 21:34:26 +00:00
this.cfg.terminate(
block,
source_info,
TerminatorKind::Call {
func: fun,
args,
cleanup: None,
// FIXME(varkor): replace this with an uninhabitedness-based check.
// This requires getting access to the current module to call
// `tcx.is_ty_uninhabited_from`, which is currently tricky to do.
destination: if expr.ty.is_never() {
None
} else {
Some((destination, success))
},
from_hir_call,
2020-07-16 21:09:51 +00:00
fn_span,
2018-09-06 21:34:26 +00:00
},
);
this.diverge_from(block);
2020-10-24 21:30:57 +00:00
schedule_drop(this);
success.unit()
}
2015-08-18 21:59:21 +00:00
}
2020-10-24 21:30:57 +00:00
ExprKind::Use { source } => this.into(destination, scope, block, source),
ExprKind::Borrow { arg, borrow_kind } => {
// We don't do this in `as_rvalue` because we use `as_place`
// for borrow expressions, so we cannot create an `RValue` that
// remains valid across user code. `as_rvalue` is usually called
// by this method anyway, so this shouldn't cause too many
// unnecessary temporaries.
let arg_place = match borrow_kind {
BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)),
_ => unpack!(block = this.as_place(block, arg)),
};
2019-12-22 22:42:04 +00:00
let borrow =
Rvalue::Ref(this.hir.tcx().lifetimes.re_erased, borrow_kind, arg_place);
this.cfg.push_assign(block, source_info, destination, borrow);
block.unit()
}
2019-12-22 22:42:04 +00:00
ExprKind::AddressOf { mutability, arg } => {
let place = match mutability {
hir::Mutability::Not => this.as_read_only_place(block, arg),
hir::Mutability::Mut => this.as_place(block, arg),
};
let address_of = Rvalue::AddressOf(mutability, unpack!(block = place));
this.cfg.push_assign(block, source_info, destination, address_of);
block.unit()
}
2019-12-22 22:42:04 +00:00
ExprKind::Adt { adt_def, variant_index, substs, user_ty, fields, base } => {
// See the notes for `ExprKind::Array` in `as_rvalue` and for
// `ExprKind::Borrow` above.
let is_union = adt_def.is_union();
2019-12-22 22:42:04 +00:00
let active_field_index = if is_union { Some(fields[0].name.index()) } else { None };
2019-12-22 22:42:04 +00:00
let scope = this.local_scope();
// first process the set of fields that were provided
// (evaluating them in order given by user)
let fields_map: FxHashMap<_, _> = fields
.into_iter()
.map(|f| (f.name, unpack!(block = this.as_operand(block, Some(scope), f.expr))))
2019-12-22 22:42:04 +00:00
.collect();
let field_names = this.hir.all_fields(adt_def, variant_index);
let fields: Vec<_> = if let Some(FruInfo { base, field_types }) = base {
let base = unpack!(block = this.as_place(block, base));
2019-12-22 22:42:04 +00:00
// MIR does not natively support FRU, so for each
// base-supplied field, generate an operand that
// reads it from the base.
field_names
.into_iter()
.zip(field_types.into_iter())
.map(|(n, ty)| match fields_map.get(&n) {
Some(v) => v.clone(),
None => this.consume_by_copy_or_move(
this.hir.tcx().mk_place_field(base, n, ty),
),
})
.collect()
} else {
field_names.iter().filter_map(|n| fields_map.get(n).cloned()).collect()
};
let inferred_ty = expr.ty;
let user_ty = user_ty.map(|ty| {
this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
span: source_info.span,
user_ty: ty,
inferred_ty,
})
});
let adt = box AggregateKind::Adt(
adt_def,
variant_index,
substs,
user_ty,
active_field_index,
);
this.record_operands_moved(&fields);
this.cfg.push_assign(
block,
source_info,
destination,
2019-12-22 22:42:04 +00:00
Rvalue::Aggregate(adt, fields),
);
2020-10-24 21:30:57 +00:00
schedule_drop(this);
block.unit()
}
2020-05-26 19:07:59 +00:00
ExprKind::InlineAsm { template, operands, options, line_spans } => {
2020-07-21 09:09:27 +00:00
use crate::thir;
2020-02-14 18:17:50 +00:00
use rustc_middle::mir;
let operands = operands
.into_iter()
.map(|op| match op {
2020-07-21 09:09:27 +00:00
thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
2020-02-14 18:17:50 +00:00
reg,
value: unpack!(block = this.as_local_operand(block, expr)),
},
2020-07-21 09:09:27 +00:00
thir::InlineAsmOperand::Out { reg, late, expr } => {
2020-02-14 18:17:50 +00:00
mir::InlineAsmOperand::Out {
reg,
late,
place: expr.map(|expr| unpack!(block = this.as_place(block, expr))),
}
}
2020-07-21 09:09:27 +00:00
thir::InlineAsmOperand::InOut { reg, late, expr } => {
2020-02-14 18:17:50 +00:00
let place = unpack!(block = this.as_place(block, expr));
mir::InlineAsmOperand::InOut {
reg,
late,
// This works because asm operands must be Copy
in_value: Operand::Copy(place),
out_place: Some(place),
}
}
2020-07-21 09:09:27 +00:00
thir::InlineAsmOperand::SplitInOut { reg, late, in_expr, out_expr } => {
2020-02-14 18:17:50 +00:00
mir::InlineAsmOperand::InOut {
reg,
late,
in_value: unpack!(block = this.as_local_operand(block, in_expr)),
out_place: out_expr.map(|out_expr| {
unpack!(block = this.as_place(block, out_expr))
}),
}
}
2020-07-21 09:09:27 +00:00
thir::InlineAsmOperand::Const { expr } => mir::InlineAsmOperand::Const {
2020-02-14 18:17:50 +00:00
value: unpack!(block = this.as_local_operand(block, expr)),
},
2020-07-21 09:09:27 +00:00
thir::InlineAsmOperand::SymFn { expr } => {
2020-02-14 18:17:50 +00:00
mir::InlineAsmOperand::SymFn { value: box this.as_constant(expr) }
}
2020-07-21 09:09:27 +00:00
thir::InlineAsmOperand::SymStatic { def_id } => {
mir::InlineAsmOperand::SymStatic { def_id }
2020-02-14 18:17:50 +00:00
}
})
.collect();
let destination = this.cfg.start_new_block();
this.cfg.terminate(
block,
source_info,
TerminatorKind::InlineAsm {
template,
operands,
options,
2020-05-26 19:07:59 +00:00
line_spans,
2020-02-14 18:17:50 +00:00
destination: if options.contains(InlineAsmOptions::NORETURN) {
None
} else {
Some(destination)
},
},
);
destination.unit()
}
Various improvements to MIR and LLVM IR Construction Primarily affects the MIR construction, which indirectly improves LLVM IR generation, but some LLVM IR changes have been made too. * Handle "statement expressions" more intelligently. These are expressions that always evaluate to `()`. Previously a temporary would be generated as a destination to translate into, which is unnecessary. This affects assignment, augmented assignment, `return`, `break` and `continue`. * Avoid inserting drops for non-drop types in more places. Scheduled drops were already skipped for types that we knew wouldn't need dropping at construction time. However manually-inserted drops like those for `x` in `x = y;` were still generated. `build_drop` now takes a type parameter like its `schedule_drop` counterpart and checks to see if the type needs dropping. * Avoid generating an extra temporary for an assignment where the types involved don't need dropping. Previously an expression like `a = b + 1;` would result in a temporary for `b + 1`. This is so the RHS can be evaluated, then the LHS evaluated and dropped and have everything work correctly. However, this isn't necessary if the `LHS` doesn't need a drop, as we can just overwrite the existing value. * Improves lvalue analysis to allow treating an `Rvalue::Use` as an operand in certain conditions. The reason for it never being an operand is so it can be zeroed/drop-filled, but this is only true for types that need dropping. The first two changes result in significantly fewer MIR blocks being generated, as previously almost every statement would end up generating a new block due to the drop of the `()` temporary being generated.
2016-04-15 00:36:16 +00:00
// These cases don't actually need a destination
2018-09-06 21:34:26 +00:00
ExprKind::Assign { .. }
| ExprKind::AssignOp { .. }
2020-07-16 21:09:51 +00:00
| ExprKind::LlvmInlineAsm { .. } => {
unpack!(block = this.stmt_expr(block, expr, None));
this.cfg.push_assign_unit(block, source_info, destination, this.hir.tcx());
block.unit()
Various improvements to MIR and LLVM IR Construction Primarily affects the MIR construction, which indirectly improves LLVM IR generation, but some LLVM IR changes have been made too. * Handle "statement expressions" more intelligently. These are expressions that always evaluate to `()`. Previously a temporary would be generated as a destination to translate into, which is unnecessary. This affects assignment, augmented assignment, `return`, `break` and `continue`. * Avoid inserting drops for non-drop types in more places. Scheduled drops were already skipped for types that we knew wouldn't need dropping at construction time. However manually-inserted drops like those for `x` in `x = y;` were still generated. `build_drop` now takes a type parameter like its `schedule_drop` counterpart and checks to see if the type needs dropping. * Avoid generating an extra temporary for an assignment where the types involved don't need dropping. Previously an expression like `a = b + 1;` would result in a temporary for `b + 1`. This is so the RHS can be evaluated, then the LHS evaluated and dropped and have everything work correctly. However, this isn't necessary if the `LHS` doesn't need a drop, as we can just overwrite the existing value. * Improves lvalue analysis to allow treating an `Rvalue::Use` as an operand in certain conditions. The reason for it never being an operand is so it can be zeroed/drop-filled, but this is only true for types that need dropping. The first two changes result in significantly fewer MIR blocks being generated, as previously almost every statement would end up generating a new block due to the drop of the `()` temporary being generated.
2016-04-15 00:36:16 +00:00
}
2020-07-16 21:09:51 +00:00
ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Return { .. } => {
unpack!(block = this.stmt_expr(block, expr, None));
// No assign, as these have type `!`.
block.unit()
}
// Avoid creating a temporary
2019-12-22 22:42:04 +00:00
ExprKind::VarRef { .. }
| ExprKind::UpvarRef { .. }
2019-12-22 22:42:04 +00:00
| ExprKind::PlaceTypeAscription { .. }
| ExprKind::ValueTypeAscription { .. } => {
debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
let place = unpack!(block = this.as_place(block, expr));
let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
2019-12-22 22:42:04 +00:00
this.cfg.push_assign(block, source_info, destination, rvalue);
2020-10-24 21:30:57 +00:00
schedule_drop(this);
block.unit()
}
2018-09-06 21:34:26 +00:00
ExprKind::Index { .. } | ExprKind::Deref { .. } | ExprKind::Field { .. } => {
debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
// Create a "fake" temporary variable so that we check that the
// value is Sized. Usually, this is caught in type checking, but
// in the case of box expr there is no such check.
if !destination.projection.is_empty() {
this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
}
debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
let place = unpack!(block = this.as_place(block, expr));
let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
2019-12-22 22:42:04 +00:00
this.cfg.push_assign(block, source_info, destination, rvalue);
2020-10-24 21:30:57 +00:00
schedule_drop(this);
block.unit()
}
ExprKind::Yield { value } => {
let scope = this.local_scope();
let value = unpack!(block = this.as_operand(block, Some(scope), value));
let resume = this.cfg.start_new_block();
this.record_operands_moved(slice::from_ref(&value));
this.cfg.terminate(
block,
source_info,
TerminatorKind::Yield { value, resume, resume_arg: destination, drop: None },
);
this.generator_drop_cleanup(block);
2020-10-24 21:30:57 +00:00
schedule_drop(this);
resume.unit()
}
2015-08-18 21:59:21 +00:00
// these are the cases that are more naturally handled by some other mode
2018-09-06 21:34:26 +00:00
ExprKind::Unary { .. }
| ExprKind::Binary { .. }
| ExprKind::Box { .. }
| ExprKind::Cast { .. }
| ExprKind::Pointer { .. }
2018-09-06 21:34:26 +00:00
| ExprKind::Repeat { .. }
| ExprKind::Array { .. }
| ExprKind::Tuple { .. }
| ExprKind::Closure { .. }
2020-10-06 20:51:15 +00:00
| ExprKind::ConstBlock { .. }
2018-09-06 21:34:26 +00:00
| ExprKind::Literal { .. }
2020-05-02 19:44:25 +00:00
| ExprKind::ThreadLocalRef(_)
| ExprKind::StaticRef { .. } => {
2015-08-18 21:59:21 +00:00
debug_assert!(match Category::of(&expr.kind).unwrap() {
2018-09-24 21:53:28 +00:00
// should be handled above
2015-08-18 21:59:21 +00:00
Category::Rvalue(RvalueFunc::Into) => false,
2018-09-24 21:53:28 +00:00
// must be handled above or else we get an
// infinite loop in the builder; see
// e.g., `ExprKind::VarRef` above
2018-09-24 21:53:28 +00:00
Category::Place => false,
2015-08-18 21:59:21 +00:00
_ => true,
});
let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
2018-11-20 19:07:17 +00:00
this.cfg.push_assign(block, source_info, destination, rvalue);
2020-10-24 21:30:57 +00:00
schedule_drop(this);
2015-08-18 21:59:21 +00:00
block.unit()
}
};
if !expr_is_block_or_scope {
let popped = this.block_context.pop();
assert!(popped.is_some());
2015-08-18 21:59:21 +00:00
}
block_and
2015-08-18 21:59:21 +00:00
}
}