1//! See docs in build/expr/mod.rs
23use rustc_middle::middle::region::TempLifetime;
4use rustc_middle::mir::*;
5use rustc_middle::thir::*;
6use tracing::{debug, instrument};
78use crate::builder::expr::category::Category;
9use crate::builder::scope::LintLevel;
10use crate::builder::{BlockAnd, BlockAndExtension, Builder, NeedsTemporary};
1112impl<'a, 'tcx> Builder<'a, 'tcx> {
13/// Construct a temporary lifetime restricted to just the local scope
14pub(crate) fn local_temp_lifetime(&self) -> TempLifetime {
15let local_scope = self.local_scope();
16TempLifetime { temp_lifetime: Some(local_scope), backwards_incompatible: None }
17 }
1819/// Returns an operand suitable for use until the end of the current
20 /// scope expression.
21 ///
22 /// The operand returned from this function will *not be valid*
23 /// after the current enclosing `ExprKind::Scope` has ended, so
24 /// please do *not* return it from functions to avoid bad
25 /// miscompiles.
26pub(crate) fn as_local_operand(
27&mut self,
28 block: BasicBlock,
29 expr_id: ExprId,
30 ) -> BlockAnd<Operand<'tcx>> {
31self.as_operand(
32block,
33self.local_temp_lifetime(),
34expr_id,
35 LocalInfo::Boring,
36 NeedsTemporary::Maybe,
37 )
38 }
3940/// Returns an operand suitable for use until the end of the current scope expression and
41 /// suitable also to be passed as function arguments.
42 ///
43 /// The operand returned from this function will *not be valid* after an ExprKind::Scope is
44 /// passed, so please do *not* return it from functions to avoid bad miscompiles. Returns an
45 /// operand suitable for use as a call argument. This is almost always equivalent to
46 /// `as_operand`, except for the particular case of passing values of (potentially) unsized
47 /// types "by value" (see details below).
48 ///
49 /// The operand returned from this function will *not be valid*
50 /// after the current enclosing `ExprKind::Scope` has ended, so
51 /// please do *not* return it from functions to avoid bad
52 /// miscompiles.
53 ///
54 /// # Parameters of unsized types
55 ///
56 /// We tweak the handling of parameters of unsized type slightly to avoid the need to create a
57 /// local variable of unsized type. For example, consider this program:
58 ///
59 /// ```
60 /// #![feature(unsized_fn_params)]
61 /// # use core::fmt::Debug;
62 /// fn foo(_p: dyn Debug) {
63 /// /* ... */
64 /// }
65 ///
66 /// fn bar(box_p: Box<dyn Debug>) { foo(*box_p); }
67 /// ```
68 ///
69 /// Ordinarily, for sized types, we would compile the call `foo(*p)` like so:
70 ///
71 /// ```ignore (illustrative)
72 /// let tmp0 = *box_p; // tmp0 would be the operand returned by this function call
73 /// foo(tmp0)
74 /// ```
75 ///
76 /// But because the parameter to `foo` is of the unsized type `dyn Debug`, and because it is
77 /// being moved the deref of a box, we compile it slightly differently. The temporary `tmp0`
78 /// that we create *stores the entire box*, and the parameter to the call itself will be
79 /// `*tmp0`:
80 ///
81 /// ```ignore (illustrative)
82 /// let tmp0 = box_p; call foo(*tmp0)
83 /// ```
84 ///
85 /// This way, the temporary `tmp0` that we create has type `Box<dyn Debug>`, which is sized.
86 /// The value passed to the call (`*tmp0`) still has the `dyn Debug` type -- but the way that
87 /// calls are compiled means that this parameter will be passed "by reference", meaning that we
88 /// will actually provide a pointer to the interior of the box, and not move the `dyn Debug`
89 /// value to the stack.
90 ///
91 /// See <https://github.com/rust-lang/rust/issues/68304> for more details.
92pub(crate) fn as_local_call_operand(
93&mut self,
94 block: BasicBlock,
95 expr: ExprId,
96 ) -> BlockAnd<Operand<'tcx>> {
97self.as_call_operand(block, self.local_temp_lifetime(), expr)
98 }
99100/// Compile `expr` into a value that can be used as an operand.
101 /// If `expr` is a place like `x`, this will introduce a
102 /// temporary `tmp = x`, so that we capture the value of `x` at
103 /// this time.
104 ///
105 /// If we end up needing to create a temporary, then we will use
106 /// `local_info` as its `LocalInfo`, unless `as_temporary`
107 /// has already assigned it a non-`None` `LocalInfo`.
108 /// Normally, you should use `None` for `local_info`
109 ///
110 /// The operand is known to be live until the end of `scope`.
111 ///
112 /// Like `as_local_call_operand`, except that the argument will
113 /// not be valid once `scope` ends.
114#[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_operand",
"rustc_mir_build::builder::expr::as_operand",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/as_operand.rs"),
::tracing_core::__macro_support::Option::Some(114u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::as_operand"),
::tracing_core::field::FieldSet::new(&["block", "expr_id",
"local_info", "needs_temporary"],
::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(&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(&local_info)
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(&needs_temporary)
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<Operand<'tcx>> = loop {};
return __tracing_attr_fake_return;
}
{
let this = self;
let expr = &this.thir[expr_id];
if let ExprKind::Scope { region_scope, hir_id, value } = expr.kind
{
let source_info = this.source_info(expr.span);
let region_scope = (region_scope, source_info);
return this.in_scope(region_scope,
LintLevel::Explicit(hir_id),
|this|
{
this.as_operand(block, scope, value, local_info,
needs_temporary)
});
}
let category = Category::of(&expr.kind).unwrap();
{
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_operand.rs:135",
"rustc_mir_build::builder::expr::as_operand",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/as_operand.rs"),
::tracing_core::__macro_support::Option::Some(135u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::as_operand"),
::tracing_core::field::FieldSet::new(&["category",
"expr.kind"],
::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(&category)
as &dyn Value)),
(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
::tracing::__macro_support::Option::Some(&debug(&expr.kind)
as &dyn Value))])
});
} else { ; }
};
match category {
Category::Constant if
#[allow(non_exhaustive_omitted_patterns)] match needs_temporary
{
NeedsTemporary::No => true,
_ => false,
} || !expr.ty.needs_drop(this.tcx, this.typing_env()) => {
let constant = this.as_constant(expr);
block.and(Operand::Constant(Box::new(constant)))
}
Category::Constant | Category::Place | Category::Rvalue(..) =>
{
let operand =
{
let BlockAnd(b, v) =
this.as_temp(block, scope, expr_id, Mutability::Mut);
block = b;
v
};
if !#[allow(non_exhaustive_omitted_patterns)] match local_info
{
LocalInfo::Boring => true,
_ => false,
} {
let decl_info =
this.local_decls[operand].local_info.as_mut().unwrap_crate_local();
if let LocalInfo::Boring | LocalInfo::BlockTailTemp(_) =
**decl_info {
**decl_info = local_info;
}
}
block.and(Operand::Move(Place::from(operand)))
}
}
}
}
}#[instrument(level = "debug", skip(self, scope))]115pub(crate) fn as_operand(
116&mut self,
117mut block: BasicBlock,
118 scope: TempLifetime,
119 expr_id: ExprId,
120 local_info: LocalInfo<'tcx>,
121 needs_temporary: NeedsTemporary,
122 ) -> BlockAnd<Operand<'tcx>> {
123let this = self; // See "LET_THIS_SELF".
124125let expr = &this.thir[expr_id];
126if let ExprKind::Scope { region_scope, hir_id, value } = expr.kind {
127let source_info = this.source_info(expr.span);
128let region_scope = (region_scope, source_info);
129return this.in_scope(region_scope, LintLevel::Explicit(hir_id), |this| {
130 this.as_operand(block, scope, value, local_info, needs_temporary)
131 });
132 }
133134let category = Category::of(&expr.kind).unwrap();
135debug!(?category, ?expr.kind);
136match category {
137 Category::Constant
138if matches!(needs_temporary, NeedsTemporary::No)
139 || !expr.ty.needs_drop(this.tcx, this.typing_env()) =>
140 {
141let constant = this.as_constant(expr);
142 block.and(Operand::Constant(Box::new(constant)))
143 }
144 Category::Constant | Category::Place | Category::Rvalue(..) => {
145let operand = unpack!(block = this.as_temp(block, scope, expr_id, Mutability::Mut));
146// Overwrite temp local info if we have something more interesting to record.
147if !matches!(local_info, LocalInfo::Boring) {
148let decl_info =
149 this.local_decls[operand].local_info.as_mut().unwrap_crate_local();
150if let LocalInfo::Boring | LocalInfo::BlockTailTemp(_) = **decl_info {
151**decl_info = local_info;
152 }
153 }
154 block.and(Operand::Move(Place::from(operand)))
155 }
156 }
157 }
158159pub(crate) fn as_call_operand(
160&mut self,
161mut block: BasicBlock,
162 scope: TempLifetime,
163 expr_id: ExprId,
164 ) -> BlockAnd<Operand<'tcx>> {
165let this = self; // See "LET_THIS_SELF".
166let expr = &this.thir[expr_id];
167{
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_operand.rs:167",
"rustc_mir_build::builder::expr::as_operand",
::tracing::Level::DEBUG,
::tracing_core::__macro_support::Option::Some("compiler/rustc_mir_build/src/builder/expr/as_operand.rs"),
::tracing_core::__macro_support::Option::Some(167u32),
::tracing_core::__macro_support::Option::Some("rustc_mir_build::builder::expr::as_operand"),
::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!("as_call_operand(block={0:?}, expr={1:?})",
block, expr) as &dyn Value))])
});
} else { ; }
};debug!("as_call_operand(block={:?}, expr={:?})", block, expr);
168169if let ExprKind::Scope { region_scope, hir_id, value } = expr.kind {
170let source_info = this.source_info(expr.span);
171let region_scope = (region_scope, source_info);
172return this.in_scope(region_scope, LintLevel::Explicit(hir_id), |this| {
173this.as_call_operand(block, scope, value)
174 });
175 }
176177let tcx = this.tcx;
178179if tcx.features().unsized_fn_params() {
180let ty = expr.ty;
181if !ty.is_sized(tcx, this.typing_env()) {
182// !sized means !copy, so this is an unsized move
183if !!tcx.type_is_copy_modulo_regions(this.typing_env(), ty) {
::core::panicking::panic("assertion failed: !tcx.type_is_copy_modulo_regions(this.typing_env(), ty)")
};assert!(!tcx.type_is_copy_modulo_regions(this.typing_env(), ty));
184185// As described above, detect the case where we are passing a value of unsized
186 // type, and that value is coming from the deref of a box.
187if let ExprKind::Deref { arg } = expr.kind {
188// Generate let tmp0 = arg0
189let operand = {
let BlockAnd(b, v) = this.as_temp(block, scope, arg, Mutability::Mut);
block = b;
v
}unpack!(block = this.as_temp(block, scope, arg, Mutability::Mut));
190191// Return the operand *tmp0 to be used as the call argument
192let place = Place {
193 local: operand,
194 projection: tcx.mk_place_elems(&[PlaceElem::Deref]),
195 };
196197return block.and(Operand::Move(place));
198 }
199 }
200 }
201202this.as_operand(block, scope, expr_id, LocalInfo::Boring, NeedsTemporary::Maybe)
203 }
204}