mirror of
https://github.com/rust-lang/rust.git
synced 2025-11-01 21:45:15 +00:00
120 lines
5.2 KiB
Rust
120 lines
5.2 KiB
Rust
//! See docs in build/expr/mod.rs
|
|
|
|
use crate::build::scope::DropKind;
|
|
use crate::build::{BlockAnd, BlockAndExtension, Builder};
|
|
use rustc_data_structures::stack::ensure_sufficient_stack;
|
|
use rustc_middle::middle::region;
|
|
use rustc_middle::mir::*;
|
|
use rustc_middle::thir::*;
|
|
|
|
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|
/// Compile `expr` into a fresh temporary. This is used when building
|
|
/// up rvalues so as to freeze the value that will be consumed.
|
|
crate fn as_temp(
|
|
&mut self,
|
|
block: BasicBlock,
|
|
temp_lifetime: Option<region::Scope>,
|
|
expr: &Expr<'tcx>,
|
|
mutability: Mutability,
|
|
) -> BlockAnd<Local> {
|
|
// this is the only place in mir building that we need to truly need to worry about
|
|
// infinite recursion. Everything else does recurse, too, but it always gets broken up
|
|
// at some point by inserting an intermediate temporary
|
|
ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr, mutability))
|
|
}
|
|
|
|
fn as_temp_inner(
|
|
&mut self,
|
|
mut block: BasicBlock,
|
|
temp_lifetime: Option<region::Scope>,
|
|
expr: &Expr<'tcx>,
|
|
mutability: Mutability,
|
|
) -> BlockAnd<Local> {
|
|
debug!(
|
|
"as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})",
|
|
block, temp_lifetime, expr, mutability
|
|
);
|
|
let this = self;
|
|
|
|
let expr_span = expr.span;
|
|
let source_info = this.source_info(expr_span);
|
|
if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
|
|
return this.in_scope((region_scope, source_info), lint_level, |this| {
|
|
this.as_temp(block, temp_lifetime, &this.thir[value], mutability)
|
|
});
|
|
}
|
|
|
|
let expr_ty = expr.ty;
|
|
let temp = {
|
|
let mut local_decl = LocalDecl::new(expr_ty, expr_span);
|
|
if mutability == Mutability::Not {
|
|
local_decl = local_decl.immutable();
|
|
}
|
|
|
|
debug!("creating temp {:?} with block_context: {:?}", local_decl, this.block_context);
|
|
// Find out whether this temp is being created within the
|
|
// tail expression of a block whose result is ignored.
|
|
if let Some(tail_info) = this.block_context.currently_in_block_tail() {
|
|
local_decl = local_decl.block_tail(tail_info);
|
|
}
|
|
match expr.kind {
|
|
ExprKind::StaticRef { def_id, .. } => {
|
|
assert!(!this.tcx.is_thread_local_static(def_id));
|
|
local_decl.internal = true;
|
|
local_decl.local_info =
|
|
Some(Box::new(LocalInfo::StaticRef { def_id, is_thread_local: false }));
|
|
}
|
|
ExprKind::ThreadLocalRef(def_id) => {
|
|
assert!(this.tcx.is_thread_local_static(def_id));
|
|
local_decl.internal = true;
|
|
local_decl.local_info =
|
|
Some(Box::new(LocalInfo::StaticRef { def_id, is_thread_local: true }));
|
|
}
|
|
ExprKind::Literal { const_id: Some(def_id), .. } => {
|
|
local_decl.local_info = Some(Box::new(LocalInfo::ConstRef { def_id }));
|
|
}
|
|
_ => {}
|
|
}
|
|
this.local_decls.push(local_decl)
|
|
};
|
|
let temp_place = Place::from(temp);
|
|
|
|
match expr.kind {
|
|
// Don't bother with StorageLive and Dead for these temporaries,
|
|
// they are never assigned.
|
|
ExprKind::Break { .. } | ExprKind::Continue { .. } | ExprKind::Return { .. } => (),
|
|
ExprKind::Block { body: Block { expr: None, targeted_by_break: false, .. } }
|
|
if expr_ty.is_never() => {}
|
|
_ => {
|
|
this.cfg
|
|
.push(block, Statement { source_info, kind: StatementKind::StorageLive(temp) });
|
|
|
|
// In constants, `temp_lifetime` is `None` for temporaries that
|
|
// live for the `'static` lifetime. Thus we do not drop these
|
|
// temporaries and simply leak them.
|
|
// This is equivalent to what `let x = &foo();` does in
|
|
// functions. The temporary is lifted to their surrounding
|
|
// scope. In a function that means the temporary lives until
|
|
// just before the function returns. In constants that means it
|
|
// outlives the constant's initialization value computation.
|
|
// Anything outliving a constant must have the `'static`
|
|
// lifetime and live forever.
|
|
// Anything with a shorter lifetime (e.g the `&foo()` in
|
|
// `bar(&foo())` or anything within a block will keep the
|
|
// regular drops just like runtime code.
|
|
if let Some(temp_lifetime) = temp_lifetime {
|
|
this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Storage);
|
|
}
|
|
}
|
|
}
|
|
|
|
unpack!(block = this.expr_into_dest(temp_place, block, expr));
|
|
|
|
if let Some(temp_lifetime) = temp_lifetime {
|
|
this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value);
|
|
}
|
|
|
|
block.and(temp)
|
|
}
|
|
}
|