Auto merge of #119122 - matthewjasper:if-let-guard-scoping, r=TaKO8Ki

Give temporaries in if let guards correct scopes

Temporaries in if-let guards have scopes that escape the match arm, this causes problems because the drops might be for temporaries that are not storage live. This PR changes the scope of temporaries in if-let guards to be limited to the arm:

```rust
_ if let Some(s) = std::convert::identity(&Some(String::new())) => {}
//                Temporary for Some(String::new()) is dropped here ^
```

We also now deduplicate temporaries between copies of the guard created for or-patterns:

```rust
// Only create a single Some(String::new()) temporary variable
_ | _ if let Some(s) = std::convert::identity(&Some(String::new())) => {}
```

This changes MIR building to pass around `ExprId`s rather than `Expr`s so that we have a way to index different expressions.

cc #51114
Closes #116079
This commit is contained in:
bors 2023-12-25 04:06:58 +00:00
commit f2348fb29a
17 changed files with 387 additions and 245 deletions

View File

@ -179,10 +179,10 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) {
let prev_cx = visitor.cx;
visitor.enter_scope(Scope { id: arm.hir_id.local_id, data: ScopeData::Node });
visitor.cx.var_parent = visitor.cx.parent;
visitor.terminating_scopes.insert(arm.hir_id.local_id);
visitor.terminating_scopes.insert(arm.body.hir_id.local_id);
visitor.enter_node_scope_with_dtor(arm.hir_id.local_id);
visitor.cx.var_parent = visitor.cx.parent;
if let Some(hir::Guard::If(expr)) = arm.guard {
visitor.terminating_scopes.insert(expr.hir_id.local_id);

View File

@ -15,7 +15,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
) -> BlockAnd<()> {
let Block { region_scope, span, ref stmts, expr, targeted_by_break, safety_mode } =
self.thir[ast_block];
let expr = expr.map(|expr| &self.thir[expr]);
self.in_scope((region_scope, source_info), LintLevel::Inherited, move |this| {
if targeted_by_break {
this.in_breakable_scope(None, destination, span, |this| {
@ -49,7 +48,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
mut block: BasicBlock,
span: Span,
stmts: &[StmtId],
expr: Option<&Expr<'tcx>>,
expr: Option<ExprId>,
safety_mode: BlockSafety,
region_scope: Scope,
) -> BlockAnd<()> {
@ -90,7 +89,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let si = (*scope, source_info);
unpack!(
block = this.in_scope(si, LintLevel::Inherited, |this| {
this.stmt_expr(block, &this.thir[*expr], Some(*scope))
this.stmt_expr(block, *expr, Some(*scope))
})
);
}
@ -205,8 +204,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let visibility_scope =
Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
let init = &this.thir[*initializer];
let initializer_span = init.span;
let initializer_span = this.thir[*initializer].span;
let scope = (*init_scope, source_info);
let failure = unpack!(
block = this.in_scope(scope, *lint_level, |this| {
@ -232,7 +230,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
);
this.ast_let_else(
block,
init,
*initializer,
initializer_span,
*else_block,
&last_remainder_scope,
@ -276,9 +274,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Some(this.new_source_scope(remainder_span, LintLevel::Inherited, None));
// Evaluate the initializer, if present.
if let Some(init) = initializer {
let init = &this.thir[*init];
let initializer_span = init.span;
if let Some(init) = *initializer {
let initializer_span = this.thir[init].span;
let scope = (*init_scope, source_info);
unpack!(
@ -334,13 +331,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// of the block, which is stored into `destination`.
let tcx = this.tcx;
let destination_ty = destination.ty(&this.local_decls, tcx).ty;
if let Some(expr) = expr {
if let Some(expr_id) = expr {
let expr = &this.thir[expr_id];
let tail_result_is_ignored =
destination_ty.is_unit() || this.block_context.currently_ignores_tail_results();
this.block_context
.push(BlockFrame::TailExpr { tail_result_is_ignored, span: expr.span });
unpack!(block = this.expr_into_dest(destination, block, expr));
unpack!(block = this.expr_into_dest(destination, block, expr_id));
let popped = this.block_context.pop();
assert!(popped.is_some_and(|bf| bf.is_tail_expr()));

View File

@ -17,10 +17,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn as_local_operand(
&mut self,
block: BasicBlock,
expr: &Expr<'tcx>,
expr_id: ExprId,
) -> BlockAnd<Operand<'tcx>> {
let local_scope = self.local_scope();
self.as_operand(block, Some(local_scope), expr, LocalInfo::Boring, NeedsTemporary::Maybe)
self.as_operand(block, Some(local_scope), expr_id, LocalInfo::Boring, NeedsTemporary::Maybe)
}
/// Returns an operand suitable for use until the end of the current scope expression and
@ -76,7 +76,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn as_local_call_operand(
&mut self,
block: BasicBlock,
expr: &Expr<'tcx>,
expr: ExprId,
) -> BlockAnd<Operand<'tcx>> {
let local_scope = self.local_scope();
self.as_call_operand(block, Some(local_scope), expr)
@ -101,17 +101,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut self,
mut block: BasicBlock,
scope: Option<region::Scope>,
expr: &Expr<'tcx>,
expr_id: ExprId,
local_info: LocalInfo<'tcx>,
needs_temporary: NeedsTemporary,
) -> BlockAnd<Operand<'tcx>> {
let this = self;
let expr = &this.thir[expr_id];
if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
let source_info = this.source_info(expr.span);
let region_scope = (region_scope, source_info);
return this.in_scope(region_scope, lint_level, |this| {
this.as_operand(block, scope, &this.thir[value], local_info, needs_temporary)
this.as_operand(block, scope, value, local_info, needs_temporary)
});
}
@ -126,7 +127,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.and(Operand::Constant(Box::new(constant)))
}
Category::Constant | Category::Place | Category::Rvalue(..) => {
let operand = unpack!(block = this.as_temp(block, scope, expr, Mutability::Mut));
let operand = unpack!(block = this.as_temp(block, scope, expr_id, Mutability::Mut));
// Overwrite temp local info if we have something more interesting to record.
if !matches!(local_info, LocalInfo::Boring) {
let decl_info =
@ -144,16 +145,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut self,
mut block: BasicBlock,
scope: Option<region::Scope>,
expr: &Expr<'tcx>,
expr_id: ExprId,
) -> BlockAnd<Operand<'tcx>> {
debug!("as_call_operand(block={:?}, expr={:?})", block, expr);
let this = self;
let expr = &this.thir[expr_id];
debug!("as_call_operand(block={:?}, expr={:?})", block, expr);
if let ExprKind::Scope { region_scope, lint_level, value } = expr.kind {
let source_info = this.source_info(expr.span);
let region_scope = (region_scope, source_info);
return this.in_scope(region_scope, lint_level, |this| {
this.as_call_operand(block, scope, &this.thir[value])
this.as_call_operand(block, scope, value)
});
}
@ -171,9 +173,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// type, and that value is coming from the deref of a box.
if let ExprKind::Deref { arg } = expr.kind {
// Generate let tmp0 = arg0
let operand = unpack!(
block = this.as_temp(block, scope, &this.thir[arg], Mutability::Mut)
);
let operand = unpack!(block = this.as_temp(block, scope, arg, Mutability::Mut));
// Return the operand *tmp0 to be used as the call argument
let place = Place {
@ -186,6 +186,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
this.as_operand(block, scope, expr, LocalInfo::Boring, NeedsTemporary::Maybe)
this.as_operand(block, scope, expr_id, LocalInfo::Boring, NeedsTemporary::Maybe)
}
}

View File

@ -354,9 +354,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn as_place(
&mut self,
mut block: BasicBlock,
expr: &Expr<'tcx>,
expr_id: ExprId,
) -> BlockAnd<Place<'tcx>> {
let place_builder = unpack!(block = self.as_place_builder(block, expr));
let place_builder = unpack!(block = self.as_place_builder(block, expr_id));
block.and(place_builder.to_place(self))
}
@ -365,9 +365,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn as_place_builder(
&mut self,
block: BasicBlock,
expr: &Expr<'tcx>,
expr_id: ExprId,
) -> BlockAnd<PlaceBuilder<'tcx>> {
self.expr_as_place(block, expr, Mutability::Mut, None)
self.expr_as_place(block, expr_id, Mutability::Mut, None)
}
/// Compile `expr`, yielding a place that we can move from etc.
@ -378,9 +378,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn as_read_only_place(
&mut self,
mut block: BasicBlock,
expr: &Expr<'tcx>,
expr_id: ExprId,
) -> BlockAnd<Place<'tcx>> {
let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr_id));
block.and(place_builder.to_place(self))
}
@ -393,18 +393,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn as_read_only_place_builder(
&mut self,
block: BasicBlock,
expr: &Expr<'tcx>,
expr_id: ExprId,
) -> BlockAnd<PlaceBuilder<'tcx>> {
self.expr_as_place(block, expr, Mutability::Not, None)
self.expr_as_place(block, expr_id, Mutability::Not, None)
}
fn expr_as_place(
&mut self,
mut block: BasicBlock,
expr: &Expr<'tcx>,
expr_id: ExprId,
mutability: Mutability,
fake_borrow_temps: Option<&mut Vec<Local>>,
) -> BlockAnd<PlaceBuilder<'tcx>> {
let expr = &self.thir[expr_id];
debug!("expr_as_place(block={:?}, expr={:?}, mutability={:?})", block, expr, mutability);
let this = self;
@ -413,14 +414,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match expr.kind {
ExprKind::Scope { region_scope, lint_level, value } => {
this.in_scope((region_scope, source_info), lint_level, |this| {
this.expr_as_place(block, &this.thir[value], mutability, fake_borrow_temps)
this.expr_as_place(block, value, mutability, fake_borrow_temps)
})
}
ExprKind::Field { lhs, variant_index, name } => {
let lhs = &this.thir[lhs];
let lhs_expr = &this.thir[lhs];
let mut place_builder =
unpack!(block = this.expr_as_place(block, lhs, mutability, fake_borrow_temps,));
if let ty::Adt(adt_def, _) = lhs.ty.kind() {
if let ty::Adt(adt_def, _) = lhs_expr.ty.kind() {
if adt_def.is_enum() {
place_builder = place_builder.downcast(*adt_def, variant_index);
}
@ -428,16 +429,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.and(place_builder.field(name, expr.ty))
}
ExprKind::Deref { arg } => {
let place_builder = unpack!(
block =
this.expr_as_place(block, &this.thir[arg], mutability, fake_borrow_temps,)
);
let place_builder =
unpack!(block = this.expr_as_place(block, arg, mutability, fake_borrow_temps,));
block.and(place_builder.deref())
}
ExprKind::Index { lhs, index } => this.lower_index_expression(
block,
&this.thir[lhs],
&this.thir[index],
lhs,
index,
mutability,
fake_borrow_temps,
expr.temp_lifetime,
@ -461,12 +460,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ExprKind::PlaceTypeAscription { source, ref user_ty } => {
let place_builder = unpack!(
block = this.expr_as_place(
block,
&this.thir[source],
mutability,
fake_borrow_temps,
)
block = this.expr_as_place(block, source, mutability, fake_borrow_temps,)
);
if let Some(user_ty) = user_ty {
let annotation_index =
@ -494,9 +488,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.and(place_builder)
}
ExprKind::ValueTypeAscription { source, ref user_ty } => {
let source = &this.thir[source];
let temp =
unpack!(block = this.as_temp(block, source.temp_lifetime, source, mutability));
let source_expr = &this.thir[source];
let temp = unpack!(
block = this.as_temp(block, source_expr.temp_lifetime, source, mutability)
);
if let Some(user_ty) = user_ty {
let annotation_index =
this.canonical_user_type_annotations.push(CanonicalUserTypeAnnotation {
@ -562,7 +557,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// these are not places, so we need to make a temporary.
debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place)));
let temp =
unpack!(block = this.as_temp(block, expr.temp_lifetime, expr, mutability));
unpack!(block = this.as_temp(block, expr.temp_lifetime, expr_id, mutability));
block.and(PlaceBuilder::from(temp))
}
}
@ -591,8 +586,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn lower_index_expression(
&mut self,
mut block: BasicBlock,
base: &Expr<'tcx>,
index: &Expr<'tcx>,
base: ExprId,
index: ExprId,
mutability: Mutability,
fake_borrow_temps: Option<&mut Vec<Local>>,
temp_lifetime: Option<region::Scope>,
@ -609,7 +604,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Making this a *fresh* temporary means we do not have to worry about
// the index changing later: Nothing will ever change this temporary.
// The "retagging" transformation (for Stacked Borrows) relies on this.
let idx = unpack!(block = self.as_temp(block, temp_lifetime, index, Mutability::Not,));
let idx = unpack!(block = self.as_temp(block, temp_lifetime, index, Mutability::Not));
block = self.bounds_check(block, &base_place, idx, expr_span, source_info);

View File

@ -27,10 +27,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn as_local_rvalue(
&mut self,
block: BasicBlock,
expr: &Expr<'tcx>,
expr_id: ExprId,
) -> BlockAnd<Rvalue<'tcx>> {
let local_scope = self.local_scope();
self.as_rvalue(block, Some(local_scope), expr)
self.as_rvalue(block, Some(local_scope), expr_id)
}
/// Compile `expr`, yielding an rvalue.
@ -38,11 +38,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut self,
mut block: BasicBlock,
scope: Option<region::Scope>,
expr: &Expr<'tcx>,
expr_id: ExprId,
) -> BlockAnd<Rvalue<'tcx>> {
let this = self;
let expr = &this.thir[expr_id];
debug!("expr_as_rvalue(block={:?}, scope={:?}, expr={:?})", block, scope, expr);
let this = self;
let expr_span = expr.span;
let source_info = this.source_info(expr_span);
@ -50,9 +51,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ExprKind::ThreadLocalRef(did) => block.and(Rvalue::ThreadLocalRef(did)),
ExprKind::Scope { region_scope, lint_level, value } => {
let region_scope = (region_scope, source_info);
this.in_scope(region_scope, lint_level, |this| {
this.as_rvalue(block, scope, &this.thir[value])
})
this.in_scope(region_scope, lint_level, |this| this.as_rvalue(block, scope, value))
}
ExprKind::Repeat { value, count } => {
if Some(0) == count.try_eval_target_usize(this.tcx, this.param_env) {
@ -62,7 +61,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block = this.as_operand(
block,
scope,
&this.thir[value],
value,
LocalInfo::Boring,
NeedsTemporary::No
)
@ -75,31 +74,21 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block = this.as_operand(
block,
scope,
&this.thir[lhs],
lhs,
LocalInfo::Boring,
NeedsTemporary::Maybe
)
);
let rhs = unpack!(
block = this.as_operand(
block,
scope,
&this.thir[rhs],
LocalInfo::Boring,
NeedsTemporary::No
)
block =
this.as_operand(block, scope, rhs, LocalInfo::Boring, NeedsTemporary::No)
);
this.build_binary_op(block, op, expr_span, expr.ty, lhs, rhs)
}
ExprKind::Unary { op, arg } => {
let arg = unpack!(
block = this.as_operand(
block,
scope,
&this.thir[arg],
LocalInfo::Boring,
NeedsTemporary::No
)
block =
this.as_operand(block, scope, arg, LocalInfo::Boring, NeedsTemporary::No)
);
// Check for -MIN on signed integers
if this.check_overflow && op == UnOp::Neg && expr.ty.is_signed() {
@ -126,7 +115,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.and(Rvalue::UnaryOp(op, arg))
}
ExprKind::Box { value } => {
let value = &this.thir[value];
let value_ty = this.thir[value].ty;
let tcx = this.tcx;
// `exchange_malloc` is unsafe but box is safe, so need a new scope.
@ -142,7 +131,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block,
synth_info,
size,
Rvalue::NullaryOp(NullOp::SizeOf, value.ty),
Rvalue::NullaryOp(NullOp::SizeOf, value_ty),
);
let align = this.temp(tcx.types.usize, expr_span);
@ -150,7 +139,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block,
synth_info,
align,
Rvalue::NullaryOp(NullOp::AlignOf, value.ty),
Rvalue::NullaryOp(NullOp::AlignOf, value_ty),
);
// malloc some memory of suitable size and align:
@ -192,7 +181,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
// Transmute `*mut u8` to the box (thus far, uninitialized):
let box_ = Rvalue::ShallowInitBox(Operand::Move(storage), value.ty);
let box_ = Rvalue::ShallowInitBox(Operand::Move(storage), value_ty);
this.cfg.push_assign(block, source_info, Place::from(result), box_);
// initialize the box contents:
@ -200,24 +189,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block = this.expr_into_dest(
this.tcx.mk_place_deref(Place::from(result)),
block,
value
value,
)
);
block.and(Rvalue::Use(Operand::Move(Place::from(result))))
}
ExprKind::Cast { source } => {
let source = &this.thir[source];
let source_expr = &this.thir[source];
// Casting an enum to an integer is equivalent to computing the discriminant and casting the
// discriminant. Previously every backend had to repeat the logic for this operation. Now we
// create all the steps directly in MIR with operations all backends need to support anyway.
let (source, ty) = if let ty::Adt(adt_def, ..) = source.ty.kind()
let (source, ty) = if let ty::Adt(adt_def, ..) = source_expr.ty.kind()
&& adt_def.is_enum()
{
let discr_ty = adt_def.repr().discr_type().to_ty(this.tcx);
let temp = unpack!(block = this.as_temp(block, scope, source, Mutability::Not));
let layout = this.tcx.layout_of(this.param_env.and(source.ty));
let discr = this.temp(discr_ty, source.span);
let layout = this.tcx.layout_of(this.param_env.and(source_expr.ty));
let discr = this.temp(discr_ty, source_expr.span);
this.cfg.push_assign(
block,
source_info,
@ -296,7 +285,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
(op, ty)
} else {
let ty = source.ty;
let ty = source_expr.ty;
let source = unpack!(
block = this.as_operand(
block,
@ -310,7 +299,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
};
let from_ty = CastTy::from_ty(ty);
let cast_ty = CastTy::from_ty(expr.ty);
debug!("ExprKind::Cast from_ty={from_ty:?}, cast_ty={:?}/{cast_ty:?}", expr.ty,);
debug!("ExprKind::Cast from_ty={from_ty:?}, cast_ty={:?}/{cast_ty:?}", expr.ty);
let cast_kind = mir_cast_kind(ty, expr.ty);
block.and(Rvalue::Cast(cast_kind, source, expr.ty))
}
@ -319,7 +308,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block = this.as_operand(
block,
scope,
&this.thir[source],
source,
LocalInfo::Boring,
NeedsTemporary::No
)
@ -363,7 +352,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block = this.as_operand(
block,
scope,
&this.thir[f],
f,
LocalInfo::Boring,
NeedsTemporary::Maybe
)
@ -384,7 +373,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block = this.as_operand(
block,
scope,
&this.thir[f],
f,
LocalInfo::Boring,
NeedsTemporary::Maybe
)
@ -416,8 +405,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// ```
//
for (thir_place, cause, hir_id) in fake_reads.into_iter() {
let place_builder =
unpack!(block = this.as_place_builder(block, &this.thir[*thir_place]));
let place_builder = unpack!(block = this.as_place_builder(block, *thir_place));
if let Some(mir_place) = place_builder.try_to_place(this) {
this.cfg.push_fake_read(
@ -434,8 +422,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.into_iter()
.copied()
.map(|upvar| {
let upvar = &this.thir[upvar];
match Category::of(&upvar.kind) {
let upvar_expr = &this.thir[upvar];
match Category::of(&upvar_expr.kind) {
// Use as_place to avoid creating a temporary when
// moving a variable into a closure, so that
// borrowck knows which variables to mark as being
@ -453,18 +441,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// borrow captures when capturing an immutable
// variable. This is sound because the mutation
// that caused the capture will cause an error.
match upvar.kind {
match upvar_expr.kind {
ExprKind::Borrow {
borrow_kind:
BorrowKind::Mut { kind: MutBorrowKind::Default },
arg,
} => unpack!(
block = this.limit_capture_mutability(
upvar.span,
upvar.ty,
upvar_expr.span,
upvar_expr.ty,
scope,
block,
&this.thir[arg],
arg,
)
),
_ => {
@ -498,7 +486,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.and(Rvalue::Aggregate(result, operands))
}
ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
block = unpack!(this.stmt_expr(block, expr, None));
block = unpack!(this.stmt_expr(block, expr_id, None));
block.and(Rvalue::Use(Operand::Constant(Box::new(ConstOperand {
span: expr_span,
user_ty: None,
@ -553,8 +541,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Some(Category::Rvalue(RvalueFunc::AsRvalue) | Category::Constant)
));
let operand = unpack!(
block =
this.as_operand(block, scope, expr, LocalInfo::Boring, NeedsTemporary::No)
block = this.as_operand(
block,
scope,
expr_id,
LocalInfo::Boring,
NeedsTemporary::No,
)
);
block.and(Rvalue::Use(operand))
}
@ -719,9 +712,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
outer_source_info: SourceInfo,
) -> BlockAnd<Rvalue<'tcx>> {
let this = self;
let value = &this.thir[value];
let elem_ty = value.ty;
if let Some(Category::Constant) = Category::of(&value.kind) {
let value_expr = &this.thir[value];
let elem_ty = value_expr.ty;
if let Some(Category::Constant) = Category::of(&value_expr.kind) {
// Repeating a const does nothing
} else {
// For a non-const, we may need to generate an appropriate `Drop`
@ -754,7 +747,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
upvar_ty: Ty<'tcx>,
temp_lifetime: Option<region::Scope>,
mut block: BasicBlock,
arg: &Expr<'tcx>,
arg: ExprId,
) -> BlockAnd<Operand<'tcx>> {
let this = self;

View File

@ -14,13 +14,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut self,
block: BasicBlock,
temp_lifetime: Option<region::Scope>,
expr: &Expr<'tcx>,
expr_id: ExprId,
mutability: Mutability,
) -> BlockAnd<Local> {
// 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))
ensure_sufficient_stack(|| self.as_temp_inner(block, temp_lifetime, expr_id, mutability))
}
#[instrument(skip(self), level = "debug")]
@ -28,21 +28,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut self,
mut block: BasicBlock,
temp_lifetime: Option<region::Scope>,
expr: &Expr<'tcx>,
expr_id: ExprId,
mutability: Mutability,
) -> BlockAnd<Local> {
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, 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)
this.as_temp(block, temp_lifetime, value, mutability)
});
}
let expr_ty = expr.ty;
let temp = {
let deduplicate_temps =
this.fixed_temps_scope.is_some() && this.fixed_temps_scope == 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();
@ -71,6 +76,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
**local_decl.local_info.as_mut().assert_crate_local() = local_info;
this.local_decls.push(local_decl)
};
if deduplicate_temps {
this.fixed_temps.insert(expr_id, temp);
}
let temp_place = Place::from(temp);
match expr.kind {
@ -103,7 +111,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
}
unpack!(block = this.expr_into_dest(temp_place, block, expr));
unpack!(block = this.expr_into_dest(temp_place, block, expr_id));
if let Some(temp_lifetime) = temp_lifetime {
this.schedule_drop(expr_span, temp_lifetime, temp, DropKind::Value);

View File

@ -19,12 +19,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut self,
destination: Place<'tcx>,
mut block: BasicBlock,
expr: &Expr<'tcx>,
expr_id: ExprId,
) -> BlockAnd<()> {
// since we frequently have to reference `self` from within a
// closure, where `self` would be shadowed, it's easier to
// just use the name `this` uniformly
let this = self;
let expr = &this.thir[expr_id];
let expr_span = expr.span;
let source_info = this.source_info(expr_span);
@ -40,20 +41,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let region_scope = (region_scope, source_info);
ensure_sufficient_stack(|| {
this.in_scope(region_scope, lint_level, |this| {
this.expr_into_dest(destination, block, &this.thir[value])
this.expr_into_dest(destination, block, value)
})
})
}
ExprKind::Block { block: ast_block } => {
this.ast_block(destination, block, ast_block, source_info)
}
ExprKind::Match { scrutinee, ref arms, .. } => {
this.match_expr(destination, expr_span, block, &this.thir[scrutinee], arms)
}
ExprKind::Match { scrutinee, ref arms, .. } => this.match_expr(
destination,
block,
scrutinee,
arms,
expr_span,
this.thir[scrutinee].span,
),
ExprKind::If { cond, then, else_opt, if_then_scope } => {
let then_blk;
let then_expr = &this.thir[then];
let then_source_info = this.source_info(then_expr.span);
let then_span = this.thir[then].span;
let then_source_info = this.source_info(then_span);
let condition_scope = this.local_scope();
let mut else_blk = unpack!(
@ -62,27 +68,24 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
LintLevel::Inherited,
|this| {
let source_info = if this.is_let(cond) {
let variable_scope = this.new_source_scope(
then_expr.span,
LintLevel::Inherited,
None,
);
let variable_scope =
this.new_source_scope(then_span, LintLevel::Inherited, None);
this.source_scope = variable_scope;
SourceInfo { span: then_expr.span, scope: variable_scope }
SourceInfo { span: then_span, scope: variable_scope }
} else {
this.source_info(then_expr.span)
this.source_info(then_span)
};
let (then_block, else_block) =
this.in_if_then_scope(condition_scope, then_expr.span, |this| {
this.in_if_then_scope(condition_scope, then_span, |this| {
let then_blk = unpack!(this.then_else_break(
block,
&this.thir[cond],
cond,
Some(condition_scope),
condition_scope,
source_info
));
this.expr_into_dest(destination, then_blk, then_expr)
this.expr_into_dest(destination, then_blk, then)
});
then_block.and(else_block)
},
@ -90,7 +93,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
);
else_blk = if let Some(else_opt) = else_opt {
unpack!(this.expr_into_dest(destination, else_blk, &this.thir[else_opt]))
unpack!(this.expr_into_dest(destination, else_blk, else_opt))
} else {
// Body of the `if` expression without an `else` clause must return `()`, thus
// we implicitly generate an `else {}` if it is not specified.
@ -107,7 +110,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ExprKind::Let { expr, ref pat } => {
let scope = this.local_scope();
let (true_block, false_block) = this.in_if_then_scope(scope, expr_span, |this| {
this.lower_let_expr(block, &this.thir[expr], pat, scope, None, expr_span, true)
this.lower_let_expr(block, expr, pat, scope, None, expr_span, true)
});
this.cfg.push_assign_constant(
@ -138,14 +141,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
join_block.unit()
}
ExprKind::NeverToAny { source } => {
let source = &this.thir[source];
let source_expr = &this.thir[source];
let is_call =
matches!(source.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. });
matches!(source_expr.kind, ExprKind::Call { .. } | ExprKind::InlineAsm { .. });
// (#66975) Source could be a const of type `!`, so has to
// exist in the generated MIR.
unpack!(
block = this.as_temp(block, Some(this.local_scope()), source, Mutability::Mut,)
block = this.as_temp(block, Some(this.local_scope()), source, Mutability::Mut)
);
// This is an optimization. If the expression was a call then we already have an
@ -166,7 +169,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.in_if_then_scope(condition_scope, expr.span, |this| {
this.then_else_break(
block,
&this.thir[lhs],
lhs,
Some(condition_scope),
condition_scope,
source_info,
@ -192,7 +195,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
const_: Const::from_bool(this.tcx, constant),
},
);
let rhs = unpack!(this.expr_into_dest(destination, continuation, &this.thir[rhs]));
let rhs = unpack!(this.expr_into_dest(destination, continuation, rhs));
let target = this.cfg.start_new_block();
this.cfg.goto(rhs, source_info, target);
this.cfg.goto(short_circuit, source_info, target);
@ -231,8 +234,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// introduce a unit temporary as the destination for the loop body.
let tmp = this.get_unit_temp();
// Execute the body, branching back to the test.
let body_block_end =
unpack!(this.expr_into_dest(tmp, body_block, &this.thir[body]));
let body_block_end = unpack!(this.expr_into_dest(tmp, body_block, body));
this.cfg.goto(body_block_end, source_info, loop_block);
// Loops are only exited by `break` expressions.
@ -240,11 +242,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
})
}
ExprKind::Call { ty: _, fun, ref args, from_hir_call, fn_span } => {
let fun = unpack!(block = this.as_local_operand(block, &this.thir[fun]));
let fun = unpack!(block = this.as_local_operand(block, fun));
let args: Vec<_> = args
.into_iter()
.copied()
.map(|arg| unpack!(block = this.as_local_call_operand(block, &this.thir[arg])))
.map(|arg| unpack!(block = this.as_local_call_operand(block, arg)))
.collect();
let success = this.cfg.start_new_block();
@ -280,16 +282,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.diverge_from(block);
success.unit()
}
ExprKind::Use { source } => this.expr_into_dest(destination, block, &this.thir[source]),
ExprKind::Use { source } => this.expr_into_dest(destination, block, source),
ExprKind::Borrow { arg, borrow_kind } => {
let arg = &this.thir[arg];
// We don't do this in `as_rvalue` because we use `as_place`
// for borrow expressions, so we cannot create an `RValue` that
// remains valid across user code. `as_rvalue` is usually called
// by this method anyway, so this shouldn't cause too many
// unnecessary temporaries.
let arg_place = match borrow_kind {
BorrowKind::Shared => unpack!(block = this.as_read_only_place(block, arg)),
BorrowKind::Shared => {
unpack!(block = this.as_read_only_place(block, arg))
}
_ => unpack!(block = this.as_place(block, arg)),
};
let borrow = Rvalue::Ref(this.tcx.lifetimes.re_erased, borrow_kind, arg_place);
@ -297,7 +300,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.unit()
}
ExprKind::AddressOf { mutability, arg } => {
let arg = &this.thir[arg];
let place = match mutability {
hir::Mutability::Not => this.as_read_only_place(block, arg),
hir::Mutability::Mut => this.as_place(block, arg),
@ -332,7 +334,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block = this.as_operand(
block,
Some(scope),
&this.thir[f.expr],
f.expr,
LocalInfo::AggregateTemp,
NeedsTemporary::Maybe,
)
@ -344,8 +346,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let field_names = adt_def.variant(variant_index).fields.indices();
let fields = if let Some(FruInfo { base, field_types }) = base {
let place_builder =
unpack!(block = this.as_place_builder(block, &this.thir[*base]));
let place_builder = unpack!(block = this.as_place_builder(block, *base));
// MIR does not natively support FRU, so for each
// base-supplied field, generate an operand that
@ -398,19 +399,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
.map(|op| match *op {
thir::InlineAsmOperand::In { reg, expr } => mir::InlineAsmOperand::In {
reg,
value: unpack!(block = this.as_local_operand(block, &this.thir[expr])),
value: unpack!(block = this.as_local_operand(block, expr)),
},
thir::InlineAsmOperand::Out { reg, late, expr } => {
mir::InlineAsmOperand::Out {
reg,
late,
place: expr.map(|expr| {
unpack!(block = this.as_place(block, &this.thir[expr]))
}),
place: expr.map(|expr| unpack!(block = this.as_place(block, expr))),
}
}
thir::InlineAsmOperand::InOut { reg, late, expr } => {
let place = unpack!(block = this.as_place(block, &this.thir[expr]));
let place = unpack!(block = this.as_place(block, expr));
mir::InlineAsmOperand::InOut {
reg,
late,
@ -423,11 +422,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
mir::InlineAsmOperand::InOut {
reg,
late,
in_value: unpack!(
block = this.as_local_operand(block, &this.thir[in_expr])
),
in_value: unpack!(block = this.as_local_operand(block, in_expr)),
out_place: out_expr.map(|out_expr| {
unpack!(block = this.as_place(block, &this.thir[out_expr]))
unpack!(block = this.as_place(block, out_expr))
}),
}
}
@ -488,7 +485,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// These cases don't actually need a destination
ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
unpack!(block = this.stmt_expr(block, expr, None));
unpack!(block = this.stmt_expr(block, expr_id, None));
this.cfg.push_assign_unit(block, source_info, destination, this.tcx);
block.unit()
}
@ -497,7 +494,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::Break { .. }
| ExprKind::Return { .. }
| ExprKind::Become { .. } => {
unpack!(block = this.stmt_expr(block, expr, None));
unpack!(block = this.stmt_expr(block, expr_id, None));
// No assign, as these have type `!`.
block.unit()
}
@ -509,7 +506,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::ValueTypeAscription { .. } => {
debug_assert!(Category::of(&expr.kind) == Some(Category::Place));
let place = unpack!(block = this.as_place(block, expr));
let place = unpack!(block = this.as_place(block, expr_id));
let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
this.cfg.push_assign(block, source_info, destination, rvalue);
block.unit()
@ -524,7 +521,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.local_decls.push(LocalDecl::new(expr.ty, expr.span));
}
let place = unpack!(block = this.as_place(block, expr));
let place = unpack!(block = this.as_place(block, expr_id));
let rvalue = Rvalue::Use(this.consume_by_copy_or_move(place));
this.cfg.push_assign(block, source_info, destination, rvalue);
block.unit()
@ -536,7 +533,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block = this.as_operand(
block,
Some(scope),
&this.thir[value],
value,
LocalInfo::Boring,
NeedsTemporary::No
)
@ -582,7 +579,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
_ => true,
});
let rvalue = unpack!(block = this.as_local_rvalue(block, expr));
let rvalue = unpack!(block = this.as_local_rvalue(block, expr_id));
this.cfg.push_assign(block, source_info, destination, rvalue);
block.unit()
}

View File

@ -11,10 +11,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn stmt_expr(
&mut self,
mut block: BasicBlock,
expr: &Expr<'tcx>,
expr_id: ExprId,
statement_scope: Option<region::Scope>,
) -> BlockAnd<()> {
let this = self;
let expr = &this.thir[expr_id];
let expr_span = expr.span;
let source_info = this.source_info(expr.span);
// Handle a number of expressions that don't need a destination at all. This
@ -22,13 +23,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
match expr.kind {
ExprKind::Scope { region_scope, lint_level, value } => {
this.in_scope((region_scope, source_info), lint_level, |this| {
this.stmt_expr(block, &this.thir[value], statement_scope)
this.stmt_expr(block, value, statement_scope)
})
}
ExprKind::Assign { lhs, rhs } => {
let lhs = &this.thir[lhs];
let rhs = &this.thir[rhs];
let lhs_span = lhs.span;
let lhs_expr = &this.thir[lhs];
// Note: we evaluate assignments right-to-left. This
// is better for borrowck interaction with overloaded
@ -39,10 +38,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Generate better code for things that don't need to be
// dropped.
if lhs.ty.needs_drop(this.tcx, this.param_env) {
if lhs_expr.ty.needs_drop(this.tcx, this.param_env) {
let rhs = unpack!(block = this.as_local_rvalue(block, rhs));
let lhs = unpack!(block = this.as_place(block, lhs));
unpack!(block = this.build_drop_and_replace(block, lhs_span, lhs, rhs));
unpack!(block = this.build_drop_and_replace(block, lhs_expr.span, lhs, rhs));
} else {
let rhs = unpack!(block = this.as_local_rvalue(block, rhs));
let lhs = unpack!(block = this.as_place(block, lhs));
@ -61,9 +60,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// only affects weird things like `x += {x += 1; x}`
// -- is that equal to `x + (x + 1)` or `2*(x+1)`?
let lhs = &this.thir[lhs];
let rhs = &this.thir[rhs];
let lhs_ty = lhs.ty;
let lhs_ty = this.thir[lhs].ty;
debug!("stmt_expr AssignOp block_context.push(SubExpr) : {:?}", expr);
this.block_context.push(BlockFrame::SubExpr);
@ -87,25 +84,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
ExprKind::Continue { label } => {
this.break_scope(block, None, BreakableTarget::Continue(label), source_info)
}
ExprKind::Break { label, value } => this.break_scope(
block,
value.map(|value| &this.thir[value]),
BreakableTarget::Break(label),
source_info,
),
ExprKind::Return { value } => this.break_scope(
block,
value.map(|value| &this.thir[value]),
BreakableTarget::Return,
source_info,
),
ExprKind::Break { label, value } => {
this.break_scope(block, value, BreakableTarget::Break(label), source_info)
}
ExprKind::Return { value } => {
this.break_scope(block, value, BreakableTarget::Return, source_info)
}
// FIXME(explicit_tail_calls): properly lower tail calls here
ExprKind::Become { value } => this.break_scope(
block,
Some(&this.thir[value]),
BreakableTarget::Return,
source_info,
),
ExprKind::Become { value } => {
this.break_scope(block, Some(value), BreakableTarget::Return, source_info)
}
_ => {
assert!(
statement_scope.is_some(),
@ -147,7 +135,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
};
let temp =
unpack!(block = this.as_temp(block, statement_scope, expr, Mutability::Not));
unpack!(block = this.as_temp(block, statement_scope, expr_id, Mutability::Not));
if let Some(span) = adjusted_span {
this.local_decls[temp].source_info.span = span;

View File

@ -36,19 +36,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn then_else_break(
&mut self,
mut block: BasicBlock,
expr: &Expr<'tcx>,
expr_id: ExprId,
temp_scope_override: Option<region::Scope>,
break_scope: region::Scope,
variable_source_info: SourceInfo,
) -> BlockAnd<()> {
let this = self;
let expr = &this.thir[expr_id];
let expr_span = expr.span;
match expr.kind {
ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
let lhs_then_block = unpack!(this.then_else_break(
block,
&this.thir[lhs],
lhs,
temp_scope_override,
break_scope,
variable_source_info,
@ -56,7 +57,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let rhs_then_block = unpack!(this.then_else_break(
lhs_then_block,
&this.thir[rhs],
rhs,
temp_scope_override,
break_scope,
variable_source_info,
@ -70,7 +71,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.in_if_then_scope(local_scope, expr_span, |this| {
this.then_else_break(
block,
&this.thir[lhs],
lhs,
temp_scope_override,
local_scope,
variable_source_info,
@ -78,7 +79,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
});
let rhs_success_block = unpack!(this.then_else_break(
failure_block,
&this.thir[rhs],
rhs,
temp_scope_override,
break_scope,
variable_source_info,
@ -97,7 +98,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
this.then_else_break(
block,
&this.thir[arg],
arg,
temp_scope_override,
local_scope,
variable_source_info,
@ -111,7 +112,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.in_scope(region_scope, lint_level, |this| {
this.then_else_break(
block,
&this.thir[value],
value,
temp_scope_override,
break_scope,
variable_source_info,
@ -120,14 +121,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
ExprKind::Use { source } => this.then_else_break(
block,
&this.thir[source],
source,
temp_scope_override,
break_scope,
variable_source_info,
),
ExprKind::Let { expr, ref pat } => this.lower_let_expr(
block,
&this.thir[expr],
expr,
pat,
break_scope,
Some(variable_source_info.scope),
@ -138,7 +139,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let temp_scope = temp_scope_override.unwrap_or_else(|| this.local_scope());
let mutability = Mutability::Mut;
let place =
unpack!(block = this.as_temp(block, Some(temp_scope), expr, mutability));
unpack!(block = this.as_temp(block, Some(temp_scope), expr_id, mutability));
let operand = Operand::Move(Place::from(place));
let then_block = this.cfg.start_new_block();
@ -208,14 +209,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn match_expr(
&mut self,
destination: Place<'tcx>,
span: Span,
mut block: BasicBlock,
scrutinee: &Expr<'tcx>,
scrutinee_id: ExprId,
arms: &[ArmId],
span: Span,
scrutinee_span: Span,
) -> BlockAnd<()> {
let scrutinee_span = scrutinee.span;
let scrutinee_span = scrutinee_span;
let scrutinee_place =
unpack!(block = self.lower_scrutinee(block, scrutinee, scrutinee_span,));
unpack!(block = self.lower_scrutinee(block, scrutinee_id, scrutinee_span));
let mut arm_candidates = self.create_match_candidates(&scrutinee_place, arms);
@ -223,7 +225,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let mut candidates =
arm_candidates.iter_mut().map(|(_, candidate)| candidate).collect::<Vec<_>>();
let match_start_span = span.shrink_to_lo().to(scrutinee.span);
let match_start_span = span.shrink_to_lo().to(scrutinee_span);
let fake_borrow_temps = self.lower_match_tree(
block,
@ -248,10 +250,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
fn lower_scrutinee(
&mut self,
mut block: BasicBlock,
scrutinee: &Expr<'tcx>,
scrutinee_id: ExprId,
scrutinee_span: Span,
) -> BlockAnd<PlaceBuilder<'tcx>> {
let scrutinee_place_builder = unpack!(block = self.as_place_builder(block, scrutinee));
let scrutinee_place_builder = unpack!(block = self.as_place_builder(block, scrutinee_id));
if let Some(scrutinee_place) = scrutinee_place_builder.try_to_place(self) {
let source_info = self.source_info(scrutinee_span);
self.cfg.push_place_mention(block, source_info, scrutinee_place);
@ -394,6 +396,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let arm_scope = (arm.scope, arm_source_info);
let match_scope = self.local_scope();
self.in_scope(arm_scope, arm.lint_level, |this| {
let old_dedup_scope =
mem::replace(&mut this.fixed_temps_scope, Some(arm.scope));
// `try_to_place` may fail if it is unable to resolve the given
// `PlaceBuilder` inside a closure. In this case, we don't want to include
// a scrutinee place. `scrutinee_place_builder` will fail to be resolved
@ -425,11 +430,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
false,
);
this.fixed_temps_scope = old_dedup_scope;
if let Some(source_scope) = scope {
this.source_scope = source_scope;
}
this.expr_into_dest(destination, arm_block, &this.thir[arm.body])
this.expr_into_dest(destination, arm_block, arm.body)
})
})
.collect();
@ -538,14 +545,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
&mut self,
mut block: BasicBlock,
irrefutable_pat: &Pat<'tcx>,
initializer: &Expr<'tcx>,
initializer_id: ExprId,
) -> BlockAnd<()> {
match irrefutable_pat.kind {
// Optimize the case of `let x = ...` to write directly into `x`
PatKind::Binding { mode: BindingMode::ByValue, var, subpattern: None, .. } => {
let place =
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
unpack!(block = self.expr_into_dest(place, block, initializer));
unpack!(block = self.expr_into_dest(place, block, initializer_id));
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
let source_info = self.source_info(irrefutable_pat.span);
@ -576,7 +583,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
} => {
let place =
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
unpack!(block = self.expr_into_dest(place, block, initializer));
unpack!(block = self.expr_into_dest(place, block, initializer_id));
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
let pattern_source_info = self.source_info(irrefutable_pat.span);
@ -616,8 +623,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
}
_ => {
let initializer = &self.thir[initializer_id];
let place_builder =
unpack!(block = self.lower_scrutinee(block, initializer, initializer.span));
unpack!(block = self.lower_scrutinee(block, initializer_id, initializer.span));
self.place_into_pattern(block, irrefutable_pat, place_builder, true)
}
}
@ -1831,15 +1839,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn lower_let_expr(
&mut self,
mut block: BasicBlock,
expr: &Expr<'tcx>,
expr_id: ExprId,
pat: &Pat<'tcx>,
else_target: region::Scope,
source_scope: Option<SourceScope>,
span: Span,
declare_bindings: bool,
) -> BlockAnd<()> {
let expr_span = expr.span;
let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr, expr_span));
let expr_span = self.thir[expr_id].span;
let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span));
let wildcard = Pat::wildcard_from_ty(pat.ty);
let mut guard_candidate = Candidate::new(expr_place_builder.clone(), pat, false, self);
let mut otherwise_candidate =
@ -1865,7 +1873,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.source_info(pat.span),
guard_candidate,
&fake_borrow_temps,
expr.span,
expr_span,
None,
false,
);
@ -2028,8 +2036,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let (post_guard_block, otherwise_post_guard_block) =
self.in_if_then_scope(match_scope, guard_span, |this| match *guard {
Guard::If(e) => {
let e = &this.thir[e];
guard_span = e.span;
guard_span = this.thir[e].span;
this.then_else_break(
block,
e,
@ -2038,9 +2045,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.source_info(arm.span),
)
}
Guard::IfLet(ref pat, scrutinee) => {
let s = &this.thir[scrutinee];
guard_span = s.span;
Guard::IfLet(ref pat, s) => {
guard_span = this.thir[s].span;
this.lower_let_expr(block, s, pat, match_scope, None, arm.span, false)
}
});
@ -2334,7 +2340,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn ast_let_else(
&mut self,
mut block: BasicBlock,
init: &Expr<'tcx>,
init_id: ExprId,
initializer_span: Span,
else_block: BlockId,
let_else_scope: &region::Scope,
@ -2342,8 +2348,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
) -> BlockAnd<BasicBlock> {
let else_block_span = self.thir[else_block].span;
let (matching, failure) = self.in_if_then_scope(*let_else_scope, else_block_span, |this| {
let scrutinee = unpack!(block = this.lower_scrutinee(block, init, initializer_span));
let pat = Pat { ty: init.ty, span: else_block_span, kind: PatKind::Wild };
let scrutinee = unpack!(block = this.lower_scrutinee(block, init_id, initializer_span));
let pat = Pat { ty: pattern.ty, span: else_block_span, kind: PatKind::Wild };
let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false, this);
let mut candidate = Candidate::new(scrutinee.clone(), pattern, false, this);
let fake_borrow_temps = this.lower_match_tree(

View File

@ -18,7 +18,7 @@ use rustc_middle::middle::region;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::*;
use rustc_middle::thir::{
self, BindingMode, Expr, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir,
self, BindingMode, ExprId, LintLevel, LocalVarId, Param, ParamId, PatKind, Thir,
};
use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt};
use rustc_span::symbol::sym;
@ -210,6 +210,12 @@ struct Builder<'a, 'tcx> {
/// finish building it.
guard_context: Vec<GuardFrame>,
/// Temporaries with fixed indexes. Used so that if-let guards on arms
/// with an or-pattern are only created once.
fixed_temps: FxHashMap<ExprId, Local>,
/// Scope of temporaries that should be deduplicated using [Self::fixed_temps].
fixed_temps_scope: Option<region::Scope>,
/// Maps `HirId`s of variable bindings to the `Local`s created for them.
/// (A match binding can have two locals; the 2nd is for the arm's guard.)
var_indices: FxHashMap<LocalVarId, LocalsForNode>,
@ -539,7 +545,7 @@ fn construct_fn<'tcx>(
let return_block =
unpack!(builder.in_breakable_scope(None, Place::return_place(), fn_end, |builder| {
Some(builder.in_scope(arg_scope_s, LintLevel::Inherited, |builder| {
builder.args_and_body(START_BLOCK, arguments, arg_scope, &thir[expr])
builder.args_and_body(START_BLOCK, arguments, arg_scope, expr)
}))
}));
let source_info = builder.source_info(fn_end);
@ -606,7 +612,7 @@ fn construct_const<'a, 'tcx>(
);
let mut block = START_BLOCK;
unpack!(block = builder.expr_into_dest(Place::return_place(), block, &thir[expr]));
unpack!(block = builder.expr_into_dest(Place::return_place(), block, expr));
let source_info = builder.source_info(span);
builder.cfg.terminate(block, source_info, TerminatorKind::Return);
@ -752,6 +758,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
source_scopes: IndexVec::new(),
source_scope: OUTERMOST_SOURCE_SCOPE,
guard_context: vec![],
fixed_temps: Default::default(),
fixed_temps_scope: None,
in_scope_unsafe: safety,
local_decls: IndexVec::from_elem_n(LocalDecl::new(return_ty, return_span), 1),
canonical_user_type_annotations: IndexVec::new(),
@ -865,8 +873,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
mut block: BasicBlock,
arguments: &IndexSlice<ParamId, Param<'tcx>>,
argument_scope: region::Scope,
expr: &Expr<'tcx>,
expr_id: ExprId,
) -> BlockAnd<()> {
let expr_span = self.thir[expr_id].span;
// Allocate locals for the function arguments
for (argument_index, param) in arguments.iter().enumerate() {
let source_info =
@ -899,7 +908,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// Make sure we drop (parts of) the argument even when not matched on.
self.schedule_drop(
param.pat.as_ref().map_or(expr.span, |pat| pat.span),
param.pat.as_ref().map_or(expr_span, |pat| pat.span),
argument_scope,
local,
DropKind::Value,
@ -941,8 +950,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
_ => {
scope = self.declare_bindings(
scope,
expr.span,
pat,
expr_span,
&pat,
None,
Some((Some(&place), span)),
);
@ -958,7 +967,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
self.source_scope = source_scope;
}
self.expr_into_dest(Place::return_place(), block, expr)
self.expr_into_dest(Place::return_place(), block, expr_id)
}
fn set_correct_source_scope_for_arg(

View File

@ -89,7 +89,7 @@ use rustc_hir::HirId;
use rustc_index::{IndexSlice, IndexVec};
use rustc_middle::middle::region;
use rustc_middle::mir::*;
use rustc_middle::thir::{Expr, LintLevel};
use rustc_middle::thir::{ExprId, LintLevel};
use rustc_session::lint::Level;
use rustc_span::{Span, DUMMY_SP};
@ -592,7 +592,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
pub(crate) fn break_scope(
&mut self,
mut block: BasicBlock,
value: Option<&Expr<'tcx>>,
value: Option<ExprId>,
target: BreakableTarget,
source_info: SourceInfo,
) -> BlockAnd<()> {

View File

@ -343,6 +343,17 @@ fn if_let_guard(a: &Allocator, c: bool, d: i32) {
}
}
fn if_let_guard_2(a: &Allocator, num: i32) {
let d = a.alloc();
match num {
#[allow(irrefutable_let_patterns)]
1 | 2 if let Ptr(ref _idx, _) = a.alloc() => {
a.alloc();
}
_ => {}
}
}
fn panic_after_return(a: &Allocator) -> Ptr<'_> {
// Panic in the drop of `p` or `q` can leak
let exceptions = vec![8, 9];
@ -514,6 +525,9 @@ fn main() {
run_test(|a| if_let_guard(a, false, 0));
run_test(|a| if_let_guard(a, false, 1));
run_test(|a| if_let_guard(a, false, 2));
run_test(|a| if_let_guard_2(a, 0));
run_test(|a| if_let_guard_2(a, 1));
run_test(|a| if_let_guard_2(a, 2));
run_test(|a| {
panic_after_return(a);

View File

@ -0,0 +1,59 @@
// check drop order of temporaries create in match guards.
// For normal guards all temporaries are dropped before the body of the arm.
// For let guards temporaries live until the end of the arm.
// run-pass
#![feature(if_let_guard)]
#![allow(irrefutable_let_patterns)]
use std::sync::Mutex;
static A: Mutex<Vec<i32>> = Mutex::new(Vec::new());
struct D(i32);
fn make_d(x: i32) -> D {
A.lock().unwrap().push(x);
D(x)
}
impl Drop for D {
fn drop(&mut self) {
A.lock().unwrap().push(!self.0);
}
}
fn if_guard(num: i32) {
let _d = make_d(1);
match num {
1 | 2 if make_d(2).0 == 2 => {
make_d(3);
}
_ => {}
}
}
fn if_let_guard(num: i32) {
let _d = make_d(1);
match num {
1 | 2 if let D(ref _x) = make_d(2) => {
make_d(3);
}
_ => {}
}
}
fn main() {
if_guard(1);
if_guard(2);
if_let_guard(1);
if_let_guard(2);
let expected = [
1, 2, !2, 3, !3, !1,
1, 2, !2, 3, !3, !1,
1, 2, 3, !3, !2, !1,
1, 2, 3, !3, !2, !1,
];
assert_eq!(*A.lock().unwrap(), expected);
}

View File

@ -0,0 +1,19 @@
// check-pass
#![feature(if_let_guard)]
fn split_last(_: &()) -> Option<(&i32, &i32)> {
None
}
fn assign_twice() {
loop {
match () {
#[allow(irrefutable_let_patterns)]
() if let _ = split_last(&()) => {}
_ => {}
}
}
}
fn main() {}

View File

@ -0,0 +1,32 @@
// Check that temporaries in if-let guards are correctly scoped.
// Regression test for #116079.
// build-pass
// edition:2018
// -Zvalidate-mir
#![feature(if_let_guard)]
static mut A: [i32; 5] = [1, 2, 3, 4, 5];
async fn fun() {
unsafe {
match A {
_ => (),
i if let Some(1) = async { Some(1) }.await => (),
_ => (),
}
}
}
async fn funner() {
unsafe {
match A {
_ => (),
_ | _ if let Some(1) = async { Some(1) }.await => (),
_ => (),
}
}
}
fn main() {}

View File

@ -0,0 +1,24 @@
// Check that temporaries in if-let guards are correctly scoped.
// build-pass
// -Zvalidate-mir
#![feature(if_let_guard)]
fn fun() {
match 0 {
_ => (),
_ if let Some(s) = std::convert::identity(&Some(String::new())) => {}
_ => (),
}
}
fn funner() {
match 0 {
_ => (),
_ | _ if let Some(s) = std::convert::identity(&Some(String::new())) => {}
_ => (),
}
}
fn main() {}

View File

@ -124,7 +124,7 @@ body:
body:
Expr {
ty: bool
temp_lifetime: Some(Node(13))
temp_lifetime: Some(Node(12))
span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0)
kind:
Scope {
@ -133,7 +133,7 @@ body:
value:
Expr {
ty: bool
temp_lifetime: Some(Node(13))
temp_lifetime: Some(Node(12))
span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0)
kind:
Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:17:36: 17:40 (#0) }, neg: false)
@ -176,7 +176,7 @@ body:
body:
Expr {
ty: bool
temp_lifetime: Some(Node(19))
temp_lifetime: Some(Node(18))
span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0)
kind:
Scope {
@ -185,7 +185,7 @@ body:
value:
Expr {
ty: bool
temp_lifetime: Some(Node(19))
temp_lifetime: Some(Node(18))
span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0)
kind:
Literal( lit: Spanned { node: Bool(false), span: $DIR/thir-tree-match.rs:18:27: 18:32 (#0) }, neg: false)
@ -220,7 +220,7 @@ body:
body:
Expr {
ty: bool
temp_lifetime: Some(Node(24))
temp_lifetime: Some(Node(23))
span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0)
kind:
Scope {
@ -229,7 +229,7 @@ body:
value:
Expr {
ty: bool
temp_lifetime: Some(Node(24))
temp_lifetime: Some(Node(23))
span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0)
kind:
Literal( lit: Spanned { node: Bool(true), span: $DIR/thir-tree-match.rs:19:24: 19:28 (#0) }, neg: false)