From 698f8c2f01ea549d77d7dc3338a12e04c11057b9 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:02:58 +0200 Subject: Adding upstream version 1.64.0+dfsg1. Signed-off-by: Daniel Baumann --- compiler/rustc_mir_build/src/build/expr/as_temp.rs | 119 +++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 compiler/rustc_mir_build/src/build/expr/as_temp.rs (limited to 'compiler/rustc_mir_build/src/build/expr/as_temp.rs') diff --git a/compiler/rustc_mir_build/src/build/expr/as_temp.rs b/compiler/rustc_mir_build/src/build/expr/as_temp.rs new file mode 100644 index 000000000..724b72f87 --- /dev/null +++ b/compiler/rustc_mir_build/src/build/expr/as_temp.rs @@ -0,0 +1,119 @@ +//! 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. + pub(crate) fn as_temp( + &mut self, + block: BasicBlock, + temp_lifetime: Option, + expr: &Expr<'tcx>, + mutability: Mutability, + ) -> BlockAnd { + // 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, + expr: &Expr<'tcx>, + mutability: Mutability, + ) -> BlockAnd { + 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::NamedConst { def_id, .. } | ExprKind::ConstParam { 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) + } +} -- cgit v1.2.3