]> git.lizzy.rs Git - rust.git/blob - src/librustc_mir/build/expr/as_temp.rs
Allow `Downcast` projections in `qualify_min_const_fn`
[rust.git] / src / librustc_mir / build / expr / as_temp.rs
1 //! See docs in build/expr/mod.rs
2
3 use crate::build::{BlockAnd, BlockAndExtension, Builder};
4 use crate::build::scope::DropKind;
5 use crate::hair::*;
6 use rustc::hir;
7 use rustc::middle::region;
8 use rustc::mir::*;
9
10 impl<'a, 'tcx> Builder<'a, 'tcx> {
11     /// Compile `expr` into a fresh temporary. This is used when building
12     /// up rvalues so as to freeze the value that will be consumed.
13     pub fn as_temp<M>(
14         &mut self,
15         block: BasicBlock,
16         temp_lifetime: Option<region::Scope>,
17         expr: M,
18         mutability: Mutability,
19     ) -> BlockAnd<Local>
20     where
21         M: Mirror<'tcx, Output = Expr<'tcx>>,
22     {
23         let expr = self.hir.mirror(expr);
24         self.expr_as_temp(block, temp_lifetime, expr, mutability)
25     }
26
27     fn expr_as_temp(
28         &mut self,
29         mut block: BasicBlock,
30         temp_lifetime: Option<region::Scope>,
31         expr: Expr<'tcx>,
32         mutability: Mutability,
33     ) -> BlockAnd<Local> {
34         debug!(
35             "expr_as_temp(block={:?}, temp_lifetime={:?}, expr={:?}, mutability={:?})",
36             block, temp_lifetime, expr, mutability
37         );
38         let this = self;
39
40         let expr_span = expr.span;
41         let source_info = this.source_info(expr_span);
42         if let ExprKind::Scope {
43             region_scope,
44             lint_level,
45             value,
46         } = expr.kind
47         {
48             return this.in_scope((region_scope, source_info), lint_level, |this| {
49                 this.as_temp(block, temp_lifetime, value, mutability)
50             });
51         }
52
53         let expr_ty = expr.ty;
54         let temp = {
55             let mut local_decl = LocalDecl::new_temp(expr_ty, expr_span);
56             if mutability == Mutability::Not {
57                 local_decl = local_decl.immutable();
58             }
59
60             debug!("creating temp {:?} with block_context: {:?}", local_decl, this.block_context);
61             // Find out whether this temp is being created within the
62             // tail expression of a block whose result is ignored.
63             if let Some(tail_info) = this.block_context.currently_in_block_tail() {
64                 local_decl = local_decl.block_tail(tail_info);
65             }
66             this.local_decls.push(local_decl)
67         };
68         let temp_place = &Place::from(temp);
69
70         match expr.kind {
71             // Don't bother with StorageLive and Dead for these temporaries,
72             // they are never assigned.
73             ExprKind::Break { .. } |
74             ExprKind::Continue { .. } |
75             ExprKind::Return { .. } => (),
76             ExprKind::Block {
77                 body: hir::Block { expr: None, targeted_by_break: false, .. }
78             } if expr_ty.is_never() => (),
79             _ => {
80                 this.cfg.push(
81                     block,
82                     Statement {
83                         source_info,
84                         kind: StatementKind::StorageLive(temp),
85                     },
86                 );
87
88                 // In constants, `temp_lifetime` is `None` for temporaries that
89                 // live for the `'static` lifetime. Thus we do not drop these
90                 // temporaries and simply leak them.
91                 // This is equivalent to what `let x = &foo();` does in
92                 // functions. The temporary is lifted to their surrounding
93                 // scope. In a function that means the temporary lives until
94                 // just before the function returns. In constants that means it
95                 // outlives the constant's initialization value computation.
96                 // Anything outliving a constant must have the `'static`
97                 // lifetime and live forever.
98                 // Anything with a shorter lifetime (e.g the `&foo()` in
99                 // `bar(&foo())` or anything within a block will keep the
100                 // regular drops just like runtime code.
101                 if let Some(temp_lifetime) = temp_lifetime {
102                     this.schedule_drop(
103                         expr_span,
104                         temp_lifetime,
105                         temp,
106                         DropKind::Storage,
107                     );
108                 }
109             }
110         }
111
112         unpack!(block = this.into(temp_place, block, expr));
113
114         if let Some(temp_lifetime) = temp_lifetime {
115             this.schedule_drop(
116                 expr_span,
117                 temp_lifetime,
118                 temp,
119                 DropKind::Value,
120             );
121         }
122
123         block.and(temp)
124     }
125 }