Skip to main content

rustc_mir_build/builder/expr/
as_temp.rs

1//! See docs in build/expr/mod.rs
2
3use rustc_data_structures::stack::ensure_sufficient_stack;
4use rustc_hir::HirId;
5use rustc_middle::middle::region::{Scope, ScopeData, TempLifetime};
6use rustc_middle::mir::*;
7use rustc_middle::thir::*;
8use tracing::{debug, instrument};
9
10use crate::builder::scope::{DropKind, LintLevel};
11use crate::builder::{BlockAnd, BlockAndExtension, Builder};
12
13impl<'a, 'tcx> Builder<'a, 'tcx> {
14    /// Compile `expr` into a fresh temporary. This is used when building
15    /// up rvalues so as to freeze the value that will be consumed.
16    pub(crate) fn as_temp(
17        &mut self,
18        block: BasicBlock,
19        temp_lifetime: TempLifetime,
20        expr_id: ExprId,
21        mutability: Mutability,
22    ) -> BlockAnd<Local> {
23        // this is the only place in mir building that we need to truly need to worry about
24        // infinite recursion. Everything else does recurse, too, but it always gets broken up
25        // at some point by inserting an intermediate temporary
26        ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr_id, mutability))
27    }
28
29    #[allow(clippy :: suspicious_else_formatting)]
{
    let __tracing_attr_span;
    let __tracing_attr_guard;
    if ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::tracing::level_filters::LevelFilter::current() ||
            { false } {
        __tracing_attr_span =
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("as_temp_inner",
                                    "rustc_mir_build::builder::expr::as_temp",
                                    ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/as_temp.rs"),
                                    ::tracing_core::__macro_support::Option::Some(29u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::as_temp"),
                                    ::tracing_core::field::FieldSet::new(&["block",
                                                    "temp_lifetime", "expr_id", "mutability"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::SPAN)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let mut interest = ::tracing::subscriber::Interest::never();
                if ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                ::tracing::Level::DEBUG <=
                                    ::tracing::level_filters::LevelFilter::current() &&
                            { interest = __CALLSITE.interest(); !interest.is_never() }
                        &&
                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                            interest) {
                    let meta = __CALLSITE.metadata();
                    ::tracing::Span::new(meta,
                        &{
                                #[allow(unused_imports)]
                                use ::tracing::field::{debug, display, Value};
                                let mut iter = meta.fields().iter();
                                meta.fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&block)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&temp_lifetime)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&expr_id)
                                                            as &dyn Value)),
                                                (&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                    ::tracing::__macro_support::Option::Some(&::tracing::field::debug(&mutability)
                                                            as &dyn Value))])
                            })
                } else {
                    let span =
                        ::tracing::__macro_support::__disabled_span(__CALLSITE.metadata());
                    {};
                    span
                }
            };
        __tracing_attr_guard = __tracing_attr_span.enter();
    }

    #[warn(clippy :: suspicious_else_formatting)]
    {

        #[allow(unknown_lints, unreachable_code, clippy ::
        diverging_sub_expression, clippy :: empty_loop, clippy ::
        let_unit_value, clippy :: let_with_type_underscore, clippy ::
        needless_return, clippy :: unreachable)]
        if false {
            let __tracing_attr_fake_return: BlockAnd<Local> = loop {};
            return __tracing_attr_fake_return;
        }
        {
            let this = self;
            let expr = &this.thir[expr_id];
            let expr_span = expr.span;
            let source_info = this.source_info(expr_span);
            if let ExprKind::Scope { region_scope, hir_id, value } = expr.kind
                {
                return this.in_scope((region_scope, source_info),
                        LintLevel::Explicit(hir_id),
                        |this|
                            this.as_temp(block, temp_lifetime, value, mutability));
            }
            let expr_ty = expr.ty;
            let deduplicate_temps =
                this.fixed_temps_scope.is_some() &&
                    this.fixed_temps_scope == temp_lifetime.temp_lifetime;
            let temp =
                if deduplicate_temps &&
                        let Some(temp_index) = this.fixed_temps.get(&expr_id) {
                    *temp_index
                } else {
                    let mut local_decl = LocalDecl::new(expr_ty, expr_span);
                    if mutability.is_not() {
                        local_decl = local_decl.immutable();
                    }
                    {
                        use ::tracing::__macro_support::Callsite as _;
                        static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                            {
                                static META: ::tracing::Metadata<'static> =
                                    {
                                        ::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_build/src/builder/expr/as_temp.rs:61",
                                            "rustc_mir_build::builder::expr::as_temp",
                                            ::tracing::Level::DEBUG,
                                            ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/as_temp.rs"),
                                            ::tracing_core::__macro_support::Option::Some(61u32),
                                            ::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::as_temp"),
                                            ::tracing_core::field::FieldSet::new(&["message"],
                                                ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                            ::tracing::metadata::Kind::EVENT)
                                    };
                                ::tracing::callsite::DefaultCallsite::new(&META)
                            };
                        let enabled =
                            ::tracing::Level::DEBUG <=
                                        ::tracing::level_filters::STATIC_MAX_LEVEL &&
                                    ::tracing::Level::DEBUG <=
                                        ::tracing::level_filters::LevelFilter::current() &&
                                {
                                    let interest = __CALLSITE.interest();
                                    !interest.is_never() &&
                                        ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                                            interest)
                                };
                        if enabled {
                            (|value_set: ::tracing::field::ValueSet|
                                        {
                                            let meta = __CALLSITE.metadata();
                                            ::tracing::Event::dispatch(meta, &value_set);
                                            ;
                                        })({
                                    #[allow(unused_imports)]
                                    use ::tracing::field::{debug, display, Value};
                                    let mut iter = __CALLSITE.metadata().fields().iter();
                                    __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                        ::tracing::__macro_support::Option::Some(&format_args!("creating temp {0:?} with block_context: {1:?}",
                                                                        local_decl, this.block_context) as &dyn Value))])
                                });
                        } else { ; }
                    };
                    let local_info =
                        match expr.kind {
                            ExprKind::StaticRef { def_id, .. } => {
                                if !!this.tcx.is_thread_local_static(def_id) {
                                    ::core::panicking::panic("assertion failed: !this.tcx.is_thread_local_static(def_id)")
                                };
                                LocalInfo::StaticRef { def_id, is_thread_local: false }
                            }
                            ExprKind::ThreadLocalRef(def_id) => {
                                if !this.tcx.is_thread_local_static(def_id) {
                                    ::core::panicking::panic("assertion failed: this.tcx.is_thread_local_static(def_id)")
                                };
                                LocalInfo::StaticRef { def_id, is_thread_local: true }
                            }
                            ExprKind::NamedConst { def_id, .. } | ExprKind::ConstParam {
                                def_id, .. } => {
                                LocalInfo::ConstRef { def_id }
                            }
                            _ if
                                let Some(tail_info) =
                                    this.block_context.currently_in_block_tail() => {
                                LocalInfo::BlockTailTemp(tail_info)
                            }
                            _ if
                                let Some(Scope { data: ScopeData::IfThenRescope, local_id })
                                    = temp_lifetime.temp_lifetime => {
                                LocalInfo::IfThenRescopeTemp {
                                    if_then: HirId { owner: this.hir_id.owner, local_id },
                                }
                            }
                            _ => LocalInfo::Boring,
                        };
                    **local_decl.local_info.as_mut().unwrap_crate_local() =
                        local_info;
                    this.local_decls.push(local_decl)
                };
            {
                use ::tracing::__macro_support::Callsite as _;
                static __CALLSITE: ::tracing::callsite::DefaultCallsite =
                    {
                        static META: ::tracing::Metadata<'static> =
                            {
                                ::tracing_core::metadata::Metadata::new("event compiler/rustc_mir_build/src/builder/expr/as_temp.rs:93",
                                    "rustc_mir_build::builder::expr::as_temp",
                                    ::tracing::Level::DEBUG,
                                    ::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/as_temp.rs"),
                                    ::tracing_core::__macro_support::Option::Some(93u32),
                                    ::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::as_temp"),
                                    ::tracing_core::field::FieldSet::new(&["temp"],
                                        ::tracing_core::callsite::Identifier(&__CALLSITE)),
                                    ::tracing::metadata::Kind::EVENT)
                            };
                        ::tracing::callsite::DefaultCallsite::new(&META)
                    };
                let enabled =
                    ::tracing::Level::DEBUG <=
                                ::tracing::level_filters::STATIC_MAX_LEVEL &&
                            ::tracing::Level::DEBUG <=
                                ::tracing::level_filters::LevelFilter::current() &&
                        {
                            let interest = __CALLSITE.interest();
                            !interest.is_never() &&
                                ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                                    interest)
                        };
                if enabled {
                    (|value_set: ::tracing::field::ValueSet|
                                {
                                    let meta = __CALLSITE.metadata();
                                    ::tracing::Event::dispatch(meta, &value_set);
                                    ;
                                })({
                            #[allow(unused_imports)]
                            use ::tracing::field::{debug, display, Value};
                            let mut iter = __CALLSITE.metadata().fields().iter();
                            __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                                ::tracing::__macro_support::Option::Some(&debug(&temp) as
                                                        &dyn Value))])
                        });
                } else { ; }
            };
            if deduplicate_temps { this.fixed_temps.insert(expr_id, temp); }
            let temp_place = Place::from(temp);
            match expr.kind {
                ExprKind::Break { .. } | ExprKind::Continue { .. } |
                    ExprKind::Return { .. } => (),
                ExprKind::Block { block } if
                    let Block { expr: None, targeted_by_break: false, .. } =
                            this.thir[block] && expr_ty.is_never() => {}
                _ => {
                    this.cfg.push(block,
                        Statement::new(source_info,
                            StatementKind::StorageLive(temp)));
                    if let Some(temp_lifetime) = temp_lifetime.temp_lifetime {
                        this.schedule_drop(expr_span, temp_lifetime, temp,
                            DropKind::Storage);
                    }
                }
            }
            block =
                this.expr_into_dest(temp_place, block, expr_id).into_block();
            if let Some(temp_lifetime) = temp_lifetime.temp_lifetime {
                this.schedule_drop(expr_span, temp_lifetime, temp,
                    DropKind::Value);
            }
            if let Some(backwards_incompatible) =
                    temp_lifetime.backwards_incompatible {
                this.schedule_backwards_incompatible_drop(expr_span,
                    backwards_incompatible, temp);
            }
            block.and(temp)
        }
    }
}#[instrument(skip(self), level = "debug")]
30    fn as_temp_inner(
31        &mut self,
32        mut block: BasicBlock,
33        temp_lifetime: TempLifetime,
34        expr_id: ExprId,
35        mutability: Mutability,
36    ) -> BlockAnd<Local> {
37        let this = self; // See "LET_THIS_SELF".
38
39        let expr = &this.thir[expr_id];
40        let expr_span = expr.span;
41        let source_info = this.source_info(expr_span);
42        if let ExprKind::Scope { region_scope, hir_id, value } = expr.kind {
43            return this.in_scope(
44                (region_scope, source_info),
45                LintLevel::Explicit(hir_id),
46                |this| this.as_temp(block, temp_lifetime, value, mutability),
47            );
48        }
49
50        let expr_ty = expr.ty;
51        let deduplicate_temps = this.fixed_temps_scope.is_some()
52            && this.fixed_temps_scope == temp_lifetime.temp_lifetime;
53        let temp = if deduplicate_temps && let Some(temp_index) = this.fixed_temps.get(&expr_id) {
54            *temp_index
55        } else {
56            let mut local_decl = LocalDecl::new(expr_ty, expr_span);
57            if mutability.is_not() {
58                local_decl = local_decl.immutable();
59            }
60
61            debug!("creating temp {:?} with block_context: {:?}", local_decl, this.block_context);
62            let local_info = match expr.kind {
63                ExprKind::StaticRef { def_id, .. } => {
64                    assert!(!this.tcx.is_thread_local_static(def_id));
65                    LocalInfo::StaticRef { def_id, is_thread_local: false }
66                }
67                ExprKind::ThreadLocalRef(def_id) => {
68                    assert!(this.tcx.is_thread_local_static(def_id));
69                    LocalInfo::StaticRef { def_id, is_thread_local: true }
70                }
71                ExprKind::NamedConst { def_id, .. } | ExprKind::ConstParam { def_id, .. } => {
72                    LocalInfo::ConstRef { def_id }
73                }
74                // Find out whether this temp is being created within the
75                // tail expression of a block whose result is ignored.
76                _ if let Some(tail_info) = this.block_context.currently_in_block_tail() => {
77                    LocalInfo::BlockTailTemp(tail_info)
78                }
79
80                _ if let Some(Scope { data: ScopeData::IfThenRescope, local_id }) =
81                    temp_lifetime.temp_lifetime =>
82                {
83                    LocalInfo::IfThenRescopeTemp {
84                        if_then: HirId { owner: this.hir_id.owner, local_id },
85                    }
86                }
87
88                _ => LocalInfo::Boring,
89            };
90            **local_decl.local_info.as_mut().unwrap_crate_local() = local_info;
91            this.local_decls.push(local_decl)
92        };
93        debug!(?temp);
94        if deduplicate_temps {
95            this.fixed_temps.insert(expr_id, temp);
96        }
97        let temp_place = Place::from(temp);
98
99        match expr.kind {
100            // Don't bother with StorageLive and Dead for these temporaries,
101            // they are never assigned.
102            ExprKind::Break { .. } | ExprKind::Continue { .. } | ExprKind::Return { .. } => (),
103            ExprKind::Block { block }
104                if let Block { expr: None, targeted_by_break: false, .. } = this.thir[block]
105                    && expr_ty.is_never() => {}
106            _ => {
107                this.cfg.push(block, Statement::new(source_info, StatementKind::StorageLive(temp)));
108
109                // In constants, `temp_lifetime` is `None` for temporaries that
110                // live for the `'static` lifetime. Thus we do not drop these
111                // temporaries and simply leak them.
112                // This is equivalent to what `let x = &foo();` does in
113                // functions. The temporary is lifted to their surrounding
114                // scope. In a function that means the temporary lives until
115                // just before the function returns. In constants that means it
116                // outlives the constant's initialization value computation.
117                // Anything outliving a constant must have the `'static`
118                // lifetime and live forever.
119                // Anything with a shorter lifetime (e.g the `&foo()` in
120                // `bar(&foo())` or anything within a block will keep the
121                // regular drops just like runtime code.
122                if let Some(temp_lifetime) = temp_lifetime.temp_lifetime {
123                    this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Storage);
124                }
125            }
126        }
127
128        block = this.expr_into_dest(temp_place, block, expr_id).into_block();
129
130        if let Some(temp_lifetime) = temp_lifetime.temp_lifetime {
131            this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value);
132        }
133
134        if let Some(backwards_incompatible) = temp_lifetime.backwards_incompatible {
135            this.schedule_backwards_incompatible_drop(expr_span, backwards_incompatible, temp);
136        }
137
138        block.and(temp)
139    }
140}