Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 21 additions & 10 deletions compiler/rustc_mir_build/src/builder/block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,18 +166,18 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
// should never be used to take values at the end of the failure
// block.
let dummy_place = this.temp(this.tcx.types.never, else_block_span);
let failure_entry = this.cfg.start_new_block();
let failure_block;
failure_block = this
// An unsuccessful match will jump to this block.
let failure_entry_block = this.cfg.start_new_block();
let failure_end_block = this
.ast_block(
dummy_place,
failure_entry,
failure_entry_block,
*else_block,
this.source_info(else_block_span),
)
.into_block();
this.cfg.terminate(
failure_block,
failure_end_block,
this.source_info(else_block_span),
TerminatorKind::Unreachable,
);
Expand All @@ -193,7 +193,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let initializer_span = this.thir[*initializer].span;
let scope = (*init_scope, source_info);
let lint_level = LintLevel::Explicit(*hir_id);
let failure_and_block = this.in_scope(scope, lint_level, |this| {

// Lower the initializer and test it against the pattern, leading to a
// true path (successful match) and a false path (failure).
let true_and_false_blocks = this.in_scope(scope, lint_level, |this| {
this.declare_bindings(
visibility_scope,
remainder_span,
Expand All @@ -202,8 +205,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
Some((Some(&destination), initializer_span)),
);
let else_block_span = this.thir[*else_block].span;
let (matching, failure) =
let (true_block, false_block) =
this.in_if_then_scope(last_remainder_scope, else_block_span, |this| {
// Bypass `lower_if_condition` and call `lower_let_expr` directly,
// since we don't have an actual THIR let-expression here.
this.lower_let_expr(
block,
*initializer,
Expand All @@ -213,10 +218,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
DeclareLetBindings::No,
)
});
matching.and(failure)
// Pack `(true_block, false_block)` into `BlockAnd<BasicBlock>`.
true_block.and(false_block)
});
let failure = unpack!(block = failure_and_block);
this.cfg.goto(failure, source_info, failure_entry);
// Unpack `BlockAnd<BasicBlock>` into `(true_block, false_block)`.
let (true_block, false_block);
false_block = unpack!(true_block = true_and_false_blocks);

// Proceed along the successful path, or jump to the failure path.
block = true_block;
this.cfg.goto(false_block, source_info, failure_entry_block);

if let Some(source_scope) = visibility_scope {
this.source_scope = source_scope;
Expand Down
12 changes: 6 additions & 6 deletions compiler/rustc_mir_build/src/builder/coverageinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -232,13 +232,13 @@ impl<'tcx> Builder<'_, 'tcx> {
*block = join_block;
}

/// If branch coverage is enabled, inject marker statements into `then_block`
/// and `else_block`, and record their IDs in the table of branch spans.
/// If branch coverage is enabled, inject marker statements into `true_block`
/// and `false_block`, and record their IDs in the table of branch spans.
pub(crate) fn visit_coverage_branch_condition(
&mut self,
mut expr_id: ExprId,
mut then_block: BasicBlock,
mut else_block: BasicBlock,
mut true_block: BasicBlock,
mut false_block: BasicBlock,
) {
// Bail out if coverage is not enabled for this function.
let Some(coverage_info) = self.coverage_info.as_mut() else { return };
Expand All @@ -248,13 +248,13 @@ impl<'tcx> Builder<'_, 'tcx> {
if let Some(&NotInfo { enclosing_not, is_flipped }) = coverage_info.nots.get(&expr_id) {
expr_id = enclosing_not;
if is_flipped {
std::mem::swap(&mut then_block, &mut else_block);
std::mem::swap(&mut true_block, &mut false_block);
}
}

let source_info = SourceInfo { span: self.thir[expr_id].span, scope: self.source_scope };

coverage_info.register_two_way_branch(&mut self.cfg, source_info, then_block, else_block);
coverage_info.register_two_way_branch(&mut self.cfg, source_info, true_block, false_block);
}

/// If branch coverage is enabled, inject marker statements into `true_block`
Expand Down
81 changes: 43 additions & 38 deletions compiler/rustc_mir_build/src/builder/expr/into.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use rustc_trait_selection::infer::InferCtxtExt;
use tracing::{debug, instrument};

use crate::builder::expr::category::{Category, RvalueFunc};
use crate::builder::matches::{DeclareLetBindings, Exhaustive, HasMatchGuard};
use crate::builder::matches::{DeclareLetBindings, Exhaustive, HasMatchGuard, LowerIfCondArgs};
use crate::builder::scope::LintLevel;
use crate::builder::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
use crate::diagnostics::{LoopMatchArmWithGuard, LoopMatchUnsupportedType};
Expand Down Expand Up @@ -67,7 +67,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let then_source_info = this.source_info(then_span);
let condition_scope = this.local_scope();

let then_and_else_blocks = this.in_scope(
let true_and_false_blocks = this.in_scope(
(if_then_scope, then_source_info),
LintLevel::Inherited,
|this| {
Expand All @@ -81,47 +81,50 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
this.source_info(then_span)
};

// Lower the condition, and have it branch into `then` and `else` blocks.
let (then_block, else_block) =
// Lower the condition, and have it branch into *true* and *false* blocks.
let (true_block, false_block) =
this.in_if_then_scope(condition_scope, then_span, |this| {
let then_blk = this
.then_else_break(
let true_block = this
.lower_if_condition(
block,
cond,
Some(condition_scope), // Temp scope
source_info,
DeclareLetBindings::Yes, // Declare `let` bindings normally
LowerIfCondArgs {
temp_scope_override: Some(condition_scope),
variable_source_info: source_info,
declare_let_bindings: DeclareLetBindings::Yes,
},
)
.into_block();

// Lower the `then` arm into its block.
this.expr_into_dest(destination, then_blk, then)
this.expr_into_dest(destination, true_block, then)
});

// Pack `(then_block, else_block)` into `BlockAnd<BasicBlock>`.
then_block.and(else_block)
// Pack `(true_block, false_block)` into `BlockAnd<BasicBlock>`.
true_block.and(false_block)
},
);

// Unpack `BlockAnd<BasicBlock>` into `(then_blk, else_blk)`.
let (then_blk, mut else_blk);
else_blk = unpack!(then_blk = then_and_else_blocks);
// Unpack `BlockAnd<BasicBlock>` into `(true_block, false_block)`.
let (true_block, mut false_block);
false_block = unpack!(true_block = true_and_false_blocks);

// If there is an `else` arm, lower it into `else_blk`.
// If there is an `else` arm, lower it into `false_block`.
if let Some(else_expr) = else_opt {
else_blk = this.expr_into_dest(destination, else_blk, else_expr).into_block();
false_block =
this.expr_into_dest(destination, false_block, else_expr).into_block();
} else {
// There is no `else` arm, so we know both arms have type `()`.
// Generate the implicit `else {}` by assigning unit.
let correct_si = this.source_info(expr_span.shrink_to_hi());
this.cfg.push_assign_unit(else_blk, correct_si, destination, this.tcx);
this.cfg.push_assign_unit(false_block, correct_si, destination, this.tcx);
}

// The `then` and `else` arms have been lowered into their respective
// blocks, so make both of them meet up in a new block.
let join_block = this.cfg.start_new_block();
this.cfg.goto(then_blk, source_info, join_block);
this.cfg.goto(else_blk, source_info, join_block);
this.cfg.goto(true_block, source_info, join_block);
this.cfg.goto(false_block, source_info, join_block);
join_block.unit()
}
ExprKind::Let { .. } => {
Expand Down Expand Up @@ -158,48 +161,50 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let source_info = this.source_info(expr.span);

// We first evaluate the left-hand side of the predicate ...
let (then_block, else_block) =
let (true_block, false_block) =
this.in_if_then_scope(condition_scope, expr.span, |this| {
this.then_else_break(
this.lower_if_condition(
block,
lhs,
Some(condition_scope), // Temp scope
source_info,
// This flag controls how inner `let` expressions are lowered,
// but either way there shouldn't be any of those in here.
DeclareLetBindings::LetNotPermitted,
LowerIfCondArgs {
temp_scope_override: Some(condition_scope),
variable_source_info: source_info,
declare_let_bindings: DeclareLetBindings::LetNotPermitted,
},
)
});
let (short_circuit, continuation, constant) = match op {
LogicalOp::And => (else_block, then_block, false),
LogicalOp::Or => (then_block, else_block, true),
};

// At this point, the control flow splits into a short-circuiting path
// and a continuation path.
// - If the operator is `&&`, passing `lhs` leads to continuation of evaluation on `rhs`;
// failing it leads to the short-circuting path which assigns `false` to the place.
// - If the operator is `||`, failing `lhs` leads to continuation of evaluation on `rhs`;
// passing it leads to the short-circuting path which assigns `true` to the place.
let (short_circuit_block, short_circuit_value, continue_block) = match op {
LogicalOp::And => (false_block, false, true_block),
LogicalOp::Or => (true_block, true, false_block),
};
this.cfg.push_assign_constant(
short_circuit,
short_circuit_block,
source_info,
destination,
ConstOperand {
span: expr.span,
user_ty: None,
const_: Const::from_bool(this.tcx, constant),
const_: Const::from_bool(this.tcx, short_circuit_value),
},
);
let mut rhs_block =
this.expr_into_dest(destination, continuation, rhs).into_block();
this.expr_into_dest(destination, continue_block, rhs).into_block();
// Instrument the lowered RHS's value for condition coverage.
// (Does nothing if condition coverage is not enabled.)
this.visit_coverage_standalone_condition(rhs, destination, &mut rhs_block);

let target = this.cfg.start_new_block();
this.cfg.goto(rhs_block, source_info, target);
this.cfg.goto(short_circuit, source_info, target);
target.unit()
// Reunite the continuation path and the short-circuit path.
let join_block = this.cfg.start_new_block();
this.cfg.goto(rhs_block, source_info, join_block);
this.cfg.goto(short_circuit_block, source_info, join_block);
join_block.unit()
}
ExprKind::Loop { body } => {
// [block]
Expand Down
Loading
Loading