1 //! See docs in build/expr/mod.rs
3 use crate::build::expr::category::Category;
4 use crate::build::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
5 use rustc_middle::middle::region;
6 use rustc_middle::mir::*;
7 use rustc_middle::thir::*;
9 impl<'a, 'tcx> Builder<'a, 'tcx> {
10 /// Returns an operand suitable for use until the end of the current
13 /// The operand returned from this function will *not be valid*
14 /// after the current enclosing `ExprKind::Scope` has ended, so
15 /// please do *not* return it from functions to avoid bad
17 crate fn as_local_operand(
21 ) -> BlockAnd<Operand<'tcx>> {
22 let local_scope = self.local_scope();
23 self.as_operand(block, Some(local_scope), expr, None, NeedsTemporary::Maybe)
26 /// Returns an operand suitable for use until the end of the current scope expression and
27 /// suitable also to be passed as function arguments.
29 /// The operand returned from this function will *not be valid* after an ExprKind::Scope is
30 /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an
31 /// operand suitable for use as a call argument. This is almost always equivalent to
32 /// `as_operand`, except for the particular case of passing values of (potentially) unsized
33 /// types "by value" (see details below).
35 /// The operand returned from this function will *not be valid*
36 /// after the current enclosing `ExprKind::Scope` has ended, so
37 /// please do *not* return it from functions to avoid bad
40 /// # Parameters of unsized types
42 /// We tweak the handling of parameters of unsized type slightly to avoid the need to create a
43 /// local variable of unsized type. For example, consider this program:
46 /// #![feature(unsized_locals, unsized_fn_params)]
47 /// # use core::fmt::Debug;
48 /// fn foo(p: dyn Debug) { dbg!(p); }
50 /// fn bar(box_p: Box<dyn Debug>) { foo(*box_p); }
53 /// Ordinarily, for sized types, we would compile the call `foo(*p)` like so:
55 /// ```ignore (illustrative)
56 /// let tmp0 = *box_p; // tmp0 would be the operand returned by this function call
60 /// But because the parameter to `foo` is of the unsized type `dyn Debug`, and because it is
61 /// being moved the deref of a box, we compile it slightly differently. The temporary `tmp0`
62 /// that we create *stores the entire box*, and the parameter to the call itself will be
65 /// ```ignore (illustrative)
66 /// let tmp0 = box_p; call foo(*tmp0)
69 /// This way, the temporary `tmp0` that we create has type `Box<dyn Debug>`, which is sized.
70 /// The value passed to the call (`*tmp0`) still has the `dyn Debug` type -- but the way that
71 /// calls are compiled means that this parameter will be passed "by reference", meaning that we
72 /// will actually provide a pointer to the interior of the box, and not move the `dyn Debug`
73 /// value to the stack.
75 /// See #68034 for more details.
76 crate fn as_local_call_operand(
80 ) -> BlockAnd<Operand<'tcx>> {
81 let local_scope = self.local_scope();
82 self.as_call_operand(block, Some(local_scope), expr)
85 /// Compile `expr` into a value that can be used as an operand.
86 /// If `expr` is a place like `x`, this will introduce a
87 /// temporary `tmp = x`, so that we capture the value of `x` at
90 /// If we end up needing to create a temporary, then we will use
91 /// `local_info` as its `LocalInfo`, unless `as_temporary`
92 /// has already assigned it a non-`None` `LocalInfo`.
93 /// Normally, you should use `None` for `local_info`
95 /// The operand is known to be live until the end of `scope`.
97 /// Like `as_local_call_operand`, except that the argument will
98 /// not be valid once `scope` ends.
99 #[instrument(level = "debug", skip(self, scope))]
102 mut block: BasicBlock,
103 scope: Option<region::Scope>,
105 local_info: Option<Box<LocalInfo<'tcx>>>,
106 needs_temporary: NeedsTemporary,
107 ) -> BlockAnd<Operand<'tcx>> {
110 if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
111 let source_info = this.source_info(expr.span);
112 let region_scope = (region_scope, source_info);
113 return this.in_scope(region_scope, lint_level, |this| {
114 this.as_operand(block, scope, &this.thir[value], local_info, needs_temporary)
118 let category = Category::of(&expr.kind).unwrap();
119 debug!(?category, ?expr.kind);
121 Category::Constant if let NeedsTemporary::No = needs_temporary || !expr.ty.needs_drop(this.tcx, this.param_env) => {
122 let constant = this.as_constant(expr);
123 block.and(Operand::Constant(Box::new(constant)))
125 Category::Constant | Category::Place | Category::Rvalue(..) => {
126 let operand = unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut));
127 if this.local_decls[operand].local_info.is_none() {
128 this.local_decls[operand].local_info = local_info;
130 block.and(Operand::Move(Place::from(operand)))
135 crate fn as_call_operand(
137 mut block: BasicBlock,
138 scope: Option<region::Scope>,
140 ) -> BlockAnd<Operand<'tcx>> {
141 debug!("as_call_operand(block={:?}, expr={:?})", block, expr);
144 if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
145 let source_info = this.source_info(expr.span);
146 let region_scope = (region_scope, source_info);
147 return this.in_scope(region_scope, lint_level, |this| {
148 this.as_call_operand(block, scope, &this.thir[value])
154 if tcx.features().unsized_fn_params {
156 let span = expr.span;
157 let param_env = this.param_env;
159 if !ty.is_sized(tcx.at(span), param_env) {
160 // !sized means !copy, so this is an unsized move
161 assert!(!ty.is_copy_modulo_regions(tcx.at(span), param_env));
163 // As described above, detect the case where we are passing a value of unsized
164 // type, and that value is coming from the deref of a box.
165 if let ExprKind::Deref { arg } = expr.kind {
166 // Generate let tmp0 = arg0
167 let operand = unpack!(
168 block = this.as_temp(block, scope, &this.thir[arg], Mutability::Mut)
171 // Return the operand *tmp0 to be used as the call argument
174 projection: tcx.intern_place_elems(&[PlaceElem::Deref]),
177 return block.and(Operand::Move(place));
182 this.as_operand(block, scope, expr, None, NeedsTemporary::Maybe)