Auto merge of #127174 - matthiaskrgr:rollup-q87j6cn, r=matthiaskrgr
Rollup of 9 pull requests Successful merges: - #126018 (Remove the `box_pointers` lint.) - #126895 (Fix simd_gather documentation) - #126981 (Replace some magic booleans in match-lowering with enums) - #127038 (Update test comment) - #127053 (Update the LoongArch target documentation) - #127069 (small correction to fmt::Pointer impl) - #127157 (coverage: Avoid getting extra unexpansion info when we don't need it) - #127160 (Add a regression test for #123630) - #127161 (Improve `run-make-support` library `args` API) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
6868c831a1
33 changed files with 430 additions and 349 deletions
|
@ -56,8 +56,6 @@ lint_builtin_asm_labels = avoid using named labels in inline assembly
|
|||
.help = only local labels of the form `<number>:` should be used in inline asm
|
||||
.note = see the asm section of Rust By Example <https://doc.rust-lang.org/nightly/rust-by-example/unsafe/asm.html#labels> for more information
|
||||
|
||||
lint_builtin_box_pointers = type uses owned (Box type) pointers: {$ty}
|
||||
|
||||
lint_builtin_clashing_extern_diff_name = `{$this}` redeclares `{$orig}` with a different signature
|
||||
.previous_decl_label = `{$orig}` previously declared here
|
||||
.mismatch_label = this signature doesn't match the previous declaration
|
||||
|
|
|
@ -24,9 +24,9 @@ use crate::fluent_generated as fluent;
|
|||
use crate::{
|
||||
errors::BuiltinEllipsisInclusiveRangePatterns,
|
||||
lints::{
|
||||
BuiltinAnonymousParams, BuiltinBoxPointers, BuiltinConstNoMangle,
|
||||
BuiltinDeprecatedAttrLink, BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed,
|
||||
BuiltinDerefNullptr, BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
|
||||
BuiltinAnonymousParams, BuiltinConstNoMangle, BuiltinDeprecatedAttrLink,
|
||||
BuiltinDeprecatedAttrLinkSuggestion, BuiltinDeprecatedAttrUsed, BuiltinDerefNullptr,
|
||||
BuiltinEllipsisInclusiveRangePatternsLint, BuiltinExplicitOutlives,
|
||||
BuiltinExplicitOutlivesSuggestion, BuiltinFeatureIssueNote, BuiltinIncompleteFeatures,
|
||||
BuiltinIncompleteFeaturesHelp, BuiltinInternalFeatures, BuiltinKeywordIdents,
|
||||
BuiltinMissingCopyImpl, BuiltinMissingDebugImpl, BuiltinMissingDoc,
|
||||
|
@ -56,7 +56,6 @@ use rustc_middle::bug;
|
|||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::layout::LayoutOf;
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::GenericArgKind;
|
||||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::Upcast;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
|
||||
|
@ -134,80 +133,6 @@ impl EarlyLintPass for WhileTrue {
|
|||
}
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `box_pointers` lints use of the Box type.
|
||||
///
|
||||
/// ### Example
|
||||
///
|
||||
/// ```rust,compile_fail
|
||||
/// #![deny(box_pointers)]
|
||||
/// struct Foo {
|
||||
/// x: Box<i32>,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// {{produces}}
|
||||
///
|
||||
/// ### Explanation
|
||||
///
|
||||
/// This lint is mostly historical, and not particularly useful. `Box<T>`
|
||||
/// used to be built into the language, and the only way to do heap
|
||||
/// allocation. Today's Rust can call into other allocators, etc.
|
||||
BOX_POINTERS,
|
||||
Allow,
|
||||
"use of owned (Box type) heap memory"
|
||||
}
|
||||
|
||||
declare_lint_pass!(BoxPointers => [BOX_POINTERS]);
|
||||
|
||||
impl BoxPointers {
|
||||
fn check_heap_type(&self, cx: &LateContext<'_>, span: Span, ty: Ty<'_>) {
|
||||
for leaf in ty.walk() {
|
||||
if let GenericArgKind::Type(leaf_ty) = leaf.unpack()
|
||||
&& leaf_ty.is_box()
|
||||
{
|
||||
cx.emit_span_lint(BOX_POINTERS, span, BuiltinBoxPointers { ty });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for BoxPointers {
|
||||
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
|
||||
match it.kind {
|
||||
hir::ItemKind::Fn(..)
|
||||
| hir::ItemKind::TyAlias(..)
|
||||
| hir::ItemKind::Enum(..)
|
||||
| hir::ItemKind::Struct(..)
|
||||
| hir::ItemKind::Union(..) => self.check_heap_type(
|
||||
cx,
|
||||
it.span,
|
||||
cx.tcx.type_of(it.owner_id).instantiate_identity(),
|
||||
),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
// If it's a struct, we also have to check the fields' types
|
||||
match it.kind {
|
||||
hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => {
|
||||
for field in struct_def.fields() {
|
||||
self.check_heap_type(
|
||||
cx,
|
||||
field.span,
|
||||
cx.tcx.type_of(field.def_id).instantiate_identity(),
|
||||
);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) {
|
||||
let ty = cx.typeck_results().node_type(e.hir_id);
|
||||
self.check_heap_type(cx, e.span, ty);
|
||||
}
|
||||
}
|
||||
|
||||
declare_lint! {
|
||||
/// The `non_shorthand_field_patterns` lint detects using `Struct { x: x }`
|
||||
/// instead of `Struct { x }` in a pattern.
|
||||
|
@ -1640,7 +1565,6 @@ declare_lint_pass!(
|
|||
/// which are used by other parts of the compiler.
|
||||
SoftLints => [
|
||||
WHILE_TRUE,
|
||||
BOX_POINTERS,
|
||||
NON_SHORTHAND_FIELD_PATTERNS,
|
||||
UNSAFE_CODE,
|
||||
MISSING_DOCS,
|
||||
|
|
|
@ -187,7 +187,6 @@ late_lint_methods!(
|
|||
ImproperCTypesDefinitions: ImproperCTypesDefinitions,
|
||||
InvalidFromUtf8: InvalidFromUtf8,
|
||||
VariantSizeDifferences: VariantSizeDifferences,
|
||||
BoxPointers: BoxPointers,
|
||||
PathStatements: PathStatements,
|
||||
LetUnderscore: LetUnderscore,
|
||||
InvalidReferenceCasting: InvalidReferenceCasting,
|
||||
|
@ -551,6 +550,10 @@ fn register_builtins(store: &mut LintStore) {
|
|||
"converted into hard error, see RFC #3535 \
|
||||
<https://rust-lang.github.io/rfcs/3535-constants-in-patterns.html> for more information",
|
||||
);
|
||||
store.register_removed(
|
||||
"box_pointers",
|
||||
"it does not detect other kinds of allocations, and existed only for historical reasons",
|
||||
);
|
||||
}
|
||||
|
||||
fn register_internals(store: &mut LintStore) {
|
||||
|
|
|
@ -66,12 +66,6 @@ pub struct BuiltinWhileTrue {
|
|||
pub replace: String,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_builtin_box_pointers)]
|
||||
pub struct BuiltinBoxPointers<'a> {
|
||||
pub ty: Ty<'a>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_builtin_non_shorthand_field_patterns)]
|
||||
pub struct BuiltinNonShorthandFieldPatterns {
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use crate::build::matches::{DeclareLetBindings, EmitStorageLive, ScheduleDrops};
|
||||
use crate::build::ForGuard::OutsideGuard;
|
||||
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder};
|
||||
use rustc_middle::middle::region::Scope;
|
||||
|
@ -201,7 +202,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
pattern,
|
||||
UserTypeProjections::none(),
|
||||
&mut |this, _, _, node, span, _, _| {
|
||||
this.storage_live_binding(block, node, span, OutsideGuard, true);
|
||||
this.storage_live_binding(
|
||||
block,
|
||||
node,
|
||||
span,
|
||||
OutsideGuard,
|
||||
ScheduleDrops::Yes,
|
||||
);
|
||||
},
|
||||
);
|
||||
let else_block_span = this.thir[*else_block].span;
|
||||
|
@ -213,8 +220,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
pattern,
|
||||
None,
|
||||
initializer_span,
|
||||
false,
|
||||
true,
|
||||
DeclareLetBindings::No,
|
||||
EmitStorageLive::No,
|
||||
)
|
||||
});
|
||||
matching.and(failure)
|
||||
|
@ -291,7 +298,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
pattern,
|
||||
UserTypeProjections::none(),
|
||||
&mut |this, _, _, node, span, _, _| {
|
||||
this.storage_live_binding(block, node, span, OutsideGuard, true);
|
||||
this.storage_live_binding(
|
||||
block,
|
||||
node,
|
||||
span,
|
||||
OutsideGuard,
|
||||
ScheduleDrops::Yes,
|
||||
);
|
||||
this.schedule_drop_for_binding(node, span, OutsideGuard);
|
||||
},
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
//! See docs in build/expr/mod.rs
|
||||
|
||||
use crate::build::expr::category::{Category, RvalueFunc};
|
||||
use crate::build::matches::DeclareLetBindings;
|
||||
use crate::build::{BlockAnd, BlockAndExtension, BlockFrame, Builder, NeedsTemporary};
|
||||
use rustc_ast::InlineAsmOptions;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
|
@ -86,7 +87,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
cond,
|
||||
Some(condition_scope), // Temp scope
|
||||
source_info,
|
||||
true, // Declare `let` bindings normally
|
||||
DeclareLetBindings::Yes, // Declare `let` bindings normally
|
||||
));
|
||||
|
||||
// Lower the `then` arm into its block.
|
||||
|
@ -163,7 +164,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
source_info,
|
||||
// This flag controls how inner `let` expressions are lowered,
|
||||
// but either way there shouldn't be any of those in here.
|
||||
true,
|
||||
DeclareLetBindings::LetNotPermitted,
|
||||
)
|
||||
});
|
||||
let (short_circuit, continuation, constant) = match op {
|
||||
|
|
|
@ -28,6 +28,7 @@ mod simplify;
|
|||
mod test;
|
||||
mod util;
|
||||
|
||||
use std::assert_matches::assert_matches;
|
||||
use std::borrow::Borrow;
|
||||
use std::mem;
|
||||
|
||||
|
@ -39,9 +40,50 @@ struct ThenElseArgs {
|
|||
/// `self.local_scope()` is used.
|
||||
temp_scope_override: Option<region::Scope>,
|
||||
variable_source_info: SourceInfo,
|
||||
/// Determines how bindings should be handled when lowering `let` expressions.
|
||||
///
|
||||
/// Forwarded to [`Builder::lower_let_expr`] when lowering [`ExprKind::Let`].
|
||||
/// When false (for match guards), `let` bindings won't be declared.
|
||||
declare_let_bindings: bool,
|
||||
declare_let_bindings: DeclareLetBindings,
|
||||
}
|
||||
|
||||
/// Should lowering a `let` expression also declare its bindings?
|
||||
///
|
||||
/// Used by [`Builder::lower_let_expr`] when lowering [`ExprKind::Let`].
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum DeclareLetBindings {
|
||||
/// Yes, declare `let` bindings as normal for `if` conditions.
|
||||
Yes,
|
||||
/// No, don't declare `let` bindings, because the caller declares them
|
||||
/// separately due to special requirements.
|
||||
///
|
||||
/// Used for match guards and let-else.
|
||||
No,
|
||||
/// Let expressions are not permitted in this context, so it is a bug to
|
||||
/// try to lower one (e.g inside lazy-boolean-or or boolean-not).
|
||||
LetNotPermitted,
|
||||
}
|
||||
|
||||
/// Used by [`Builder::bind_matched_candidate_for_arm_body`] to determine
|
||||
/// whether or not to call [`Builder::storage_live_binding`] to emit
|
||||
/// [`StatementKind::StorageLive`].
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum EmitStorageLive {
|
||||
/// Yes, emit `StorageLive` as normal.
|
||||
Yes,
|
||||
/// No, don't emit `StorageLive`. The caller has taken responsibility for
|
||||
/// emitting `StorageLive` as appropriate.
|
||||
No,
|
||||
}
|
||||
|
||||
/// Used by [`Builder::storage_live_binding`] and [`Builder::bind_matched_candidate_for_arm_body`]
|
||||
/// to decide whether to schedule drops.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub(crate) enum ScheduleDrops {
|
||||
/// Yes, the relevant functions should also schedule drops as appropriate.
|
||||
Yes,
|
||||
/// No, don't schedule drops. The caller has taken responsibility for any
|
||||
/// appropriate drops.
|
||||
No,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
|
@ -57,7 +99,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
expr_id: ExprId,
|
||||
temp_scope_override: Option<region::Scope>,
|
||||
variable_source_info: SourceInfo,
|
||||
declare_let_bindings: bool,
|
||||
declare_let_bindings: DeclareLetBindings,
|
||||
) -> BlockAnd<()> {
|
||||
self.then_else_break_inner(
|
||||
block,
|
||||
|
@ -91,13 +133,19 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
this.then_else_break_inner(
|
||||
block,
|
||||
lhs,
|
||||
ThenElseArgs { declare_let_bindings: true, ..args },
|
||||
ThenElseArgs {
|
||||
declare_let_bindings: DeclareLetBindings::LetNotPermitted,
|
||||
..args
|
||||
},
|
||||
)
|
||||
});
|
||||
let rhs_success_block = unpack!(this.then_else_break_inner(
|
||||
failure_block,
|
||||
rhs,
|
||||
ThenElseArgs { declare_let_bindings: true, ..args },
|
||||
ThenElseArgs {
|
||||
declare_let_bindings: DeclareLetBindings::LetNotPermitted,
|
||||
..args
|
||||
},
|
||||
));
|
||||
|
||||
// Make the LHS and RHS success arms converge to a common block.
|
||||
|
@ -127,7 +175,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
this.then_else_break_inner(
|
||||
block,
|
||||
arg,
|
||||
ThenElseArgs { declare_let_bindings: true, ..args },
|
||||
ThenElseArgs {
|
||||
declare_let_bindings: DeclareLetBindings::LetNotPermitted,
|
||||
..args
|
||||
},
|
||||
)
|
||||
});
|
||||
this.break_for_else(success_block, args.variable_source_info);
|
||||
|
@ -147,7 +198,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
Some(args.variable_source_info.scope),
|
||||
args.variable_source_info.span,
|
||||
args.declare_let_bindings,
|
||||
false,
|
||||
EmitStorageLive::Yes,
|
||||
),
|
||||
_ => {
|
||||
let mut block = block;
|
||||
|
@ -440,7 +491,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
&fake_borrow_temps,
|
||||
scrutinee_span,
|
||||
Some((arm, match_scope)),
|
||||
false,
|
||||
EmitStorageLive::Yes,
|
||||
);
|
||||
|
||||
this.fixed_temps_scope = old_dedup_scope;
|
||||
|
@ -485,7 +536,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fake_borrow_temps: &[(Place<'tcx>, Local, FakeBorrowKind)],
|
||||
scrutinee_span: Span,
|
||||
arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
|
||||
storages_alive: bool,
|
||||
emit_storage_live: EmitStorageLive,
|
||||
) -> BasicBlock {
|
||||
if candidate.subcandidates.is_empty() {
|
||||
// Avoid generating another `BasicBlock` when we only have one
|
||||
|
@ -496,8 +547,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fake_borrow_temps,
|
||||
scrutinee_span,
|
||||
arm_match_scope,
|
||||
true,
|
||||
storages_alive,
|
||||
ScheduleDrops::Yes,
|
||||
emit_storage_live,
|
||||
)
|
||||
} else {
|
||||
// It's helpful to avoid scheduling drops multiple times to save
|
||||
|
@ -515,7 +566,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// To handle this we instead unschedule it's drop after each time
|
||||
// we lower the guard.
|
||||
let target_block = self.cfg.start_new_block();
|
||||
let mut schedule_drops = true;
|
||||
let mut schedule_drops = ScheduleDrops::Yes;
|
||||
let arm = arm_match_scope.unzip().0;
|
||||
// We keep a stack of all of the bindings and type ascriptions
|
||||
// from the parent candidates that we visit, that also need to
|
||||
|
@ -534,10 +585,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
scrutinee_span,
|
||||
arm_match_scope,
|
||||
schedule_drops,
|
||||
storages_alive,
|
||||
emit_storage_live,
|
||||
);
|
||||
if arm.is_none() {
|
||||
schedule_drops = false;
|
||||
schedule_drops = ScheduleDrops::No;
|
||||
}
|
||||
self.cfg.goto(binding_end, outer_source_info, target_block);
|
||||
},
|
||||
|
@ -563,8 +614,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
match irrefutable_pat.kind {
|
||||
// Optimize the case of `let x = ...` to write directly into `x`
|
||||
PatKind::Binding { mode: BindingMode(ByRef::No, _), var, subpattern: None, .. } => {
|
||||
let place =
|
||||
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
|
||||
let place = self.storage_live_binding(
|
||||
block,
|
||||
var,
|
||||
irrefutable_pat.span,
|
||||
OutsideGuard,
|
||||
ScheduleDrops::Yes,
|
||||
);
|
||||
unpack!(block = self.expr_into_dest(place, block, initializer_id));
|
||||
|
||||
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
|
||||
|
@ -597,8 +653,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
},
|
||||
ascription: thir::Ascription { ref annotation, variance: _ },
|
||||
} => {
|
||||
let place =
|
||||
self.storage_live_binding(block, var, irrefutable_pat.span, OutsideGuard, true);
|
||||
let place = self.storage_live_binding(
|
||||
block,
|
||||
var,
|
||||
irrefutable_pat.span,
|
||||
OutsideGuard,
|
||||
ScheduleDrops::Yes,
|
||||
);
|
||||
unpack!(block = self.expr_into_dest(place, block, initializer_id));
|
||||
|
||||
// Inject a fake read, see comments on `FakeReadCause::ForLet`.
|
||||
|
@ -704,7 +765,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
&[],
|
||||
irrefutable_pat.span,
|
||||
None,
|
||||
false,
|
||||
EmitStorageLive::Yes,
|
||||
)
|
||||
.unit()
|
||||
}
|
||||
|
@ -780,13 +841,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Emits a [`StatementKind::StorageLive`] for the given var, and also
|
||||
/// schedules a drop if requested (and possible).
|
||||
pub(crate) fn storage_live_binding(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
var: LocalVarId,
|
||||
span: Span,
|
||||
for_guard: ForGuard,
|
||||
schedule_drop: bool,
|
||||
schedule_drop: ScheduleDrops,
|
||||
) -> Place<'tcx> {
|
||||
let local_id = self.var_local_id(var, for_guard);
|
||||
let source_info = self.source_info(span);
|
||||
|
@ -794,7 +857,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// Although there is almost always scope for given variable in corner cases
|
||||
// like #92893 we might get variable with no scope.
|
||||
if let Some(region_scope) = self.region_scope_tree.var_scope(var.0.local_id)
|
||||
&& schedule_drop
|
||||
&& matches!(schedule_drop, ScheduleDrops::Yes)
|
||||
{
|
||||
self.schedule_drop(span, region_scope, local_id, DropKind::Storage);
|
||||
}
|
||||
|
@ -1991,8 +2054,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// Pat binding - used for `let` and function parameters as well.
|
||||
|
||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
/// If the bindings have already been declared, set `declare_bindings` to
|
||||
/// `false` to avoid duplicated bindings declaration; used for if-let guards.
|
||||
/// Lowers a `let` expression that appears in a suitable context
|
||||
/// (e.g. an `if` condition or match guard).
|
||||
///
|
||||
/// Also used for lowering let-else statements, since they have similar
|
||||
/// needs despite not actually using `let` expressions.
|
||||
///
|
||||
/// Use [`DeclareLetBindings`] to control whether the `let` bindings are
|
||||
/// declared or not.
|
||||
pub(crate) fn lower_let_expr(
|
||||
&mut self,
|
||||
mut block: BasicBlock,
|
||||
|
@ -2000,8 +2069,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
pat: &Pat<'tcx>,
|
||||
source_scope: Option<SourceScope>,
|
||||
scope_span: Span,
|
||||
declare_bindings: bool,
|
||||
storages_alive: bool,
|
||||
declare_let_bindings: DeclareLetBindings,
|
||||
emit_storage_live: EmitStorageLive,
|
||||
) -> BlockAnd<()> {
|
||||
let expr_span = self.thir[expr_id].span;
|
||||
let scrutinee = unpack!(block = self.lower_scrutinee(block, expr_id, expr_span));
|
||||
|
@ -2017,10 +2086,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
|
||||
self.break_for_else(otherwise_block, self.source_info(expr_span));
|
||||
|
||||
if declare_bindings {
|
||||
let expr_place = scrutinee.try_to_place(self);
|
||||
let opt_expr_place = expr_place.as_ref().map(|place| (Some(place), expr_span));
|
||||
self.declare_bindings(source_scope, pat.span.to(scope_span), pat, None, opt_expr_place);
|
||||
match declare_let_bindings {
|
||||
DeclareLetBindings::Yes => {
|
||||
let expr_place = scrutinee.try_to_place(self);
|
||||
let opt_expr_place = expr_place.as_ref().map(|place| (Some(place), expr_span));
|
||||
self.declare_bindings(
|
||||
source_scope,
|
||||
pat.span.to(scope_span),
|
||||
pat,
|
||||
None,
|
||||
opt_expr_place,
|
||||
);
|
||||
}
|
||||
DeclareLetBindings::No => {} // Caller is responsible for bindings.
|
||||
DeclareLetBindings::LetNotPermitted => {
|
||||
self.tcx.dcx().span_bug(expr_span, "let expression not expected in this context")
|
||||
}
|
||||
}
|
||||
|
||||
let success = self.bind_pattern(
|
||||
|
@ -2029,7 +2110,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
&[],
|
||||
expr_span,
|
||||
None,
|
||||
storages_alive,
|
||||
emit_storage_live,
|
||||
);
|
||||
|
||||
// If branch coverage is enabled, record this branch.
|
||||
|
@ -2053,8 +2134,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fake_borrows: &[(Place<'tcx>, Local, FakeBorrowKind)],
|
||||
scrutinee_span: Span,
|
||||
arm_match_scope: Option<(&Arm<'tcx>, region::Scope)>,
|
||||
schedule_drops: bool,
|
||||
storages_alive: bool,
|
||||
schedule_drops: ScheduleDrops,
|
||||
emit_storage_live: EmitStorageLive,
|
||||
) -> BasicBlock {
|
||||
debug!("bind_and_guard_matched_candidate(candidate={:?})", candidate);
|
||||
|
||||
|
@ -2203,7 +2284,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
guard,
|
||||
None, // Use `self.local_scope()` as the temp scope
|
||||
this.source_info(arm.span),
|
||||
false, // For guards, `let` bindings are declared separately
|
||||
DeclareLetBindings::No, // For guards, `let` bindings are declared separately
|
||||
)
|
||||
});
|
||||
|
||||
|
@ -2264,12 +2345,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let cause = FakeReadCause::ForGuardBinding;
|
||||
self.cfg.push_fake_read(post_guard_block, guard_end, cause, Place::from(local_id));
|
||||
}
|
||||
assert!(schedule_drops, "patterns with guards must schedule drops");
|
||||
assert_matches!(
|
||||
schedule_drops,
|
||||
ScheduleDrops::Yes,
|
||||
"patterns with guards must schedule drops"
|
||||
);
|
||||
self.bind_matched_candidate_for_arm_body(
|
||||
post_guard_block,
|
||||
true,
|
||||
ScheduleDrops::Yes,
|
||||
by_value_bindings,
|
||||
storages_alive,
|
||||
emit_storage_live,
|
||||
);
|
||||
|
||||
post_guard_block
|
||||
|
@ -2281,7 +2366,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
block,
|
||||
schedule_drops,
|
||||
bindings,
|
||||
storages_alive,
|
||||
emit_storage_live,
|
||||
);
|
||||
block
|
||||
}
|
||||
|
@ -2317,7 +2402,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fn bind_matched_candidate_for_guard<'b>(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
schedule_drops: bool,
|
||||
schedule_drops: ScheduleDrops,
|
||||
bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
|
||||
) where
|
||||
'tcx: 'b,
|
||||
|
@ -2370,9 +2455,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
fn bind_matched_candidate_for_arm_body<'b>(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
schedule_drops: bool,
|
||||
schedule_drops: ScheduleDrops,
|
||||
bindings: impl IntoIterator<Item = &'b Binding<'tcx>>,
|
||||
storages_alive: bool,
|
||||
emit_storage_live: EmitStorageLive,
|
||||
) where
|
||||
'tcx: 'b,
|
||||
{
|
||||
|
@ -2382,21 +2467,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// Assign each of the bindings. This may trigger moves out of the candidate.
|
||||
for binding in bindings {
|
||||
let source_info = self.source_info(binding.span);
|
||||
let local = if storages_alive {
|
||||
let local = match emit_storage_live {
|
||||
// Here storages are already alive, probably because this is a binding
|
||||
// from let-else.
|
||||
// We just need to schedule drop for the value.
|
||||
self.var_local_id(binding.var_id, OutsideGuard).into()
|
||||
} else {
|
||||
self.storage_live_binding(
|
||||
EmitStorageLive::No => self.var_local_id(binding.var_id, OutsideGuard).into(),
|
||||
EmitStorageLive::Yes => self.storage_live_binding(
|
||||
block,
|
||||
binding.var_id,
|
||||
binding.span,
|
||||
OutsideGuard,
|
||||
schedule_drops,
|
||||
)
|
||||
),
|
||||
};
|
||||
if schedule_drops {
|
||||
if matches!(schedule_drops, ScheduleDrops::Yes) {
|
||||
self.schedule_drop_for_binding(binding.var_id, binding.span, OutsideGuard);
|
||||
}
|
||||
let rvalue = match binding.binding_mode.0 {
|
||||
|
|
|
@ -9,9 +9,8 @@ use rustc_middle::ty::TyCtxt;
|
|||
use rustc_span::Span;
|
||||
|
||||
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
|
||||
use crate::coverage::spans::{
|
||||
extract_refined_covspans, unexpand_into_body_span_with_visible_macro,
|
||||
};
|
||||
use crate::coverage::spans::extract_refined_covspans;
|
||||
use crate::coverage::unexpand::unexpand_into_body_span;
|
||||
use crate::coverage::ExtractedHirInfo;
|
||||
|
||||
/// Associates an ordinary executable code span with its corresponding BCB.
|
||||
|
@ -202,8 +201,7 @@ pub(super) fn extract_branch_pairs(
|
|||
if !raw_span.ctxt().outer_expn_data().is_root() {
|
||||
return None;
|
||||
}
|
||||
let (span, _) =
|
||||
unexpand_into_body_span_with_visible_macro(raw_span, hir_info.body_span)?;
|
||||
let span = unexpand_into_body_span(raw_span, hir_info.body_span)?;
|
||||
|
||||
let bcb_from_marker =
|
||||
|marker: BlockMarkerId| basic_coverage_blocks.bcb_from_bb(block_markers[marker]?);
|
||||
|
@ -238,7 +236,7 @@ pub(super) fn extract_mcdc_mappings(
|
|||
if !raw_span.ctxt().outer_expn_data().is_root() {
|
||||
return None;
|
||||
}
|
||||
let (span, _) = unexpand_into_body_span_with_visible_macro(raw_span, body_span)?;
|
||||
let span = unexpand_into_body_span(raw_span, body_span)?;
|
||||
|
||||
let true_bcb = bcb_from_marker(true_marker)?;
|
||||
let false_bcb = bcb_from_marker(false_marker)?;
|
||||
|
@ -261,7 +259,7 @@ pub(super) fn extract_mcdc_mappings(
|
|||
|
||||
mcdc_decisions.extend(branch_info.mcdc_decision_spans.iter().filter_map(
|
||||
|decision: &mir::coverage::MCDCDecisionSpan| {
|
||||
let (span, _) = unexpand_into_body_span_with_visible_macro(decision.span, body_span)?;
|
||||
let span = unexpand_into_body_span(decision.span, body_span)?;
|
||||
|
||||
let end_bcbs = decision
|
||||
.end_markers
|
||||
|
|
|
@ -6,6 +6,7 @@ mod mappings;
|
|||
mod spans;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
mod unexpand;
|
||||
|
||||
use rustc_middle::mir::coverage::{
|
||||
CodeRegion, CoverageKind, DecisionInfo, FunctionCoverageInfo, Mapping, MappingKind,
|
||||
|
|
|
@ -14,11 +14,6 @@ use crate::coverage::ExtractedHirInfo;
|
|||
|
||||
mod from_mir;
|
||||
|
||||
// FIXME(#124545) It's awkward that we have to re-export this, because it's an
|
||||
// internal detail of `from_mir` that is also needed when handling branch and
|
||||
// MC/DC spans. Ideally we would find a more natural home for it.
|
||||
pub(super) use from_mir::unexpand_into_body_span_with_visible_macro;
|
||||
|
||||
pub(super) fn extract_refined_covspans(
|
||||
mir_body: &mir::Body<'_>,
|
||||
hir_info: &ExtractedHirInfo,
|
||||
|
|
|
@ -4,12 +4,13 @@ use rustc_middle::mir::{
|
|||
self, AggregateKind, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
|
||||
TerminatorKind,
|
||||
};
|
||||
use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
use crate::coverage::graph::{
|
||||
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
|
||||
};
|
||||
use crate::coverage::spans::Covspan;
|
||||
use crate::coverage::unexpand::unexpand_into_body_span_with_visible_macro;
|
||||
use crate::coverage::ExtractedHirInfo;
|
||||
|
||||
pub(crate) struct ExtractedCovspans {
|
||||
|
@ -215,59 +216,6 @@ fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns an extrapolated span (pre-expansion[^1]) corresponding to a range
|
||||
/// within the function's body source. This span is guaranteed to be contained
|
||||
/// within, or equal to, the `body_span`. If the extrapolated span is not
|
||||
/// contained within the `body_span`, `None` is returned.
|
||||
///
|
||||
/// [^1]Expansions result from Rust syntax including macros, syntactic sugar,
|
||||
/// etc.).
|
||||
pub(crate) fn unexpand_into_body_span_with_visible_macro(
|
||||
original_span: Span,
|
||||
body_span: Span,
|
||||
) -> Option<(Span, Option<Symbol>)> {
|
||||
let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?;
|
||||
|
||||
let visible_macro = prev
|
||||
.map(|prev| match prev.ctxt().outer_expn_data().kind {
|
||||
ExpnKind::Macro(MacroKind::Bang, name) => Some(name),
|
||||
_ => None,
|
||||
})
|
||||
.flatten();
|
||||
|
||||
Some((span, visible_macro))
|
||||
}
|
||||
|
||||
/// Walks through the expansion ancestors of `original_span` to find a span that
|
||||
/// is contained in `body_span` and has the same [`SyntaxContext`] as `body_span`.
|
||||
/// The ancestor that was traversed just before the matching span (if any) is
|
||||
/// also returned.
|
||||
///
|
||||
/// For example, a return value of `Some((ancestor, Some(prev))` means that:
|
||||
/// - `ancestor == original_span.find_ancestor_inside_same_ctxt(body_span)`
|
||||
/// - `ancestor == prev.parent_callsite()`
|
||||
///
|
||||
/// [`SyntaxContext`]: rustc_span::SyntaxContext
|
||||
fn unexpand_into_body_span_with_prev(
|
||||
original_span: Span,
|
||||
body_span: Span,
|
||||
) -> Option<(Span, Option<Span>)> {
|
||||
let mut prev = None;
|
||||
let mut curr = original_span;
|
||||
|
||||
while !body_span.contains(curr) || !curr.eq_ctxt(body_span) {
|
||||
prev = Some(curr);
|
||||
curr = curr.parent_callsite()?;
|
||||
}
|
||||
|
||||
debug_assert_eq!(Some(curr), original_span.find_ancestor_in_same_ctxt(body_span));
|
||||
if let Some(prev) = prev {
|
||||
debug_assert_eq!(Some(curr), prev.parent_callsite());
|
||||
}
|
||||
|
||||
Some((curr, prev))
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Hole {
|
||||
pub(crate) span: Span,
|
||||
|
|
60
compiler/rustc_mir_transform/src/coverage/unexpand.rs
Normal file
60
compiler/rustc_mir_transform/src/coverage/unexpand.rs
Normal file
|
@ -0,0 +1,60 @@
|
|||
use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
|
||||
|
||||
/// Walks through the expansion ancestors of `original_span` to find a span that
|
||||
/// is contained in `body_span` and has the same [syntax context] as `body_span`.
|
||||
pub(crate) fn unexpand_into_body_span(original_span: Span, body_span: Span) -> Option<Span> {
|
||||
// Because we don't need to return any extra ancestor information,
|
||||
// we can just delegate directly to `find_ancestor_inside_same_ctxt`.
|
||||
original_span.find_ancestor_inside_same_ctxt(body_span)
|
||||
}
|
||||
|
||||
/// Walks through the expansion ancestors of `original_span` to find a span that
|
||||
/// is contained in `body_span` and has the same [syntax context] as `body_span`.
|
||||
///
|
||||
/// If the returned span represents a bang-macro invocation (e.g. `foo!(..)`),
|
||||
/// the returned symbol will be the name of that macro (e.g. `foo`).
|
||||
pub(crate) fn unexpand_into_body_span_with_visible_macro(
|
||||
original_span: Span,
|
||||
body_span: Span,
|
||||
) -> Option<(Span, Option<Symbol>)> {
|
||||
let (span, prev) = unexpand_into_body_span_with_prev(original_span, body_span)?;
|
||||
|
||||
let visible_macro = prev
|
||||
.map(|prev| match prev.ctxt().outer_expn_data().kind {
|
||||
ExpnKind::Macro(MacroKind::Bang, name) => Some(name),
|
||||
_ => None,
|
||||
})
|
||||
.flatten();
|
||||
|
||||
Some((span, visible_macro))
|
||||
}
|
||||
|
||||
/// Walks through the expansion ancestors of `original_span` to find a span that
|
||||
/// is contained in `body_span` and has the same [syntax context] as `body_span`.
|
||||
/// The ancestor that was traversed just before the matching span (if any) is
|
||||
/// also returned.
|
||||
///
|
||||
/// For example, a return value of `Some((ancestor, Some(prev)))` means that:
|
||||
/// - `ancestor == original_span.find_ancestor_inside_same_ctxt(body_span)`
|
||||
/// - `prev.parent_callsite() == ancestor`
|
||||
///
|
||||
/// [syntax context]: rustc_span::SyntaxContext
|
||||
fn unexpand_into_body_span_with_prev(
|
||||
original_span: Span,
|
||||
body_span: Span,
|
||||
) -> Option<(Span, Option<Span>)> {
|
||||
let mut prev = None;
|
||||
let mut curr = original_span;
|
||||
|
||||
while !body_span.contains(curr) || !curr.eq_ctxt(body_span) {
|
||||
prev = Some(curr);
|
||||
curr = curr.parent_callsite()?;
|
||||
}
|
||||
|
||||
debug_assert_eq!(Some(curr), original_span.find_ancestor_inside_same_ctxt(body_span));
|
||||
if let Some(prev) = prev {
|
||||
debug_assert_eq!(Some(curr), prev.parent_callsite());
|
||||
}
|
||||
|
||||
Some((curr, prev))
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue