Auto merge of #122423 - matthiaskrgr:rollup-qywgl45, r=matthiaskrgr
Rollup of 10 pull requests Successful merges: - #121820 (pattern analysis: Store field indices in `DeconstructedPat` to avoid virtual wildcards) - #121908 (match lowering: don't collect test alternatives ahead of time) - #122203 (Add `intrinsic_name` to get plain intrinsic name) - #122226 (coverage: Remove or migrate all unstable values of `-Cinstrument-coverage`) - #122255 (Use `min_exhaustive_patterns` in core & std) - #122360 ( Don't Create `ParamCandidate` When Obligation Contains Errors ) - #122383 (Enable PR tracking review assignment for rust-lang/rust) - #122386 (Move `Once` implementations to `sys`) - #122400 (Fix ICE in diagnostics for parenthesized type arguments) - #122410 (rustdoc: do not preload fonts when browsing locally) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
762d3170f6
@ -52,7 +52,7 @@ Copyright: 2019 The Crossbeam Project Developers
|
||||
The Rust Project Developers (see https://thanks.rust-lang.org)
|
||||
License: MIT OR Apache-2.0
|
||||
|
||||
Files: library/std/src/sys/locks/mutex/fuchsia.rs
|
||||
Files: library/std/src/sys/sync/mutex/fuchsia.rs
|
||||
Copyright: 2016 The Fuchsia Authors
|
||||
The Rust Project Developers (see https://thanks.rust-lang.org)
|
||||
License: BSD-2-Clause AND (MIT OR Apache-2.0)
|
||||
|
@ -355,21 +355,20 @@ fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
|
||||
|
||||
let tcx = cx.tcx;
|
||||
|
||||
let ignore_unused_generics = tcx.sess.instrument_coverage_except_unused_generics();
|
||||
|
||||
let eligible_def_ids = tcx.mir_keys(()).iter().filter_map(|local_def_id| {
|
||||
let def_id = local_def_id.to_def_id();
|
||||
let kind = tcx.def_kind(def_id);
|
||||
// `mir_keys` will give us `DefId`s for all kinds of things, not
|
||||
// just "functions", like consts, statics, etc. Filter those out.
|
||||
// If `ignore_unused_generics` was specified, filter out any
|
||||
// generic functions from consideration as well.
|
||||
if !matches!(kind, DefKind::Fn | DefKind::AssocFn | DefKind::Closure) {
|
||||
return None;
|
||||
}
|
||||
if ignore_unused_generics && tcx.generics_of(def_id).requires_monomorphization(tcx) {
|
||||
return None;
|
||||
}
|
||||
|
||||
// FIXME(79651): Consider trying to filter out dummy instantiations of
|
||||
// unused generic functions from library crates, because they can produce
|
||||
// "unused instantiation" in coverage reports even when they are actually
|
||||
// used by some downstream crate in the same binary.
|
||||
|
||||
Some(local_def_id.to_def_id())
|
||||
});
|
||||
|
||||
|
@ -4,11 +4,12 @@ use rustc_data_structures::profiling::TimePassesFormat;
|
||||
use rustc_errors::{emitter::HumanReadableErrorType, registry, ColorConfig};
|
||||
use rustc_session::config::{
|
||||
build_configuration, build_session_options, rustc_optgroups, BranchProtection, CFGuard, Cfg,
|
||||
CollapseMacroDebuginfo, DebugInfo, DumpMonoStatsFormat, ErrorOutputType, ExternEntry,
|
||||
ExternLocation, Externs, FunctionReturn, InliningThreshold, Input, InstrumentCoverage,
|
||||
InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli, NextSolverConfig,
|
||||
OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet, Passes, Polonius,
|
||||
ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
|
||||
CollapseMacroDebuginfo, CoverageOptions, DebugInfo, DumpMonoStatsFormat, ErrorOutputType,
|
||||
ExternEntry, ExternLocation, Externs, FunctionReturn, InliningThreshold, Input,
|
||||
InstrumentCoverage, InstrumentXRay, LinkSelfContained, LinkerPluginLto, LocationDetail, LtoCli,
|
||||
NextSolverConfig, OomStrategy, Options, OutFileName, OutputType, OutputTypes, PAuthKey, PacRet,
|
||||
Passes, Polonius, ProcMacroExecutionStrategy, Strip, SwitchWithOptPath, SymbolManglingVersion,
|
||||
WasiExecModel,
|
||||
};
|
||||
use rustc_session::lint::Level;
|
||||
use rustc_session::search_paths::SearchPath;
|
||||
@ -750,6 +751,7 @@ fn test_unstable_options_tracking_hash() {
|
||||
);
|
||||
tracked!(codegen_backend, Some("abc".to_string()));
|
||||
tracked!(collapse_macro_debuginfo, CollapseMacroDebuginfo::Yes);
|
||||
tracked!(coverage_options, CoverageOptions { branch: true });
|
||||
tracked!(crate_attr, vec!["abc".to_string()]);
|
||||
tracked!(cross_crate_inline_threshold, InliningThreshold::Always);
|
||||
tracked!(debug_info_for_profiling, true);
|
||||
|
@ -14,7 +14,6 @@ use rustc_data_structures::{
|
||||
fx::{FxHashSet, FxIndexMap, FxIndexSet},
|
||||
stack::ensure_sufficient_stack,
|
||||
};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::middle::region;
|
||||
use rustc_middle::mir::{self, *};
|
||||
use rustc_middle::thir::{self, *};
|
||||
@ -1084,6 +1083,12 @@ enum TestCase<'pat, 'tcx> {
|
||||
Or { pats: Box<[FlatPat<'pat, 'tcx>]> },
|
||||
}
|
||||
|
||||
impl<'pat, 'tcx> TestCase<'pat, 'tcx> {
|
||||
fn as_range(&self) -> Option<&'pat PatRange<'tcx>> {
|
||||
if let Self::Range(v) = self { Some(*v) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct MatchPair<'pat, 'tcx> {
|
||||
/// This place...
|
||||
@ -1108,19 +1113,10 @@ enum TestKind<'tcx> {
|
||||
Switch {
|
||||
/// The enum type being tested.
|
||||
adt_def: ty::AdtDef<'tcx>,
|
||||
/// The set of variants that we should create a branch for. We also
|
||||
/// create an additional "otherwise" case.
|
||||
variants: BitSet<VariantIdx>,
|
||||
},
|
||||
|
||||
/// Test what value an integer or `char` has.
|
||||
SwitchInt {
|
||||
/// The (ordered) set of values that we test for.
|
||||
///
|
||||
/// We create a branch to each of the values in `options`, as well as an "otherwise" branch
|
||||
/// for all other values, even in the (rare) case that `options` is exhaustive.
|
||||
options: FxIndexMap<Const<'tcx>, u128>,
|
||||
},
|
||||
SwitchInt,
|
||||
|
||||
/// Test what value a `bool` has.
|
||||
If,
|
||||
@ -1152,6 +1148,25 @@ pub(crate) struct Test<'tcx> {
|
||||
kind: TestKind<'tcx>,
|
||||
}
|
||||
|
||||
/// The branch to be taken after a test.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
enum TestBranch<'tcx> {
|
||||
/// Success branch, used for tests with two possible outcomes.
|
||||
Success,
|
||||
/// Branch corresponding to this constant.
|
||||
Constant(Const<'tcx>, u128),
|
||||
/// Branch corresponding to this variant.
|
||||
Variant(VariantIdx),
|
||||
/// Failure branch for tests with two possible outcomes, and "otherwise" branch for other tests.
|
||||
Failure,
|
||||
}
|
||||
|
||||
impl<'tcx> TestBranch<'tcx> {
|
||||
fn as_constant(&self) -> Option<&Const<'tcx>> {
|
||||
if let Self::Constant(v, _) = self { Some(v) } else { None }
|
||||
}
|
||||
}
|
||||
|
||||
/// `ArmHasGuard` is a wrapper around a boolean flag. It indicates whether
|
||||
/// a match arm has a guard expression attached to it.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -1561,30 +1576,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
) -> (PlaceBuilder<'tcx>, Test<'tcx>) {
|
||||
// Extract the match-pair from the highest priority candidate
|
||||
let match_pair = &candidates.first().unwrap().match_pairs[0];
|
||||
let mut test = self.test(match_pair);
|
||||
let test = self.test(match_pair);
|
||||
let match_place = match_pair.place.clone();
|
||||
|
||||
debug!(?test, ?match_pair);
|
||||
// Most of the time, the test to perform is simply a function of the main candidate; but for
|
||||
// a test like SwitchInt, we may want to add cases based on the candidates that are
|
||||
// available
|
||||
match test.kind {
|
||||
TestKind::SwitchInt { ref mut options } => {
|
||||
for candidate in candidates.iter() {
|
||||
if !self.add_cases_to_switch(&match_place, candidate, options) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
TestKind::Switch { adt_def: _, ref mut variants } => {
|
||||
for candidate in candidates.iter() {
|
||||
if !self.add_variants_to_switch(&match_place, candidate, variants) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
(match_place, test)
|
||||
}
|
||||
@ -1627,11 +1621,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
match_place: &PlaceBuilder<'tcx>,
|
||||
test: &Test<'tcx>,
|
||||
mut candidates: &'b mut [&'c mut Candidate<'pat, 'tcx>],
|
||||
) -> (&'b mut [&'c mut Candidate<'pat, 'tcx>], Vec<Vec<&'b mut Candidate<'pat, 'tcx>>>) {
|
||||
// For each of the N possible outcomes, create a (initially empty) vector of candidates.
|
||||
// Those are the candidates that apply if the test has that particular outcome.
|
||||
let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
|
||||
target_candidates.resize_with(test.targets(), Default::default);
|
||||
) -> (
|
||||
&'b mut [&'c mut Candidate<'pat, 'tcx>],
|
||||
FxIndexMap<TestBranch<'tcx>, Vec<&'b mut Candidate<'pat, 'tcx>>>,
|
||||
) {
|
||||
// For each of the possible outcomes, collect vector of candidates that apply if the test
|
||||
// has that particular outcome.
|
||||
let mut target_candidates: FxIndexMap<_, Vec<&mut Candidate<'_, '_>>> = Default::default();
|
||||
|
||||
let total_candidate_count = candidates.len();
|
||||
|
||||
@ -1639,11 +1635,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
// point we may encounter a candidate where the test is not relevant; at that point, we stop
|
||||
// sorting.
|
||||
while let Some(candidate) = candidates.first_mut() {
|
||||
let Some(idx) = self.sort_candidate(&match_place, &test, candidate) else {
|
||||
let Some(branch) =
|
||||
self.sort_candidate(&match_place, test, candidate, &target_candidates)
|
||||
else {
|
||||
break;
|
||||
};
|
||||
let (candidate, rest) = candidates.split_first_mut().unwrap();
|
||||
target_candidates[idx].push(candidate);
|
||||
target_candidates.entry(branch).or_insert_with(Vec::new).push(candidate);
|
||||
candidates = rest;
|
||||
}
|
||||
|
||||
@ -1784,31 +1782,32 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
otherwise_block
|
||||
};
|
||||
|
||||
// For each outcome of test, process the candidates that still
|
||||
// apply. Collect a list of blocks where control flow will
|
||||
// branch if one of the `target_candidate` sets is not
|
||||
// exhaustive.
|
||||
let target_blocks: Vec<_> = target_candidates
|
||||
// For each outcome of test, process the candidates that still apply.
|
||||
let target_blocks: FxIndexMap<_, _> = target_candidates
|
||||
.into_iter()
|
||||
.map(|mut candidates| {
|
||||
if !candidates.is_empty() {
|
||||
let candidate_start = self.cfg.start_new_block();
|
||||
self.match_candidates(
|
||||
span,
|
||||
scrutinee_span,
|
||||
candidate_start,
|
||||
remainder_start,
|
||||
&mut *candidates,
|
||||
);
|
||||
candidate_start
|
||||
} else {
|
||||
remainder_start
|
||||
}
|
||||
.map(|(branch, mut candidates)| {
|
||||
let candidate_start = self.cfg.start_new_block();
|
||||
self.match_candidates(
|
||||
span,
|
||||
scrutinee_span,
|
||||
candidate_start,
|
||||
remainder_start,
|
||||
&mut *candidates,
|
||||
);
|
||||
(branch, candidate_start)
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Perform the test, branching to one of N blocks.
|
||||
self.perform_test(span, scrutinee_span, start_block, &match_place, &test, target_blocks);
|
||||
self.perform_test(
|
||||
span,
|
||||
scrutinee_span,
|
||||
start_block,
|
||||
remainder_start,
|
||||
&match_place,
|
||||
&test,
|
||||
target_blocks,
|
||||
);
|
||||
}
|
||||
|
||||
/// Determine the fake borrows that are needed from a set of places that
|
||||
|
@ -6,13 +6,11 @@
|
||||
// the candidates based on the result.
|
||||
|
||||
use crate::build::expr::as_place::PlaceBuilder;
|
||||
use crate::build::matches::{Candidate, MatchPair, Test, TestCase, TestKind};
|
||||
use crate::build::matches::{Candidate, MatchPair, Test, TestBranch, TestCase, TestKind};
|
||||
use crate::build::Builder;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_hir::{LangItem, RangeEnd};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::*;
|
||||
use rustc_middle::ty::util::IntTypeExt;
|
||||
use rustc_middle::ty::GenericArg;
|
||||
use rustc_middle::ty::{self, adjustment::PointerCoercion, Ty, TyCtxt};
|
||||
@ -20,7 +18,6 @@ use rustc_span::def_id::DefId;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_target::abi::VariantIdx;
|
||||
|
||||
use std::cmp::Ordering;
|
||||
|
||||
@ -30,22 +27,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
/// It is a bug to call this with a not-fully-simplified pattern.
|
||||
pub(super) fn test<'pat>(&mut self, match_pair: &MatchPair<'pat, 'tcx>) -> Test<'tcx> {
|
||||
let kind = match match_pair.test_case {
|
||||
TestCase::Variant { adt_def, variant_index: _ } => {
|
||||
TestKind::Switch { adt_def, variants: BitSet::new_empty(adt_def.variants().len()) }
|
||||
}
|
||||
TestCase::Variant { adt_def, variant_index: _ } => TestKind::Switch { adt_def },
|
||||
|
||||
TestCase::Constant { .. } if match_pair.pattern.ty.is_bool() => TestKind::If,
|
||||
|
||||
TestCase::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => {
|
||||
// For integers, we use a `SwitchInt` match, which allows
|
||||
// us to handle more cases.
|
||||
TestKind::SwitchInt {
|
||||
// these maps are empty to start; cases are
|
||||
// added below in add_cases_to_switch
|
||||
options: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
TestCase::Constant { .. } if is_switch_ty(match_pair.pattern.ty) => TestKind::SwitchInt,
|
||||
TestCase::Constant { value } => TestKind::Eq { value, ty: match_pair.pattern.ty },
|
||||
|
||||
TestCase::Range(range) => {
|
||||
@ -70,101 +55,38 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
Test { span: match_pair.pattern.span, kind }
|
||||
}
|
||||
|
||||
pub(super) fn add_cases_to_switch<'pat>(
|
||||
&mut self,
|
||||
test_place: &PlaceBuilder<'tcx>,
|
||||
candidate: &Candidate<'pat, 'tcx>,
|
||||
options: &mut FxIndexMap<Const<'tcx>, u128>,
|
||||
) -> bool {
|
||||
let Some(match_pair) = candidate.match_pairs.iter().find(|mp| mp.place == *test_place)
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
|
||||
match match_pair.test_case {
|
||||
TestCase::Constant { value } => {
|
||||
options.entry(value).or_insert_with(|| value.eval_bits(self.tcx, self.param_env));
|
||||
true
|
||||
}
|
||||
TestCase::Variant { .. } => {
|
||||
panic!("you should have called add_variants_to_switch instead!");
|
||||
}
|
||||
TestCase::Range(ref range) => {
|
||||
// Check that none of the switch values are in the range.
|
||||
self.values_not_contained_in_range(&*range, options).unwrap_or(false)
|
||||
}
|
||||
// don't know how to add these patterns to a switch
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn add_variants_to_switch<'pat>(
|
||||
&mut self,
|
||||
test_place: &PlaceBuilder<'tcx>,
|
||||
candidate: &Candidate<'pat, 'tcx>,
|
||||
variants: &mut BitSet<VariantIdx>,
|
||||
) -> bool {
|
||||
let Some(match_pair) = candidate.match_pairs.iter().find(|mp| mp.place == *test_place)
|
||||
else {
|
||||
return false;
|
||||
};
|
||||
|
||||
match match_pair.test_case {
|
||||
TestCase::Variant { variant_index, .. } => {
|
||||
// We have a pattern testing for variant `variant_index`
|
||||
// set the corresponding index to true
|
||||
variants.insert(variant_index);
|
||||
true
|
||||
}
|
||||
// don't know how to add these patterns to a switch
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self, target_blocks, place_builder), level = "debug")]
|
||||
pub(super) fn perform_test(
|
||||
&mut self,
|
||||
match_start_span: Span,
|
||||
scrutinee_span: Span,
|
||||
block: BasicBlock,
|
||||
otherwise_block: BasicBlock,
|
||||
place_builder: &PlaceBuilder<'tcx>,
|
||||
test: &Test<'tcx>,
|
||||
target_blocks: Vec<BasicBlock>,
|
||||
target_blocks: FxIndexMap<TestBranch<'tcx>, BasicBlock>,
|
||||
) {
|
||||
let place = place_builder.to_place(self);
|
||||
let place_ty = place.ty(&self.local_decls, self.tcx);
|
||||
debug!(?place, ?place_ty,);
|
||||
debug!(?place, ?place_ty);
|
||||
let target_block = |branch| target_blocks.get(&branch).copied().unwrap_or(otherwise_block);
|
||||
|
||||
let source_info = self.source_info(test.span);
|
||||
match test.kind {
|
||||
TestKind::Switch { adt_def, ref variants } => {
|
||||
// Variants is a BitVec of indexes into adt_def.variants.
|
||||
let num_enum_variants = adt_def.variants().len();
|
||||
debug_assert_eq!(target_blocks.len(), num_enum_variants + 1);
|
||||
let otherwise_block = *target_blocks.last().unwrap();
|
||||
let tcx = self.tcx;
|
||||
TestKind::Switch { adt_def } => {
|
||||
let otherwise_block = target_block(TestBranch::Failure);
|
||||
let switch_targets = SwitchTargets::new(
|
||||
adt_def.discriminants(tcx).filter_map(|(idx, discr)| {
|
||||
if variants.contains(idx) {
|
||||
debug_assert_ne!(
|
||||
target_blocks[idx.index()],
|
||||
otherwise_block,
|
||||
"no candidates for tested discriminant: {discr:?}",
|
||||
);
|
||||
Some((discr.val, target_blocks[idx.index()]))
|
||||
adt_def.discriminants(self.tcx).filter_map(|(idx, discr)| {
|
||||
if let Some(&block) = target_blocks.get(&TestBranch::Variant(idx)) {
|
||||
Some((discr.val, block))
|
||||
} else {
|
||||
debug_assert_eq!(
|
||||
target_blocks[idx.index()],
|
||||
otherwise_block,
|
||||
"found candidates for untested discriminant: {discr:?}",
|
||||
);
|
||||
None
|
||||
}
|
||||
}),
|
||||
otherwise_block,
|
||||
);
|
||||
debug!("num_enum_variants: {}, variants: {:?}", num_enum_variants, variants);
|
||||
let discr_ty = adt_def.repr().discr_type().to_ty(tcx);
|
||||
debug!("num_enum_variants: {}", adt_def.variants().len());
|
||||
let discr_ty = adt_def.repr().discr_type().to_ty(self.tcx);
|
||||
let discr = self.temp(discr_ty, test.span);
|
||||
self.cfg.push_assign(
|
||||
block,
|
||||
@ -182,12 +104,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
);
|
||||
}
|
||||
|
||||
TestKind::SwitchInt { ref options } => {
|
||||
TestKind::SwitchInt => {
|
||||
// The switch may be inexhaustive so we have a catch-all block
|
||||
debug_assert_eq!(options.len() + 1, target_blocks.len());
|
||||
let otherwise_block = *target_blocks.last().unwrap();
|
||||
let otherwise_block = target_block(TestBranch::Failure);
|
||||
let switch_targets = SwitchTargets::new(
|
||||
options.values().copied().zip(target_blocks),
|
||||
target_blocks.iter().filter_map(|(&branch, &block)| {
|
||||
if let TestBranch::Constant(_, bits) = branch {
|
||||
Some((bits, block))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}),
|
||||
otherwise_block,
|
||||
);
|
||||
let terminator = TerminatorKind::SwitchInt {
|
||||
@ -198,18 +125,17 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
|
||||
TestKind::If => {
|
||||
let [false_bb, true_bb] = *target_blocks else {
|
||||
bug!("`TestKind::If` should have two targets")
|
||||
};
|
||||
let terminator = TerminatorKind::if_(Operand::Copy(place), true_bb, false_bb);
|
||||
let success_block = target_block(TestBranch::Success);
|
||||
let fail_block = target_block(TestBranch::Failure);
|
||||
let terminator =
|
||||
TerminatorKind::if_(Operand::Copy(place), success_block, fail_block);
|
||||
self.cfg.terminate(block, self.source_info(match_start_span), terminator);
|
||||
}
|
||||
|
||||
TestKind::Eq { value, ty } => {
|
||||
let tcx = self.tcx;
|
||||
let [success_block, fail_block] = *target_blocks else {
|
||||
bug!("`TestKind::Eq` should have two target blocks")
|
||||
};
|
||||
let success_block = target_block(TestBranch::Success);
|
||||
let fail_block = target_block(TestBranch::Failure);
|
||||
if let ty::Adt(def, _) = ty.kind()
|
||||
&& Some(def.did()) == tcx.lang_items().string()
|
||||
{
|
||||
@ -286,9 +212,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
|
||||
TestKind::Range(ref range) => {
|
||||
let [success, fail] = *target_blocks else {
|
||||
bug!("`TestKind::Range` should have two target blocks");
|
||||
};
|
||||
let success = target_block(TestBranch::Success);
|
||||
let fail = target_block(TestBranch::Failure);
|
||||
// Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
|
||||
let val = Operand::Copy(place);
|
||||
|
||||
@ -333,15 +258,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
// expected = <N>
|
||||
let expected = self.push_usize(block, source_info, len);
|
||||
|
||||
let [true_bb, false_bb] = *target_blocks else {
|
||||
bug!("`TestKind::Len` should have two target blocks");
|
||||
};
|
||||
let success_block = target_block(TestBranch::Success);
|
||||
let fail_block = target_block(TestBranch::Failure);
|
||||
// result = actual == expected OR result = actual < expected
|
||||
// branch based on result
|
||||
self.compare(
|
||||
block,
|
||||
true_bb,
|
||||
false_bb,
|
||||
success_block,
|
||||
fail_block,
|
||||
source_info,
|
||||
op,
|
||||
Operand::Move(actual),
|
||||
@ -526,10 +450,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
|
||||
/// Given that we are performing `test` against `test_place`, this job
|
||||
/// sorts out what the status of `candidate` will be after the test. See
|
||||
/// `test_candidates` for the usage of this function. The returned index is
|
||||
/// the index that this candidate should be placed in the
|
||||
/// `target_candidates` vec. The candidate may be modified to update its
|
||||
/// `match_pairs`.
|
||||
/// `test_candidates` for the usage of this function. The candidate may
|
||||
/// be modified to update its `match_pairs`.
|
||||
///
|
||||
/// So, for example, if this candidate is `x @ Some(P0)` and the `Test` is
|
||||
/// a variant test, then we would modify the candidate to be `(x as
|
||||
@ -551,12 +473,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
/// that it *doesn't* apply. For now, we return false, indicate that the
|
||||
/// test does not apply to this candidate, but it might be we can get
|
||||
/// tighter match code if we do something a bit different.
|
||||
pub(super) fn sort_candidate<'pat>(
|
||||
pub(super) fn sort_candidate(
|
||||
&mut self,
|
||||
test_place: &PlaceBuilder<'tcx>,
|
||||
test: &Test<'tcx>,
|
||||
candidate: &mut Candidate<'pat, 'tcx>,
|
||||
) -> Option<usize> {
|
||||
candidate: &mut Candidate<'_, 'tcx>,
|
||||
sorted_candidates: &FxIndexMap<TestBranch<'tcx>, Vec<&mut Candidate<'_, 'tcx>>>,
|
||||
) -> Option<TestBranch<'tcx>> {
|
||||
// Find the match_pair for this place (if any). At present,
|
||||
// afaik, there can be at most one. (In the future, if we
|
||||
// adopted a more general `@` operator, there might be more
|
||||
@ -571,12 +494,12 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
// If we are performing a variant switch, then this
|
||||
// informs variant patterns, but nothing else.
|
||||
(
|
||||
&TestKind::Switch { adt_def: tested_adt_def, .. },
|
||||
&TestKind::Switch { adt_def: tested_adt_def },
|
||||
&TestCase::Variant { adt_def, variant_index },
|
||||
) => {
|
||||
assert_eq!(adt_def, tested_adt_def);
|
||||
fully_matched = true;
|
||||
Some(variant_index.as_usize())
|
||||
Some(TestBranch::Variant(variant_index))
|
||||
}
|
||||
|
||||
// If we are performing a switch over integers, then this informs integer
|
||||
@ -584,35 +507,54 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
//
|
||||
// FIXME(#29623) we could use PatKind::Range to rule
|
||||
// things out here, in some cases.
|
||||
(TestKind::SwitchInt { options }, TestCase::Constant { value })
|
||||
(TestKind::SwitchInt, &TestCase::Constant { value })
|
||||
if is_switch_ty(match_pair.pattern.ty) =>
|
||||
{
|
||||
fully_matched = true;
|
||||
let index = options.get_index_of(value).unwrap();
|
||||
Some(index)
|
||||
// Beware: there might be some ranges sorted into the failure case; we must not add
|
||||
// a success case that could be matched by one of these ranges.
|
||||
let is_covering_range = |test_case: &TestCase<'_, 'tcx>| {
|
||||
test_case.as_range().is_some_and(|range| {
|
||||
matches!(range.contains(value, self.tcx, self.param_env), None | Some(true))
|
||||
})
|
||||
};
|
||||
let is_conflicting_candidate = |candidate: &&mut Candidate<'_, 'tcx>| {
|
||||
candidate
|
||||
.match_pairs
|
||||
.iter()
|
||||
.any(|mp| mp.place == *test_place && is_covering_range(&mp.test_case))
|
||||
};
|
||||
if sorted_candidates
|
||||
.get(&TestBranch::Failure)
|
||||
.is_some_and(|candidates| candidates.iter().any(is_conflicting_candidate))
|
||||
{
|
||||
fully_matched = false;
|
||||
None
|
||||
} else {
|
||||
fully_matched = true;
|
||||
let bits = value.eval_bits(self.tcx, self.param_env);
|
||||
Some(TestBranch::Constant(value, bits))
|
||||
}
|
||||
}
|
||||
(TestKind::SwitchInt { options }, TestCase::Range(range)) => {
|
||||
(TestKind::SwitchInt, TestCase::Range(range)) => {
|
||||
fully_matched = false;
|
||||
let not_contained =
|
||||
self.values_not_contained_in_range(&*range, options).unwrap_or(false);
|
||||
sorted_candidates.keys().filter_map(|br| br.as_constant()).copied().all(
|
||||
|val| matches!(range.contains(val, self.tcx, self.param_env), Some(false)),
|
||||
);
|
||||
|
||||
not_contained.then(|| {
|
||||
// No switch values are contained in the pattern range,
|
||||
// so the pattern can be matched only if this test fails.
|
||||
options.len()
|
||||
TestBranch::Failure
|
||||
})
|
||||
}
|
||||
|
||||
(&TestKind::If, TestCase::Constant { value }) => {
|
||||
(TestKind::If, TestCase::Constant { value }) => {
|
||||
fully_matched = true;
|
||||
let value = value.try_eval_bool(self.tcx, self.param_env).unwrap_or_else(|| {
|
||||
span_bug!(test.span, "expected boolean value but got {value:?}")
|
||||
});
|
||||
Some(value as usize)
|
||||
}
|
||||
(&TestKind::If, _) => {
|
||||
fully_matched = false;
|
||||
None
|
||||
Some(if value { TestBranch::Success } else { TestBranch::Failure })
|
||||
}
|
||||
|
||||
(
|
||||
@ -624,14 +566,14 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
// on true, min_len = len = $actual_length,
|
||||
// on false, len != $actual_length
|
||||
fully_matched = true;
|
||||
Some(0)
|
||||
Some(TestBranch::Success)
|
||||
}
|
||||
(Ordering::Less, _) => {
|
||||
// test_len < pat_len. If $actual_len = test_len,
|
||||
// then $actual_len < pat_len and we don't have
|
||||
// enough elements.
|
||||
fully_matched = false;
|
||||
Some(1)
|
||||
Some(TestBranch::Failure)
|
||||
}
|
||||
(Ordering::Equal | Ordering::Greater, true) => {
|
||||
// This can match both if $actual_len = test_len >= pat_len,
|
||||
@ -643,7 +585,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
// test_len != pat_len, so if $actual_len = test_len, then
|
||||
// $actual_len != pat_len.
|
||||
fully_matched = false;
|
||||
Some(1)
|
||||
Some(TestBranch::Failure)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -657,20 +599,20 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
// $actual_len >= test_len = pat_len,
|
||||
// so we can match.
|
||||
fully_matched = true;
|
||||
Some(0)
|
||||
Some(TestBranch::Success)
|
||||
}
|
||||
(Ordering::Less, _) | (Ordering::Equal, false) => {
|
||||
// test_len <= pat_len. If $actual_len < test_len,
|
||||
// then it is also < pat_len, so the test passing is
|
||||
// necessary (but insufficient).
|
||||
fully_matched = false;
|
||||
Some(0)
|
||||
Some(TestBranch::Success)
|
||||
}
|
||||
(Ordering::Greater, false) => {
|
||||
// test_len > pat_len. If $actual_len >= test_len > pat_len,
|
||||
// then we know we won't have a match.
|
||||
fully_matched = false;
|
||||
Some(1)
|
||||
Some(TestBranch::Failure)
|
||||
}
|
||||
(Ordering::Greater, true) => {
|
||||
// test_len < pat_len, and is therefore less
|
||||
@ -684,12 +626,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
(TestKind::Range(test), &TestCase::Range(pat)) => {
|
||||
if test.as_ref() == pat {
|
||||
fully_matched = true;
|
||||
Some(0)
|
||||
Some(TestBranch::Success)
|
||||
} else {
|
||||
fully_matched = false;
|
||||
// If the testing range does not overlap with pattern range,
|
||||
// the pattern can be matched only if this test fails.
|
||||
if !test.overlaps(pat, self.tcx, self.param_env)? { Some(1) } else { None }
|
||||
if !test.overlaps(pat, self.tcx, self.param_env)? {
|
||||
Some(TestBranch::Failure)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
(TestKind::Range(range), &TestCase::Constant { value }) => {
|
||||
@ -697,7 +643,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
if !range.contains(value, self.tcx, self.param_env)? {
|
||||
// `value` is not contained in the testing range,
|
||||
// so `value` can be matched only if this test fails.
|
||||
Some(1)
|
||||
Some(TestBranch::Failure)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -708,12 +654,13 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
if test_val == case_val =>
|
||||
{
|
||||
fully_matched = true;
|
||||
Some(0)
|
||||
Some(TestBranch::Success)
|
||||
}
|
||||
|
||||
(
|
||||
TestKind::Switch { .. }
|
||||
| TestKind::SwitchInt { .. }
|
||||
| TestKind::If
|
||||
| TestKind::Len { .. }
|
||||
| TestKind::Range { .. }
|
||||
| TestKind::Eq { .. },
|
||||
@ -734,36 +681,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
|
||||
ret
|
||||
}
|
||||
|
||||
fn values_not_contained_in_range(
|
||||
&self,
|
||||
range: &PatRange<'tcx>,
|
||||
options: &FxIndexMap<Const<'tcx>, u128>,
|
||||
) -> Option<bool> {
|
||||
for &val in options.keys() {
|
||||
if range.contains(val, self.tcx, self.param_env)? {
|
||||
return Some(false);
|
||||
}
|
||||
}
|
||||
|
||||
Some(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl Test<'_> {
|
||||
pub(super) fn targets(&self) -> usize {
|
||||
match self.kind {
|
||||
TestKind::Eq { .. } | TestKind::Range(_) | TestKind::Len { .. } | TestKind::If => 2,
|
||||
TestKind::Switch { adt_def, .. } => {
|
||||
// While the switch that we generate doesn't test for all
|
||||
// variants, we have a target for each variant and the
|
||||
// otherwise case, and we make sure that all of the cases not
|
||||
// specified have the same block.
|
||||
adt_def.variants().len() + 1
|
||||
}
|
||||
TestKind::SwitchInt { ref options } => options.len() + 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_switch_ty(ty: Ty<'_>) -> bool {
|
||||
|
@ -420,9 +420,9 @@ impl<'p, 'tcx> MatchVisitor<'p, 'tcx> {
|
||||
{
|
||||
let mut redundant_subpats = redundant_subpats.clone();
|
||||
// Emit lints in the order in which they occur in the file.
|
||||
redundant_subpats.sort_unstable_by_key(|pat| pat.data().unwrap().span);
|
||||
redundant_subpats.sort_unstable_by_key(|pat| pat.data().span);
|
||||
for pat in redundant_subpats {
|
||||
report_unreachable_pattern(cx, arm.arm_data, pat.data().unwrap().span, None)
|
||||
report_unreachable_pattern(cx, arm.arm_data, pat.data().span, None)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -905,10 +905,10 @@ fn report_arm_reachability<'p, 'tcx>(
|
||||
let mut catchall = None;
|
||||
for (arm, is_useful) in report.arm_usefulness.iter() {
|
||||
if matches!(is_useful, Usefulness::Redundant) {
|
||||
report_unreachable_pattern(cx, arm.arm_data, arm.pat.data().unwrap().span, catchall)
|
||||
report_unreachable_pattern(cx, arm.arm_data, arm.pat.data().span, catchall)
|
||||
}
|
||||
if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
|
||||
catchall = Some(arm.pat.data().unwrap().span);
|
||||
catchall = Some(arm.pat.data().span);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -917,7 +917,9 @@ fn report_arm_reachability<'p, 'tcx>(
|
||||
fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
|
||||
match pat.ctor() {
|
||||
Constructor::Wildcard => true,
|
||||
Constructor::Struct | Constructor::Ref => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
|
||||
Constructor::Struct | Constructor::Ref => {
|
||||
pat.iter_fields().all(|ipat| pat_is_catchall(&ipat.pat))
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -175,9 +175,7 @@ where
|
||||
}
|
||||
|
||||
// Mark one CGU for dead code, if necessary.
|
||||
let instrument_dead_code =
|
||||
tcx.sess.instrument_coverage() && !tcx.sess.instrument_coverage_except_unused_functions();
|
||||
if instrument_dead_code {
|
||||
if tcx.sess.instrument_coverage() {
|
||||
mark_code_coverage_dead_code_cgu(&mut codegen_units);
|
||||
}
|
||||
|
||||
|
@ -449,9 +449,13 @@ impl<'a> Parser<'a> {
|
||||
prev_token_before_parsing: Token,
|
||||
error: &mut Diag<'_>,
|
||||
) {
|
||||
if ((style == PathStyle::Expr && self.parse_paren_comma_seq(|p| p.parse_expr()).is_ok())
|
||||
|| (style == PathStyle::Pat
|
||||
&& self
|
||||
match style {
|
||||
PathStyle::Expr
|
||||
if let Ok(_) = self
|
||||
.parse_paren_comma_seq(|p| p.parse_expr())
|
||||
.map_err(|error| error.cancel()) => {}
|
||||
PathStyle::Pat
|
||||
if let Ok(_) = self
|
||||
.parse_paren_comma_seq(|p| {
|
||||
p.parse_pat_allow_top_alt(
|
||||
None,
|
||||
@ -460,25 +464,31 @@ impl<'a> Parser<'a> {
|
||||
CommaRecoveryMode::LikelyTuple,
|
||||
)
|
||||
})
|
||||
.is_ok()))
|
||||
&& !matches!(self.token.kind, token::ModSep | token::RArrow)
|
||||
{
|
||||
error.span_suggestion_verbose(
|
||||
prev_token_before_parsing.span,
|
||||
format!(
|
||||
"consider removing the `::` here to {}",
|
||||
match style {
|
||||
PathStyle::Expr => "call the expression",
|
||||
PathStyle::Pat => "turn this into a tuple struct pattern",
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
),
|
||||
"",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
.map_err(|error| error.cancel()) => {}
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if let token::ModSep | token::RArrow = self.token.kind {
|
||||
return;
|
||||
}
|
||||
|
||||
error.span_suggestion_verbose(
|
||||
prev_token_before_parsing.span,
|
||||
format!(
|
||||
"consider removing the `::` here to {}",
|
||||
match style {
|
||||
PathStyle::Expr => "call the expression",
|
||||
PathStyle::Pat => "turn this into a tuple struct pattern",
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
}
|
||||
),
|
||||
"",
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
||||
/// Parses generic args (within a path segment) with recovery for extra leading angle brackets.
|
||||
|
@ -423,7 +423,7 @@ pub enum SliceKind {
|
||||
}
|
||||
|
||||
impl SliceKind {
|
||||
fn arity(self) -> usize {
|
||||
pub fn arity(self) -> usize {
|
||||
match self {
|
||||
FixedLen(length) => length,
|
||||
VarLen(prefix, suffix) => prefix + suffix,
|
||||
@ -462,7 +462,7 @@ impl Slice {
|
||||
Slice { array_len, kind }
|
||||
}
|
||||
|
||||
pub(crate) fn arity(self) -> usize {
|
||||
pub fn arity(self) -> usize {
|
||||
self.kind.arity()
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
|
||||
};
|
||||
|
||||
use rustc_errors::LintDiagnostic;
|
||||
let mut err = rcx.tcx.dcx().struct_span_warn(arm.pat.data().unwrap().span, "");
|
||||
let mut err = rcx.tcx.dcx().struct_span_warn(arm.pat.data().span, "");
|
||||
err.primary_message(decorator.msg());
|
||||
decorator.decorate_lint(&mut err);
|
||||
err.emit();
|
||||
|
@ -20,32 +20,42 @@ impl PatId {
|
||||
}
|
||||
}
|
||||
|
||||
/// A pattern with an index denoting which field it corresponds to.
|
||||
pub struct IndexedPat<Cx: TypeCx> {
|
||||
pub idx: usize,
|
||||
pub pat: DeconstructedPat<Cx>,
|
||||
}
|
||||
|
||||
/// Values and patterns can be represented as a constructor applied to some fields. This represents
|
||||
/// a pattern in this form. A `DeconstructedPat` will almost always come from user input; the only
|
||||
/// exception are some `Wildcard`s introduced during pattern lowering.
|
||||
pub struct DeconstructedPat<Cx: TypeCx> {
|
||||
ctor: Constructor<Cx>,
|
||||
fields: Vec<DeconstructedPat<Cx>>,
|
||||
fields: Vec<IndexedPat<Cx>>,
|
||||
/// The number of fields in this pattern. E.g. if the pattern is `SomeStruct { field12: true, ..
|
||||
/// }` this would be the total number of fields of the struct.
|
||||
/// This is also the same as `self.ctor.arity(self.ty)`.
|
||||
arity: usize,
|
||||
ty: Cx::Ty,
|
||||
/// Extra data to store in a pattern. `None` if the pattern is a wildcard that does not
|
||||
/// correspond to a user-supplied pattern.
|
||||
data: Option<Cx::PatData>,
|
||||
/// Extra data to store in a pattern.
|
||||
data: Cx::PatData,
|
||||
/// Globally-unique id used to track usefulness at the level of subpatterns.
|
||||
pub(crate) uid: PatId,
|
||||
}
|
||||
|
||||
impl<Cx: TypeCx> DeconstructedPat<Cx> {
|
||||
pub fn wildcard(ty: Cx::Ty) -> Self {
|
||||
DeconstructedPat { ctor: Wildcard, fields: Vec::new(), ty, data: None, uid: PatId::new() }
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
ctor: Constructor<Cx>,
|
||||
fields: Vec<DeconstructedPat<Cx>>,
|
||||
fields: Vec<IndexedPat<Cx>>,
|
||||
arity: usize,
|
||||
ty: Cx::Ty,
|
||||
data: Cx::PatData,
|
||||
) -> Self {
|
||||
DeconstructedPat { ctor, fields, ty, data: Some(data), uid: PatId::new() }
|
||||
DeconstructedPat { ctor, fields, arity, ty, data, uid: PatId::new() }
|
||||
}
|
||||
|
||||
pub fn at_index(self, idx: usize) -> IndexedPat<Cx> {
|
||||
IndexedPat { idx, pat: self }
|
||||
}
|
||||
|
||||
pub(crate) fn is_or_pat(&self) -> bool {
|
||||
@ -58,13 +68,15 @@ impl<Cx: TypeCx> DeconstructedPat<Cx> {
|
||||
pub fn ty(&self) -> &Cx::Ty {
|
||||
&self.ty
|
||||
}
|
||||
/// Returns the extra data stored in a pattern. Returns `None` if the pattern is a wildcard that
|
||||
/// does not correspond to a user-supplied pattern.
|
||||
pub fn data(&self) -> Option<&Cx::PatData> {
|
||||
self.data.as_ref()
|
||||
/// Returns the extra data stored in a pattern.
|
||||
pub fn data(&self) -> &Cx::PatData {
|
||||
&self.data
|
||||
}
|
||||
pub fn arity(&self) -> usize {
|
||||
self.arity
|
||||
}
|
||||
|
||||
pub fn iter_fields<'a>(&'a self) -> impl Iterator<Item = &'a DeconstructedPat<Cx>> {
|
||||
pub fn iter_fields<'a>(&'a self) -> impl Iterator<Item = &'a IndexedPat<Cx>> {
|
||||
self.fields.iter()
|
||||
}
|
||||
|
||||
@ -73,36 +85,40 @@ impl<Cx: TypeCx> DeconstructedPat<Cx> {
|
||||
pub(crate) fn specialize<'a>(
|
||||
&'a self,
|
||||
other_ctor: &Constructor<Cx>,
|
||||
ctor_arity: usize,
|
||||
other_ctor_arity: usize,
|
||||
) -> SmallVec<[PatOrWild<'a, Cx>; 2]> {
|
||||
let wildcard_sub_tys = || (0..ctor_arity).map(|_| PatOrWild::Wild).collect();
|
||||
match (&self.ctor, other_ctor) {
|
||||
// Return a wildcard for each field of `other_ctor`.
|
||||
(Wildcard, _) => wildcard_sub_tys(),
|
||||
if matches!(other_ctor, PrivateUninhabited) {
|
||||
// Skip this column.
|
||||
(_, PrivateUninhabited) => smallvec![],
|
||||
// The only non-trivial case: two slices of different arity. `other_slice` is
|
||||
// guaranteed to have a larger arity, so we fill the middle part with enough
|
||||
// wildcards to reach the length of the new, larger slice.
|
||||
(
|
||||
&Slice(self_slice @ Slice { kind: SliceKind::VarLen(prefix, suffix), .. }),
|
||||
&Slice(other_slice),
|
||||
) if self_slice.arity() != other_slice.arity() => {
|
||||
// Start with a slice of wildcards of the appropriate length.
|
||||
let mut fields: SmallVec<[_; 2]> = wildcard_sub_tys();
|
||||
// Fill in the fields from both ends.
|
||||
let new_arity = fields.len();
|
||||
for i in 0..prefix {
|
||||
fields[i] = PatOrWild::Pat(&self.fields[i]);
|
||||
}
|
||||
for i in 0..suffix {
|
||||
fields[new_arity - 1 - i] =
|
||||
PatOrWild::Pat(&self.fields[self.fields.len() - 1 - i]);
|
||||
}
|
||||
fields
|
||||
}
|
||||
_ => self.fields.iter().map(PatOrWild::Pat).collect(),
|
||||
return smallvec![];
|
||||
}
|
||||
|
||||
// Start with a slice of wildcards of the appropriate length.
|
||||
let mut fields: SmallVec<[_; 2]> = (0..other_ctor_arity).map(|_| PatOrWild::Wild).collect();
|
||||
// Fill `fields` with our fields. The arities are known to be compatible.
|
||||
match self.ctor {
|
||||
// The only non-trivial case: two slices of different arity. `other_ctor` is guaranteed
|
||||
// to have a larger arity, so we adjust the indices of the patterns in the suffix so
|
||||
// that they are correctly positioned in the larger slice.
|
||||
Slice(Slice { kind: SliceKind::VarLen(prefix, _), .. })
|
||||
if self.arity != other_ctor_arity =>
|
||||
{
|
||||
for ipat in &self.fields {
|
||||
let new_idx = if ipat.idx < prefix {
|
||||
ipat.idx
|
||||
} else {
|
||||
// Adjust the indices in the suffix.
|
||||
ipat.idx + other_ctor_arity - self.arity
|
||||
};
|
||||
fields[new_idx] = PatOrWild::Pat(&ipat.pat);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
for ipat in &self.fields {
|
||||
fields[ipat.idx] = PatOrWild::Pat(&ipat.pat);
|
||||
}
|
||||
}
|
||||
}
|
||||
fields
|
||||
}
|
||||
|
||||
/// Walk top-down and call `it` in each place where a pattern occurs
|
||||
@ -114,7 +130,7 @@ impl<Cx: TypeCx> DeconstructedPat<Cx> {
|
||||
}
|
||||
|
||||
for p in self.iter_fields() {
|
||||
p.walk(it)
|
||||
p.pat.walk(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -134,6 +150,11 @@ impl<Cx: TypeCx> fmt::Debug for DeconstructedPat<Cx> {
|
||||
};
|
||||
let mut start_or_comma = || start_or_continue(", ");
|
||||
|
||||
let mut fields: Vec<_> = (0..self.arity).map(|_| PatOrWild::Wild).collect();
|
||||
for ipat in self.iter_fields() {
|
||||
fields[ipat.idx] = PatOrWild::Pat(&ipat.pat);
|
||||
}
|
||||
|
||||
match pat.ctor() {
|
||||
Struct | Variant(_) | UnionField => {
|
||||
Cx::write_variant_name(f, pat)?;
|
||||
@ -141,7 +162,7 @@ impl<Cx: TypeCx> fmt::Debug for DeconstructedPat<Cx> {
|
||||
// get the names of the fields. Instead we just display everything as a tuple
|
||||
// struct, which should be good enough.
|
||||
write!(f, "(")?;
|
||||
for p in pat.iter_fields() {
|
||||
for p in fields {
|
||||
write!(f, "{}", start_or_comma())?;
|
||||
write!(f, "{p:?}")?;
|
||||
}
|
||||
@ -151,25 +172,23 @@ impl<Cx: TypeCx> fmt::Debug for DeconstructedPat<Cx> {
|
||||
// be careful to detect strings here. However a string literal pattern will never
|
||||
// be reported as a non-exhaustiveness witness, so we can ignore this issue.
|
||||
Ref => {
|
||||
let subpattern = pat.iter_fields().next().unwrap();
|
||||
write!(f, "&{:?}", subpattern)
|
||||
write!(f, "&{:?}", &fields[0])
|
||||
}
|
||||
Slice(slice) => {
|
||||
let mut subpatterns = pat.iter_fields();
|
||||
write!(f, "[")?;
|
||||
match slice.kind {
|
||||
SliceKind::FixedLen(_) => {
|
||||
for p in subpatterns {
|
||||
for p in fields {
|
||||
write!(f, "{}{:?}", start_or_comma(), p)?;
|
||||
}
|
||||
}
|
||||
SliceKind::VarLen(prefix_len, _) => {
|
||||
for p in subpatterns.by_ref().take(prefix_len) {
|
||||
for p in &fields[..prefix_len] {
|
||||
write!(f, "{}{:?}", start_or_comma(), p)?;
|
||||
}
|
||||
write!(f, "{}", start_or_comma())?;
|
||||
write!(f, "..")?;
|
||||
for p in subpatterns {
|
||||
for p in &fields[prefix_len..] {
|
||||
write!(f, "{}{:?}", start_or_comma(), p)?;
|
||||
}
|
||||
}
|
||||
@ -184,7 +203,7 @@ impl<Cx: TypeCx> fmt::Debug for DeconstructedPat<Cx> {
|
||||
Str(value) => write!(f, "{value:?}"),
|
||||
Opaque(..) => write!(f, "<constant pattern>"),
|
||||
Or => {
|
||||
for pat in pat.iter_fields() {
|
||||
for pat in fields {
|
||||
write!(f, "{}{:?}", start_or_continue(" | "), pat)?;
|
||||
}
|
||||
Ok(())
|
||||
@ -242,9 +261,10 @@ impl<'p, Cx: TypeCx> PatOrWild<'p, Cx> {
|
||||
/// Expand this (possibly-nested) or-pattern into its alternatives.
|
||||
pub(crate) fn flatten_or_pat(self) -> SmallVec<[Self; 1]> {
|
||||
match self {
|
||||
PatOrWild::Pat(pat) if pat.is_or_pat() => {
|
||||
pat.iter_fields().flat_map(|p| PatOrWild::Pat(p).flatten_or_pat()).collect()
|
||||
}
|
||||
PatOrWild::Pat(pat) if pat.is_or_pat() => pat
|
||||
.iter_fields()
|
||||
.flat_map(|ipat| PatOrWild::Pat(&ipat.pat).flatten_or_pat())
|
||||
.collect(),
|
||||
_ => smallvec![self],
|
||||
}
|
||||
}
|
||||
|
@ -445,7 +445,8 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
let cx = self;
|
||||
let ty = cx.reveal_opaque_ty(pat.ty);
|
||||
let ctor;
|
||||
let mut fields: Vec<_>;
|
||||
let arity;
|
||||
let fields: Vec<_>;
|
||||
match &pat.kind {
|
||||
PatKind::AscribeUserType { subpattern, .. }
|
||||
| PatKind::InlineConstant { subpattern, .. } => return self.lower_pat(subpattern),
|
||||
@ -453,9 +454,11 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
PatKind::Binding { subpattern: None, .. } | PatKind::Wild => {
|
||||
ctor = Wildcard;
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
PatKind::Deref { subpattern } => {
|
||||
fields = vec![self.lower_pat(subpattern)];
|
||||
fields = vec![self.lower_pat(subpattern).at_index(0)];
|
||||
arity = 1;
|
||||
ctor = match ty.kind() {
|
||||
// This is a box pattern.
|
||||
ty::Adt(adt, ..) if adt.is_box() => Struct,
|
||||
@ -467,16 +470,13 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
match ty.kind() {
|
||||
ty::Tuple(fs) => {
|
||||
ctor = Struct;
|
||||
fields = fs
|
||||
arity = fs.len();
|
||||
fields = subpatterns
|
||||
.iter()
|
||||
.map(|ty| cx.reveal_opaque_ty(ty))
|
||||
.map(|ty| DeconstructedPat::wildcard(ty))
|
||||
.map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
|
||||
.collect();
|
||||
for pat in subpatterns {
|
||||
fields[pat.field.index()] = self.lower_pat(&pat.pattern);
|
||||
}
|
||||
}
|
||||
ty::Adt(adt, args) if adt.is_box() => {
|
||||
ty::Adt(adt, _) if adt.is_box() => {
|
||||
// The only legal patterns of type `Box` (outside `std`) are `_` and box
|
||||
// patterns. If we're here we can assume this is a box pattern.
|
||||
// FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_,
|
||||
@ -490,13 +490,13 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
// solution when we introduce generalized deref patterns. Also need to
|
||||
// prevent mixing of those two options.
|
||||
let pattern = subpatterns.into_iter().find(|pat| pat.field.index() == 0);
|
||||
let pat = if let Some(pat) = pattern {
|
||||
self.lower_pat(&pat.pattern)
|
||||
if let Some(pat) = pattern {
|
||||
fields = vec![self.lower_pat(&pat.pattern).at_index(0)];
|
||||
} else {
|
||||
DeconstructedPat::wildcard(self.reveal_opaque_ty(args.type_at(0)))
|
||||
};
|
||||
fields = vec![];
|
||||
}
|
||||
ctor = Struct;
|
||||
fields = vec![pat];
|
||||
arity = 1;
|
||||
}
|
||||
ty::Adt(adt, _) => {
|
||||
ctor = match pat.kind {
|
||||
@ -507,13 +507,11 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
};
|
||||
let variant =
|
||||
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
|
||||
fields = cx
|
||||
.variant_sub_tys(ty, variant)
|
||||
.map(|(_, ty)| DeconstructedPat::wildcard(ty))
|
||||
arity = variant.fields.len();
|
||||
fields = subpatterns
|
||||
.iter()
|
||||
.map(|ipat| self.lower_pat(&ipat.pattern).at_index(ipat.field.index()))
|
||||
.collect();
|
||||
for pat in subpatterns {
|
||||
fields[pat.field.index()] = self.lower_pat(&pat.pattern);
|
||||
}
|
||||
}
|
||||
_ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, ty),
|
||||
}
|
||||
@ -526,6 +524,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
None => Opaque(OpaqueId::new()),
|
||||
};
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
ty::Char | ty::Int(_) | ty::Uint(_) => {
|
||||
ctor = match value.try_eval_bits(cx.tcx, cx.param_env) {
|
||||
@ -542,6 +541,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
None => Opaque(OpaqueId::new()),
|
||||
};
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
ty::Float(ty::FloatTy::F32) => {
|
||||
ctor = match value.try_eval_bits(cx.tcx, cx.param_env) {
|
||||
@ -553,6 +553,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
None => Opaque(OpaqueId::new()),
|
||||
};
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
ty::Float(ty::FloatTy::F64) => {
|
||||
ctor = match value.try_eval_bits(cx.tcx, cx.param_env) {
|
||||
@ -564,6 +565,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
None => Opaque(OpaqueId::new()),
|
||||
};
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
ty::Ref(_, t, _) if t.is_str() => {
|
||||
// We want a `&str` constant to behave like a `Deref` pattern, to be compatible
|
||||
@ -574,9 +576,10 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
// subfields.
|
||||
// Note: `t` is `str`, not `&str`.
|
||||
let ty = self.reveal_opaque_ty(*t);
|
||||
let subpattern = DeconstructedPat::new(Str(*value), Vec::new(), ty, pat);
|
||||
let subpattern = DeconstructedPat::new(Str(*value), Vec::new(), 0, ty, pat);
|
||||
ctor = Ref;
|
||||
fields = vec![subpattern]
|
||||
fields = vec![subpattern.at_index(0)];
|
||||
arity = 1;
|
||||
}
|
||||
// All constants that can be structurally matched have already been expanded
|
||||
// into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
|
||||
@ -584,6 +587,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
_ => {
|
||||
ctor = Opaque(OpaqueId::new());
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -623,6 +627,7 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
_ => bug!("invalid type for range pattern: {}", ty.inner()),
|
||||
};
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
PatKind::Array { prefix, slice, suffix } | PatKind::Slice { prefix, slice, suffix } => {
|
||||
let array_len = match ty.kind() {
|
||||
@ -638,12 +643,25 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
SliceKind::FixedLen(prefix.len() + suffix.len())
|
||||
};
|
||||
ctor = Slice(Slice::new(array_len, kind));
|
||||
fields = prefix.iter().chain(suffix.iter()).map(|p| self.lower_pat(&*p)).collect();
|
||||
fields = prefix
|
||||
.iter()
|
||||
.chain(suffix.iter())
|
||||
.map(|p| self.lower_pat(&*p))
|
||||
.enumerate()
|
||||
.map(|(i, p)| p.at_index(i))
|
||||
.collect();
|
||||
arity = kind.arity();
|
||||
}
|
||||
PatKind::Or { .. } => {
|
||||
ctor = Or;
|
||||
let pats = expand_or_pat(pat);
|
||||
fields = pats.into_iter().map(|p| self.lower_pat(p)).collect();
|
||||
fields = pats
|
||||
.into_iter()
|
||||
.map(|p| self.lower_pat(p))
|
||||
.enumerate()
|
||||
.map(|(i, p)| p.at_index(i))
|
||||
.collect();
|
||||
arity = fields.len();
|
||||
}
|
||||
PatKind::Never => {
|
||||
// A never pattern matches all the values of its type (namely none). Moreover it
|
||||
@ -651,13 +669,15 @@ impl<'p, 'tcx: 'p> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
// `Result<!, !>` which has other constructors. Hence we lower it as a wildcard.
|
||||
ctor = Wildcard;
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
PatKind::Error(_) => {
|
||||
ctor = Opaque(OpaqueId::new());
|
||||
fields = vec![];
|
||||
arity = 0;
|
||||
}
|
||||
}
|
||||
DeconstructedPat::new(ctor, fields, ty, pat)
|
||||
DeconstructedPat::new(ctor, fields, arity, ty, pat)
|
||||
}
|
||||
|
||||
/// Convert back to a `thir::PatRangeBoundary` for diagnostic purposes.
|
||||
@ -884,10 +904,10 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
let overlap_as_pat = self.hoist_pat_range(&overlaps_on, *pat.ty());
|
||||
let overlaps: Vec<_> = overlaps_with
|
||||
.iter()
|
||||
.map(|pat| pat.data().unwrap().span)
|
||||
.map(|pat| pat.data().span)
|
||||
.map(|span| errors::Overlap { range: overlap_as_pat.clone(), span })
|
||||
.collect();
|
||||
let pat_span = pat.data().unwrap().span;
|
||||
let pat_span = pat.data().span;
|
||||
self.tcx.emit_node_span_lint(
|
||||
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
|
||||
self.match_lint_level,
|
||||
@ -907,7 +927,7 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
gap: IntRange,
|
||||
gapped_with: &[&crate::pat::DeconstructedPat<Self>],
|
||||
) {
|
||||
let Some(&thir_pat) = pat.data() else { return };
|
||||
let &thir_pat = pat.data();
|
||||
let thir::PatKind::Range(range) = &thir_pat.kind else { return };
|
||||
// Only lint when the left range is an exclusive range.
|
||||
if range.end != rustc_hir::RangeEnd::Excluded {
|
||||
@ -955,7 +975,7 @@ impl<'p, 'tcx: 'p> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
gap_with: gapped_with
|
||||
.iter()
|
||||
.map(|pat| errors::GappedRange {
|
||||
span: pat.data().unwrap().span,
|
||||
span: pat.data().span,
|
||||
gap: gap_as_pat.clone(),
|
||||
first_range: thir_pat.clone(),
|
||||
})
|
||||
|
@ -1006,15 +1006,17 @@ impl<'p, Cx: TypeCx> PatStack<'p, Cx> {
|
||||
ctor_arity: usize,
|
||||
ctor_is_relevant: bool,
|
||||
) -> Result<PatStack<'p, Cx>, Cx::Error> {
|
||||
// We pop the head pattern and push the new fields extracted from the arguments of
|
||||
// `self.head()`.
|
||||
let mut new_pats = self.head().specialize(ctor, ctor_arity);
|
||||
if new_pats.len() != ctor_arity {
|
||||
let head_pat = self.head();
|
||||
if head_pat.as_pat().is_some_and(|pat| pat.arity() > ctor_arity) {
|
||||
// Arity can be smaller in case of variable-length slices, but mustn't be larger.
|
||||
return Err(cx.bug(format_args!(
|
||||
"uncaught type error: pattern {:?} has inconsistent arity (expected arity {ctor_arity})",
|
||||
self.head().as_pat().unwrap()
|
||||
"uncaught type error: pattern {:?} has inconsistent arity (expected arity <= {ctor_arity})",
|
||||
head_pat.as_pat().unwrap()
|
||||
)));
|
||||
}
|
||||
// We pop the head pattern and push the new fields extracted from the arguments of
|
||||
// `self.head()`.
|
||||
let mut new_pats = head_pat.specialize(ctor, ctor_arity);
|
||||
new_pats.extend_from_slice(&self.pats[1..]);
|
||||
// `ctor` is relevant for this row if it is the actual constructor of this row, or if the
|
||||
// row has a wildcard and `ctor` is relevant for wildcards.
|
||||
@ -1706,7 +1708,8 @@ fn collect_pattern_usefulness<'p, Cx: TypeCx>(
|
||||
) -> bool {
|
||||
if useful_subpatterns.contains(&pat.uid) {
|
||||
true
|
||||
} else if pat.is_or_pat() && pat.iter_fields().any(|f| pat_is_useful(useful_subpatterns, f))
|
||||
} else if pat.is_or_pat()
|
||||
&& pat.iter_fields().any(|f| pat_is_useful(useful_subpatterns, &f.pat))
|
||||
{
|
||||
// We always expand or patterns in the matrix, so we will never see the actual
|
||||
// or-pattern (the one with constructor `Or`) in the column. As such, it will not be
|
||||
|
@ -134,31 +134,26 @@ pub enum LtoCli {
|
||||
/// and higher). Nevertheless, there are many variables, depending on options
|
||||
/// selected, code structure, and enabled attributes. If errors are encountered,
|
||||
/// either while compiling or when generating `llvm-cov show` reports, consider
|
||||
/// lowering the optimization level, including or excluding `-C link-dead-code`,
|
||||
/// or using `-Zunstable-options -C instrument-coverage=except-unused-functions`
|
||||
/// or `-Zunstable-options -C instrument-coverage=except-unused-generics`.
|
||||
///
|
||||
/// Note that `ExceptUnusedFunctions` means: When `mapgen.rs` generates the
|
||||
/// coverage map, it will not attempt to generate synthetic functions for unused
|
||||
/// (and not code-generated) functions (whether they are generic or not). As a
|
||||
/// result, non-codegenned functions will not be included in the coverage map,
|
||||
/// and will not appear, as covered or uncovered, in coverage reports.
|
||||
///
|
||||
/// `ExceptUnusedGenerics` will add synthetic functions to the coverage map,
|
||||
/// unless the function has type parameters.
|
||||
/// lowering the optimization level, or including/excluding `-C link-dead-code`.
|
||||
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
|
||||
pub enum InstrumentCoverage {
|
||||
/// `-C instrument-coverage=no` (or `off`, `false` etc.)
|
||||
No,
|
||||
/// `-C instrument-coverage` or `-C instrument-coverage=yes`
|
||||
Yes,
|
||||
/// Additionally, instrument branches and output branch coverage.
|
||||
/// `-Zunstable-options -C instrument-coverage=branch`
|
||||
Branch,
|
||||
/// `-Zunstable-options -C instrument-coverage=except-unused-generics`
|
||||
ExceptUnusedGenerics,
|
||||
/// `-Zunstable-options -C instrument-coverage=except-unused-functions`
|
||||
ExceptUnusedFunctions,
|
||||
}
|
||||
|
||||
/// Individual flag values controlled by `-Z coverage-options`.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct CoverageOptions {
|
||||
/// Add branch coverage instrumentation (placeholder flag; not yet implemented).
|
||||
pub branch: bool,
|
||||
}
|
||||
|
||||
impl Default for CoverageOptions {
|
||||
fn default() -> Self {
|
||||
Self { branch: false }
|
||||
}
|
||||
}
|
||||
|
||||
/// Settings for `-Z instrument-xray` flag.
|
||||
@ -2718,24 +2713,6 @@ pub fn build_session_options(early_dcx: &mut EarlyDiagCtxt, matches: &getopts::M
|
||||
}
|
||||
}
|
||||
|
||||
// Check for unstable values of `-C instrument-coverage`.
|
||||
// This is what prevents them from being used on stable compilers.
|
||||
match cg.instrument_coverage {
|
||||
// Stable values:
|
||||
InstrumentCoverage::Yes | InstrumentCoverage::No => {}
|
||||
// Unstable values:
|
||||
InstrumentCoverage::Branch
|
||||
| InstrumentCoverage::ExceptUnusedFunctions
|
||||
| InstrumentCoverage::ExceptUnusedGenerics => {
|
||||
if !unstable_opts.unstable_options {
|
||||
early_dcx.early_fatal(
|
||||
"`-C instrument-coverage=branch` and `-C instrument-coverage=except-*` \
|
||||
require `-Z unstable-options`",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if cg.instrument_coverage != InstrumentCoverage::No {
|
||||
if cg.profile_generate.enabled() || cg.profile_use.is_some() {
|
||||
early_dcx.early_fatal(
|
||||
@ -3204,12 +3181,12 @@ pub enum WasiExecModel {
|
||||
/// how the hash should be calculated when adding a new command-line argument.
|
||||
pub(crate) mod dep_tracking {
|
||||
use super::{
|
||||
BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CrateType, DebugInfo,
|
||||
DebugInfoCompression, ErrorOutputType, FunctionReturn, InliningThreshold,
|
||||
InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail, LtoCli,
|
||||
NextSolverConfig, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes, Polonius,
|
||||
RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm, SplitDwarfKind,
|
||||
SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
|
||||
BranchProtection, CFGuard, CFProtection, CollapseMacroDebuginfo, CoverageOptions,
|
||||
CrateType, DebugInfo, DebugInfoCompression, ErrorOutputType, FunctionReturn,
|
||||
InliningThreshold, InstrumentCoverage, InstrumentXRay, LinkerPluginLto, LocationDetail,
|
||||
LtoCli, NextSolverConfig, OomStrategy, OptLevel, OutFileName, OutputType, OutputTypes,
|
||||
Polonius, RemapPathScopeComponents, ResolveDocLinks, SourceFileHashAlgorithm,
|
||||
SplitDwarfKind, SwitchWithOptPath, SymbolManglingVersion, WasiExecModel,
|
||||
};
|
||||
use crate::lint;
|
||||
use crate::utils::NativeLib;
|
||||
@ -3279,6 +3256,7 @@ pub(crate) mod dep_tracking {
|
||||
CodeModel,
|
||||
TlsModel,
|
||||
InstrumentCoverage,
|
||||
CoverageOptions,
|
||||
InstrumentXRay,
|
||||
CrateType,
|
||||
MergeFunctions,
|
||||
|
@ -395,7 +395,8 @@ mod desc {
|
||||
pub const parse_linker_flavor: &str = ::rustc_target::spec::LinkerFlavorCli::one_of();
|
||||
pub const parse_optimization_fuel: &str = "crate=integer";
|
||||
pub const parse_dump_mono_stats: &str = "`markdown` (default) or `json`";
|
||||
pub const parse_instrument_coverage: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc) or (unstable) one of `branch`, `except-unused-generics`, `except-unused-functions`";
|
||||
pub const parse_instrument_coverage: &str = parse_bool;
|
||||
pub const parse_coverage_options: &str = "`branch` or `no-branch`";
|
||||
pub const parse_instrument_xray: &str = "either a boolean (`yes`, `no`, `on`, `off`, etc), or a comma separated list of settings: `always` or `never` (mutually exclusive), `ignore-loops`, `instruction-threshold=N`, `skip-entry`, `skip-exit`";
|
||||
pub const parse_unpretty: &str = "`string` or `string=string`";
|
||||
pub const parse_treat_err_as_bug: &str = "either no value or a non-negative number";
|
||||
@ -928,21 +929,34 @@ mod parse {
|
||||
return true;
|
||||
};
|
||||
|
||||
// Parse values that have historically been accepted by stable compilers,
|
||||
// even though they're currently just aliases for boolean values.
|
||||
*slot = match v {
|
||||
"all" => InstrumentCoverage::Yes,
|
||||
"branch" => InstrumentCoverage::Branch,
|
||||
"except-unused-generics" | "except_unused_generics" => {
|
||||
InstrumentCoverage::ExceptUnusedGenerics
|
||||
}
|
||||
"except-unused-functions" | "except_unused_functions" => {
|
||||
InstrumentCoverage::ExceptUnusedFunctions
|
||||
}
|
||||
"0" => InstrumentCoverage::No,
|
||||
_ => return false,
|
||||
};
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn parse_coverage_options(slot: &mut CoverageOptions, v: Option<&str>) -> bool {
|
||||
let Some(v) = v else { return true };
|
||||
|
||||
for option in v.split(',') {
|
||||
let (option, enabled) = match option.strip_prefix("no-") {
|
||||
Some(without_no) => (without_no, false),
|
||||
None => (option, true),
|
||||
};
|
||||
let slot = match option {
|
||||
"branch" => &mut slot.branch,
|
||||
_ => return false,
|
||||
};
|
||||
*slot = enabled;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
pub(crate) fn parse_instrument_xray(
|
||||
slot: &mut Option<InstrumentXRay>,
|
||||
v: Option<&str>,
|
||||
@ -1445,14 +1459,9 @@ options! {
|
||||
"set the threshold for inlining a function"),
|
||||
#[rustc_lint_opt_deny_field_access("use `Session::instrument_coverage` instead of this field")]
|
||||
instrument_coverage: InstrumentCoverage = (InstrumentCoverage::No, parse_instrument_coverage, [TRACKED],
|
||||
"instrument the generated code to support LLVM source-based code coverage \
|
||||
reports (note, the compiler build config must include `profiler = true`); \
|
||||
implies `-C symbol-mangling-version=v0`. Optional values are:
|
||||
`=no` `=n` `=off` `=false` (default)
|
||||
`=yes` `=y` `=on` `=true` (implicit value)
|
||||
`=branch` (unstable)
|
||||
`=except-unused-generics` (unstable)
|
||||
`=except-unused-functions` (unstable)"),
|
||||
"instrument the generated code to support LLVM source-based code coverage reports \
|
||||
(note, the compiler build config must include `profiler = true`); \
|
||||
implies `-C symbol-mangling-version=v0`"),
|
||||
link_arg: (/* redirected to link_args */) = ((), parse_string_push, [UNTRACKED],
|
||||
"a single extra argument to append to the linker invocation (can be used several times)"),
|
||||
link_args: Vec<String> = (Vec::new(), parse_list, [UNTRACKED],
|
||||
@ -1574,6 +1583,8 @@ options! {
|
||||
"set option to collapse debuginfo for macros"),
|
||||
combine_cgu: bool = (false, parse_bool, [TRACKED],
|
||||
"combine CGUs into a single one"),
|
||||
coverage_options: CoverageOptions = (CoverageOptions::default(), parse_coverage_options, [TRACKED],
|
||||
"control details of coverage instrumentation"),
|
||||
crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED],
|
||||
"inject the given attribute in the crate"),
|
||||
cross_crate_inline_threshold: InliningThreshold = (InliningThreshold::Sometimes(100), parse_inlining_threshold, [TRACKED],
|
||||
|
@ -353,15 +353,7 @@ impl Session {
|
||||
}
|
||||
|
||||
pub fn instrument_coverage_branch(&self) -> bool {
|
||||
self.opts.cg.instrument_coverage() == InstrumentCoverage::Branch
|
||||
}
|
||||
|
||||
pub fn instrument_coverage_except_unused_generics(&self) -> bool {
|
||||
self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedGenerics
|
||||
}
|
||||
|
||||
pub fn instrument_coverage_except_unused_functions(&self) -> bool {
|
||||
self.opts.cg.instrument_coverage() == InstrumentCoverage::ExceptUnusedFunctions
|
||||
self.instrument_coverage() && self.opts.unstable_opts.coverage_options.branch
|
||||
}
|
||||
|
||||
pub fn is_sanitizer_cfi_enabled(&self) -> bool {
|
||||
|
@ -587,6 +587,16 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the plain intrinsic name of an instance.
|
||||
///
|
||||
/// This assumes that the instance is an intrinsic.
|
||||
fn intrinsic_name(&self, def: InstanceDef) -> Symbol {
|
||||
let tables = self.0.borrow_mut();
|
||||
let instance = tables.instances[def];
|
||||
let intrinsic = tables.tcx.intrinsic(instance.def_id()).unwrap();
|
||||
intrinsic.name.to_string()
|
||||
}
|
||||
|
||||
fn ty_layout(&self, ty: Ty) -> Result<Layout, Error> {
|
||||
let mut tables = self.0.borrow_mut();
|
||||
let tcx = tables.tcx;
|
||||
|
@ -219,6 +219,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||
) -> Result<(), SelectionError<'tcx>> {
|
||||
debug!(?stack.obligation);
|
||||
|
||||
// An error type will unify with anything. So, avoid
|
||||
// matching an error type with `ParamCandidate`.
|
||||
// This helps us avoid spurious errors like issue #121941.
|
||||
if stack.obligation.predicate.references_error() {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let all_bounds = stack
|
||||
.obligation
|
||||
.param_env
|
||||
|
@ -183,6 +183,7 @@ pub trait Context {
|
||||
fn vtable_allocation(&self, global_alloc: &GlobalAlloc) -> Option<AllocId>;
|
||||
fn krate(&self, def_id: DefId) -> Crate;
|
||||
fn instance_name(&self, def: InstanceDef, trimmed: bool) -> Symbol;
|
||||
fn intrinsic_name(&self, def: InstanceDef) -> Symbol;
|
||||
|
||||
/// Return information about the target machine.
|
||||
fn target_info(&self) -> MachineInfo;
|
||||
|
@ -90,6 +90,17 @@ impl Instance {
|
||||
with(|context| context.instance_name(self.def, true))
|
||||
}
|
||||
|
||||
/// Retrieve the plain intrinsic name of an instance if it's an intrinsic.
|
||||
///
|
||||
/// The plain name does not include type arguments (as `trimmed_name` does),
|
||||
/// which is more convenient to match with intrinsic symbols.
|
||||
pub fn intrinsic_name(&self) -> Option<Symbol> {
|
||||
match self.kind {
|
||||
InstanceKind::Intrinsic => Some(with(|context| context.intrinsic_name(self.def))),
|
||||
InstanceKind::Item | InstanceKind::Virtual { .. } | InstanceKind::Shim => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Resolve an instance starting from a function definition and generic arguments.
|
||||
pub fn resolve(def: FnDef, args: &GenericArgs) -> Result<Instance, crate::Error> {
|
||||
with(|context| {
|
||||
|
@ -203,8 +203,10 @@
|
||||
// Language features:
|
||||
// tidy-alphabetical-start
|
||||
#![cfg_attr(bootstrap, feature(diagnostic_namespace))]
|
||||
#![cfg_attr(bootstrap, feature(exhaustive_patterns))]
|
||||
#![cfg_attr(bootstrap, feature(platform_intrinsics))]
|
||||
#![cfg_attr(not(bootstrap), feature(freeze_impls))]
|
||||
#![cfg_attr(not(bootstrap), feature(min_exhaustive_patterns))]
|
||||
#![feature(abi_unadjusted)]
|
||||
#![feature(adt_const_params)]
|
||||
#![feature(allow_internal_unsafe)]
|
||||
@ -229,7 +231,6 @@
|
||||
#![feature(doc_cfg_hide)]
|
||||
#![feature(doc_notable_trait)]
|
||||
#![feature(effects)]
|
||||
#![feature(exhaustive_patterns)]
|
||||
#![feature(extern_types)]
|
||||
#![feature(fundamental)]
|
||||
#![feature(generic_arg_infer)]
|
||||
|
@ -270,7 +270,9 @@
|
||||
//
|
||||
// Language features:
|
||||
// tidy-alphabetical-start
|
||||
#![cfg_attr(bootstrap, feature(exhaustive_patterns))]
|
||||
#![cfg_attr(bootstrap, feature(platform_intrinsics))]
|
||||
#![cfg_attr(not(bootstrap), feature(min_exhaustive_patterns))]
|
||||
#![feature(alloc_error_handler)]
|
||||
#![feature(allocator_internals)]
|
||||
#![feature(allow_internal_unsafe)]
|
||||
@ -289,7 +291,6 @@
|
||||
#![feature(doc_masked)]
|
||||
#![feature(doc_notable_trait)]
|
||||
#![feature(dropck_eyepatch)]
|
||||
#![feature(exhaustive_patterns)]
|
||||
#![feature(if_let_guard)]
|
||||
#![feature(intra_doc_pointers)]
|
||||
#![feature(lang_items)]
|
||||
|
@ -3,7 +3,7 @@ mod tests;
|
||||
|
||||
use crate::fmt;
|
||||
use crate::sync::{mutex, poison, LockResult, MutexGuard, PoisonError};
|
||||
use crate::sys::locks as sys;
|
||||
use crate::sys::sync as sys;
|
||||
use crate::time::{Duration, Instant};
|
||||
|
||||
/// A type indicating whether a timed wait on a condition variable returned
|
||||
|
@ -8,7 +8,7 @@ use crate::mem::ManuallyDrop;
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use crate::ptr::NonNull;
|
||||
use crate::sync::{poison, LockResult, TryLockError, TryLockResult};
|
||||
use crate::sys::locks as sys;
|
||||
use crate::sys::sync as sys;
|
||||
|
||||
/// A mutual exclusion primitive useful for protecting shared data
|
||||
///
|
||||
|
@ -8,7 +8,7 @@ mod tests;
|
||||
|
||||
use crate::fmt;
|
||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sys_common::once as sys;
|
||||
use crate::sys::sync as sys;
|
||||
|
||||
/// A synchronization primitive which can be used to run a one-time global
|
||||
/// initialization. Useful for one-time initialization for FFI or related
|
||||
|
@ -270,6 +270,8 @@ impl<T> fmt::Debug for TryLockError<T> {
|
||||
match *self {
|
||||
#[cfg(panic = "unwind")]
|
||||
TryLockError::Poisoned(..) => "Poisoned(..)".fmt(f),
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
TryLockError::Poisoned(ref p) => match p._never {},
|
||||
TryLockError::WouldBlock => "WouldBlock".fmt(f),
|
||||
}
|
||||
}
|
||||
@ -281,6 +283,8 @@ impl<T> fmt::Display for TryLockError<T> {
|
||||
match *self {
|
||||
#[cfg(panic = "unwind")]
|
||||
TryLockError::Poisoned(..) => "poisoned lock: another task failed inside",
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
TryLockError::Poisoned(ref p) => match p._never {},
|
||||
TryLockError::WouldBlock => "try_lock failed because the operation would block",
|
||||
}
|
||||
.fmt(f)
|
||||
@ -294,6 +298,8 @@ impl<T> Error for TryLockError<T> {
|
||||
match *self {
|
||||
#[cfg(panic = "unwind")]
|
||||
TryLockError::Poisoned(ref p) => p.description(),
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
TryLockError::Poisoned(ref p) => match p._never {},
|
||||
TryLockError::WouldBlock => "try_lock failed because the operation would block",
|
||||
}
|
||||
}
|
||||
@ -303,6 +309,8 @@ impl<T> Error for TryLockError<T> {
|
||||
match *self {
|
||||
#[cfg(panic = "unwind")]
|
||||
TryLockError::Poisoned(ref p) => Some(p),
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
TryLockError::Poisoned(ref p) => match p._never {},
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,7 @@ use crate::fmt;
|
||||
use crate::ops::Deref;
|
||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||
use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed};
|
||||
use crate::sys::locks as sys;
|
||||
use crate::sys::sync as sys;
|
||||
|
||||
/// A re-entrant mutual exclusion lock
|
||||
///
|
||||
|
@ -8,7 +8,7 @@ use crate::mem::ManuallyDrop;
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use crate::ptr::NonNull;
|
||||
use crate::sync::{poison, LockResult, TryLockError, TryLockResult};
|
||||
use crate::sys::locks as sys;
|
||||
use crate::sys::sync as sys;
|
||||
|
||||
/// A reader-writer lock
|
||||
///
|
||||
|
@ -6,9 +6,9 @@ mod pal;
|
||||
mod personality;
|
||||
|
||||
pub mod cmath;
|
||||
pub mod locks;
|
||||
pub mod os_str;
|
||||
pub mod path;
|
||||
pub mod sync;
|
||||
#[allow(dead_code)]
|
||||
#[allow(unused_imports)]
|
||||
pub mod thread_local;
|
||||
|
@ -19,8 +19,6 @@ pub mod fs;
|
||||
#[path = "../unsupported/io.rs"]
|
||||
pub mod io;
|
||||
pub mod net;
|
||||
#[path = "../unsupported/once.rs"]
|
||||
pub mod once;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
|
@ -21,8 +21,6 @@ pub mod fs;
|
||||
pub mod io;
|
||||
#[path = "../unsupported/net.rs"]
|
||||
pub mod net;
|
||||
#[path = "../unsupported/once.rs"]
|
||||
pub mod once;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
|
@ -6,7 +6,6 @@ pub mod env;
|
||||
pub mod fs;
|
||||
pub mod io;
|
||||
pub mod net;
|
||||
pub mod once;
|
||||
pub mod os;
|
||||
pub mod pipe;
|
||||
pub mod process;
|
||||
|
@ -41,8 +41,6 @@ pub mod time;
|
||||
|
||||
cfg_if::cfg_if! {
|
||||
if #[cfg(not(target_feature = "atomics"))] {
|
||||
#[path = "../unsupported/once.rs"]
|
||||
pub mod once;
|
||||
#[path = "../unsupported/thread_parking.rs"]
|
||||
pub mod thread_parking;
|
||||
}
|
||||
|
@ -51,10 +51,6 @@ cfg_if::cfg_if! {
|
||||
if #[cfg(target_feature = "atomics")] {
|
||||
compile_error!("The wasm32-wasip2 target does not support atomics");
|
||||
} else {
|
||||
#[path = "../unsupported/locks/mod.rs"]
|
||||
pub mod locks;
|
||||
#[path = "../unsupported/once.rs"]
|
||||
pub mod once;
|
||||
#[path = "../unsupported/thread_parking.rs"]
|
||||
pub mod thread_parking;
|
||||
}
|
||||
|
@ -48,8 +48,6 @@ cfg_if::cfg_if! {
|
||||
#[path = "atomics/thread.rs"]
|
||||
pub mod thread;
|
||||
} else {
|
||||
#[path = "../unsupported/once.rs"]
|
||||
pub mod once;
|
||||
#[path = "../unsupported/thread.rs"]
|
||||
pub mod thread;
|
||||
#[path = "../unsupported/thread_parking.rs"]
|
||||
|
@ -21,8 +21,6 @@ pub mod fs;
|
||||
pub mod io;
|
||||
#[path = "../unsupported/net.rs"]
|
||||
pub mod net;
|
||||
#[path = "../unsupported/once.rs"]
|
||||
pub mod once;
|
||||
pub mod os;
|
||||
#[path = "../unsupported/pipe.rs"]
|
||||
pub mod pipe;
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::sync::atomic::{AtomicU32, Ordering::Relaxed};
|
||||
use crate::sys::futex::{futex_wait, futex_wake, futex_wake_all};
|
||||
use crate::sys::locks::Mutex;
|
||||
use crate::sys::sync::Mutex;
|
||||
use crate::time::Duration;
|
||||
|
||||
pub struct Condvar {
|
@ -2,7 +2,7 @@
|
||||
use crate::sys::pal::itron::{
|
||||
abi, error::expect_success_aborting, spin::SpinMutex, task, time::with_tmos_strong,
|
||||
};
|
||||
use crate::{mem::replace, ptr::NonNull, sys::locks::Mutex, time::Duration};
|
||||
use crate::{mem::replace, ptr::NonNull, sys::sync::Mutex, time::Duration};
|
||||
|
||||
// The implementation is inspired by the queue-based implementation shown in
|
||||
// Andrew D. Birrell's paper "Implementing Condition Variables with Semaphores"
|
@ -1,4 +1,4 @@
|
||||
use crate::sys::locks::Mutex;
|
||||
use crate::sys::sync::Mutex;
|
||||
use crate::time::Duration;
|
||||
|
||||
pub struct Condvar {}
|
@ -1,7 +1,7 @@
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::ptr;
|
||||
use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed};
|
||||
use crate::sys::locks::{mutex, Mutex};
|
||||
use crate::sys::sync::{mutex, Mutex};
|
||||
#[cfg(not(target_os = "nto"))]
|
||||
use crate::sys::time::TIMESPEC_MAX;
|
||||
#[cfg(target_os = "nto")]
|
@ -1,5 +1,5 @@
|
||||
use crate::sys::locks::Mutex;
|
||||
use crate::sys::pal::waitqueue::{SpinMutex, WaitQueue, WaitVariable};
|
||||
use crate::sys::sync::Mutex;
|
||||
use crate::sys_common::lazy_box::{LazyBox, LazyInit};
|
||||
use crate::time::Duration;
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::ptr;
|
||||
use crate::sync::atomic::{AtomicPtr, Ordering::Relaxed};
|
||||
use crate::sys::locks::mutex::{self, Mutex};
|
||||
use crate::sys::sync::mutex::{self, Mutex};
|
||||
use crate::sys::time::TIMESPEC_MAX;
|
||||
use crate::sys_common::lazy_box::{LazyBox, LazyInit};
|
||||
use crate::time::Duration;
|
@ -1,7 +1,7 @@
|
||||
use crate::cell::UnsafeCell;
|
||||
use crate::sys::c;
|
||||
use crate::sys::locks::{mutex, Mutex};
|
||||
use crate::sys::os;
|
||||
use crate::sys::sync::{mutex, Mutex};
|
||||
use crate::time::Duration;
|
||||
|
||||
pub struct Condvar {
|
@ -1,6 +1,6 @@
|
||||
use crate::os::xous::ffi::{blocking_scalar, scalar};
|
||||
use crate::os::xous::services::{ticktimer_server, TicktimerScalar};
|
||||
use crate::sys::locks::Mutex;
|
||||
use crate::sys::sync::Mutex;
|
||||
use crate::time::Duration;
|
||||
use core::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
@ -1,7 +1,9 @@
|
||||
mod condvar;
|
||||
mod mutex;
|
||||
mod once;
|
||||
mod rwlock;
|
||||
|
||||
pub use condvar::Condvar;
|
||||
pub use mutex::Mutex;
|
||||
pub use once::{Once, OnceState};
|
||||
pub use rwlock::RwLock;
|
@ -30,6 +30,7 @@ cfg_if::cfg_if! {
|
||||
mod queue;
|
||||
pub use queue::{Once, OnceState};
|
||||
} else {
|
||||
pub use crate::sys::once::{Once, OnceState};
|
||||
mod no_threads;
|
||||
pub use no_threads::{Once, OnceState};
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
use crate::sys::locks::mutex::Mutex;
|
||||
use crate::sys::sync::mutex::Mutex;
|
||||
|
||||
/// we do not supported rwlock, so use mutex to simulate rwlock.
|
||||
/// it's useful because so many code in std will use rwlock.
|
@ -24,7 +24,6 @@ pub mod backtrace;
|
||||
pub mod fs;
|
||||
pub mod io;
|
||||
pub mod lazy_box;
|
||||
pub mod once;
|
||||
pub mod process;
|
||||
pub mod thread;
|
||||
pub mod thread_info;
|
||||
|
@ -346,14 +346,13 @@ $ llvm-cov report \
|
||||
more fine-grained coverage options are added.
|
||||
Using this value is currently not recommended.
|
||||
|
||||
### Unstable values
|
||||
## `-Z coverage-options=<options>`
|
||||
|
||||
- `-Z unstable-options -C instrument-coverage=branch`:
|
||||
Placeholder for potential branch coverage support in the future.
|
||||
- `-Z unstable-options -C instrument-coverage=except-unused-generics`:
|
||||
Instrument all functions except unused generics.
|
||||
- `-Z unstable-options -C instrument-coverage=except-unused-functions`:
|
||||
Instrument only used (called) functions and instantiated generic functions.
|
||||
This unstable option provides finer control over some aspects of coverage
|
||||
instrumentation. Pass one or more of the following values, separated by commas.
|
||||
|
||||
- `branch` or `no-branch`
|
||||
- Placeholder for potential branch coverage support in the future.
|
||||
|
||||
## Other references
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
# `coverage-options`
|
||||
|
||||
This option controls details of the coverage instrumentation performed by
|
||||
`-C instrument-coverage`.
|
||||
|
||||
Multiple options can be passed, separated by commas. Valid options are:
|
||||
|
||||
- `branch` or `no-branch`: Placeholder for future branch coverage support.
|
@ -6,11 +6,13 @@
|
||||
<meta name="generator" content="rustdoc"> {# #}
|
||||
<meta name="description" content="{{page.description}}"> {# #}
|
||||
<title>{{page.title}}</title> {# #}
|
||||
<script> if (window.location.protocol !== "file:") document.write(` {# Hack to skip preloading fonts locally - see #98769 #}
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_serif_4_regular}}"> {# #}
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.fira_sans_regular}}"> {# #}
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.fira_sans_medium}}"> {# #}
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_code_pro_regular}}"> {# #}
|
||||
<link rel="preload" as="font" type="font/woff2" crossorigin href="{{static_root_path|safe}}{{files.source_code_pro_semibold}}"> {# #}
|
||||
`)</script> {# #}
|
||||
<link rel="stylesheet" {#+ #}
|
||||
href="{{static_root_path|safe}}{{files.normalize_css}}"> {# #}
|
||||
<link rel="stylesheet" {#+ #}
|
||||
|
@ -76,13 +76,8 @@ fn use_this_lib_crate() {
|
||||
// ```
|
||||
//
|
||||
// The notice is triggered because the function is unused by the library itself,
|
||||
// and when the library is compiled, a synthetic function is generated, so
|
||||
// unused function coverage can be reported. Coverage can be skipped for unused
|
||||
// generic functions with:
|
||||
//
|
||||
// ```shell
|
||||
// $ `rustc -Zunstable-options -C instrument-coverage=except-unused-generics ...`
|
||||
// ```
|
||||
// so when the library is compiled, an "unused" set of mappings for that function
|
||||
// is included in the library's coverage metadata.
|
||||
//
|
||||
// Even though this function is used by `uses_crate.rs` (and
|
||||
// counted), with substitutions for `T`, those instantiations are only generated
|
||||
@ -98,6 +93,6 @@ fn use_this_lib_crate() {
|
||||
// another binary that never used this generic function, then it would be valid
|
||||
// to show the unused generic, with unknown substitution (`_`).
|
||||
//
|
||||
// The alternative is to exclude all generics from being included in the "unused
|
||||
// functions" list, which would then omit coverage results for
|
||||
// `unused_generic_function<T>()`, below.
|
||||
// The alternative would be to exclude all generics from being included in the
|
||||
// "unused functions" list, which would then omit coverage results for
|
||||
// `unused_generic_function<T>()`.
|
||||
|
@ -124,13 +124,8 @@ $DIR/auxiliary/used_crate.rs:
|
||||
LL| |// ```
|
||||
LL| |//
|
||||
LL| |// The notice is triggered because the function is unused by the library itself,
|
||||
LL| |// and when the library is compiled, a synthetic function is generated, so
|
||||
LL| |// unused function coverage can be reported. Coverage can be skipped for unused
|
||||
LL| |// generic functions with:
|
||||
LL| |//
|
||||
LL| |// ```shell
|
||||
LL| |// $ `rustc -Zunstable-options -C instrument-coverage=except-unused-generics ...`
|
||||
LL| |// ```
|
||||
LL| |// so when the library is compiled, an "unused" set of mappings for that function
|
||||
LL| |// is included in the library's coverage metadata.
|
||||
LL| |//
|
||||
LL| |// Even though this function is used by `uses_crate.rs` (and
|
||||
LL| |// counted), with substitutions for `T`, those instantiations are only generated
|
||||
@ -146,9 +141,9 @@ $DIR/auxiliary/used_crate.rs:
|
||||
LL| |// another binary that never used this generic function, then it would be valid
|
||||
LL| |// to show the unused generic, with unknown substitution (`_`).
|
||||
LL| |//
|
||||
LL| |// The alternative is to exclude all generics from being included in the "unused
|
||||
LL| |// functions" list, which would then omit coverage results for
|
||||
LL| |// `unused_generic_function<T>()`, below.
|
||||
LL| |// The alternative would be to exclude all generics from being included in the
|
||||
LL| |// "unused functions" list, which would then omit coverage results for
|
||||
LL| |// `unused_generic_function<T>()`.
|
||||
|
||||
$DIR/uses_crate.rs:
|
||||
LL| |// This test was failing on Linux for a while due to #110393 somehow making
|
||||
|
@ -10,7 +10,7 @@
|
||||
//
|
||||
// cdb-command:dx m,d
|
||||
// cdb-check:m,d [Type: std::sync::mutex::Mutex<i32>]
|
||||
// cdb-check: [...] inner [Type: std::sys::locks::mutex::futex::Mutex]
|
||||
// cdb-check: [...] inner [Type: std::sys::sync::mutex::futex::Mutex]
|
||||
// cdb-check: [...] poison [Type: std::sync::poison::Flag]
|
||||
// cdb-check: [...] data : 0 [Type: core::cell::UnsafeCell<i32>]
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
||||
// cdb-command:dx r
|
||||
// cdb-check:r [Type: std::sync::rwlock::RwLockReadGuard<i32>]
|
||||
// cdb-check: [...] data : NonNull([...]: 0) [Type: core::ptr::non_null::NonNull<i32>]
|
||||
// cdb-check: [...] inner_lock : [...] [Type: std::sys::locks::rwlock::futex::RwLock *]
|
||||
// cdb-check: [...] inner_lock : [...] [Type: std::sys::sync::rwlock::futex::RwLock *]
|
||||
|
||||
#[allow(unused_variables)]
|
||||
|
||||
|
@ -28,7 +28,7 @@ fn full_tested_match() -> () {
|
||||
_2 = Option::<i32>::Some(const 42_i32);
|
||||
PlaceMention(_2);
|
||||
_3 = discriminant(_2);
|
||||
switchInt(move _3) -> [0: bb2, 1: bb4, otherwise: bb1];
|
||||
switchInt(move _3) -> [0: bb5, 1: bb2, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
@ -37,20 +37,20 @@ fn full_tested_match() -> () {
|
||||
}
|
||||
|
||||
bb2: {
|
||||
_1 = (const 3_i32, const 3_i32);
|
||||
goto -> bb13;
|
||||
falseEdge -> [real: bb7, imaginary: bb3];
|
||||
}
|
||||
|
||||
bb3: {
|
||||
goto -> bb1;
|
||||
falseEdge -> [real: bb12, imaginary: bb5];
|
||||
}
|
||||
|
||||
bb4: {
|
||||
falseEdge -> [real: bb7, imaginary: bb5];
|
||||
goto -> bb1;
|
||||
}
|
||||
|
||||
bb5: {
|
||||
falseEdge -> [real: bb12, imaginary: bb2];
|
||||
_1 = (const 3_i32, const 3_i32);
|
||||
goto -> bb13;
|
||||
}
|
||||
|
||||
bb6: {
|
||||
@ -91,7 +91,7 @@ fn full_tested_match() -> () {
|
||||
bb11: {
|
||||
StorageDead(_7);
|
||||
StorageDead(_6);
|
||||
goto -> bb5;
|
||||
goto -> bb3;
|
||||
}
|
||||
|
||||
bb12: {
|
||||
|
@ -28,7 +28,7 @@ fn full_tested_match2() -> () {
|
||||
_2 = Option::<i32>::Some(const 42_i32);
|
||||
PlaceMention(_2);
|
||||
_3 = discriminant(_2);
|
||||
switchInt(move _3) -> [0: bb2, 1: bb4, otherwise: bb1];
|
||||
switchInt(move _3) -> [0: bb5, 1: bb2, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
@ -37,18 +37,10 @@ fn full_tested_match2() -> () {
|
||||
}
|
||||
|
||||
bb2: {
|
||||
falseEdge -> [real: bb12, imaginary: bb5];
|
||||
falseEdge -> [real: bb7, imaginary: bb5];
|
||||
}
|
||||
|
||||
bb3: {
|
||||
goto -> bb1;
|
||||
}
|
||||
|
||||
bb4: {
|
||||
falseEdge -> [real: bb7, imaginary: bb2];
|
||||
}
|
||||
|
||||
bb5: {
|
||||
StorageLive(_9);
|
||||
_9 = ((_2 as Some).0: i32);
|
||||
StorageLive(_10);
|
||||
@ -59,6 +51,14 @@ fn full_tested_match2() -> () {
|
||||
goto -> bb13;
|
||||
}
|
||||
|
||||
bb4: {
|
||||
goto -> bb1;
|
||||
}
|
||||
|
||||
bb5: {
|
||||
falseEdge -> [real: bb12, imaginary: bb3];
|
||||
}
|
||||
|
||||
bb6: {
|
||||
goto -> bb1;
|
||||
}
|
||||
@ -97,7 +97,7 @@ fn full_tested_match2() -> () {
|
||||
bb11: {
|
||||
StorageDead(_7);
|
||||
StorageDead(_6);
|
||||
falseEdge -> [real: bb5, imaginary: bb2];
|
||||
falseEdge -> [real: bb3, imaginary: bb5];
|
||||
}
|
||||
|
||||
bb12: {
|
||||
|
@ -30,7 +30,7 @@
|
||||
StorageDead(_5);
|
||||
StorageDead(_4);
|
||||
_8 = discriminant((_3.0: std::option::Option<u32>));
|
||||
- switchInt(move _8) -> [0: bb2, 1: bb3, otherwise: bb1];
|
||||
- switchInt(move _8) -> [0: bb3, 1: bb2, otherwise: bb1];
|
||||
+ StorageLive(_11);
|
||||
+ _11 = discriminant((_3.1: std::option::Option<u32>));
|
||||
+ StorageLive(_12);
|
||||
@ -48,12 +48,12 @@
|
||||
|
||||
bb2: {
|
||||
- _6 = discriminant((_3.1: std::option::Option<u32>));
|
||||
- switchInt(move _6) -> [0: bb5, otherwise: bb1];
|
||||
- switchInt(move _6) -> [1: bb4, otherwise: bb1];
|
||||
- }
|
||||
-
|
||||
- bb3: {
|
||||
- _7 = discriminant((_3.1: std::option::Option<u32>));
|
||||
- switchInt(move _7) -> [1: bb4, otherwise: bb1];
|
||||
- switchInt(move _7) -> [0: bb5, otherwise: bb1];
|
||||
- }
|
||||
-
|
||||
- bb4: {
|
||||
|
@ -36,7 +36,7 @@
|
||||
StorageDead(_5);
|
||||
StorageDead(_4);
|
||||
_8 = discriminant((_3.0: std::option::Option<u32>));
|
||||
switchInt(move _8) -> [0: bb2, 1: bb4, otherwise: bb1];
|
||||
switchInt(move _8) -> [0: bb3, 1: bb2, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb1: {
|
||||
@ -45,17 +45,17 @@
|
||||
|
||||
bb2: {
|
||||
_6 = discriminant((_3.1: std::option::Option<u32>));
|
||||
switchInt(move _6) -> [0: bb3, 1: bb7, otherwise: bb1];
|
||||
switchInt(move _6) -> [0: bb6, 1: bb5, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb3: {
|
||||
_0 = const 3_u32;
|
||||
goto -> bb8;
|
||||
_7 = discriminant((_3.1: std::option::Option<u32>));
|
||||
switchInt(move _7) -> [0: bb4, 1: bb7, otherwise: bb1];
|
||||
}
|
||||
|
||||
bb4: {
|
||||
_7 = discriminant((_3.1: std::option::Option<u32>));
|
||||
switchInt(move _7) -> [0: bb6, 1: bb5, otherwise: bb1];
|
||||
_0 = const 3_u32;
|
||||
goto -> bb8;
|
||||
}
|
||||
|
||||
bb5: {
|
||||
|
@ -42,11 +42,15 @@
|
||||
}
|
||||
|
||||
bb2: {
|
||||
- switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb4];
|
||||
- switchInt((_2.0: bool)) -> [0: bb4, otherwise: bb3];
|
||||
+ switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb17];
|
||||
}
|
||||
|
||||
bb3: {
|
||||
- falseEdge -> [real: bb20, imaginary: bb4];
|
||||
- }
|
||||
-
|
||||
- bb4: {
|
||||
StorageLive(_15);
|
||||
_15 = (_2.1: bool);
|
||||
StorageLive(_16);
|
||||
@ -55,12 +59,8 @@
|
||||
+ goto -> bb16;
|
||||
}
|
||||
|
||||
bb4: {
|
||||
- falseEdge -> [real: bb20, imaginary: bb3];
|
||||
- }
|
||||
-
|
||||
- bb5: {
|
||||
- falseEdge -> [real: bb13, imaginary: bb4];
|
||||
- falseEdge -> [real: bb13, imaginary: bb3];
|
||||
- }
|
||||
-
|
||||
- bb6: {
|
||||
@ -68,6 +68,7 @@
|
||||
- }
|
||||
-
|
||||
- bb7: {
|
||||
+ bb4: {
|
||||
_0 = const 1_i32;
|
||||
- drop(_7) -> [return: bb18, unwind: bb25];
|
||||
+ drop(_7) -> [return: bb15, unwind: bb22];
|
||||
@ -183,7 +184,7 @@
|
||||
StorageDead(_12);
|
||||
StorageDead(_8);
|
||||
StorageDead(_6);
|
||||
- falseEdge -> [real: bb2, imaginary: bb4];
|
||||
- falseEdge -> [real: bb2, imaginary: bb3];
|
||||
+ goto -> bb2;
|
||||
}
|
||||
|
||||
|
@ -42,11 +42,15 @@
|
||||
}
|
||||
|
||||
bb2: {
|
||||
- switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb4];
|
||||
- switchInt((_2.0: bool)) -> [0: bb4, otherwise: bb3];
|
||||
+ switchInt((_2.0: bool)) -> [0: bb3, otherwise: bb17];
|
||||
}
|
||||
|
||||
bb3: {
|
||||
- falseEdge -> [real: bb20, imaginary: bb4];
|
||||
- }
|
||||
-
|
||||
- bb4: {
|
||||
StorageLive(_15);
|
||||
_15 = (_2.1: bool);
|
||||
StorageLive(_16);
|
||||
@ -55,12 +59,8 @@
|
||||
+ goto -> bb16;
|
||||
}
|
||||
|
||||
bb4: {
|
||||
- falseEdge -> [real: bb20, imaginary: bb3];
|
||||
- }
|
||||
-
|
||||
- bb5: {
|
||||
- falseEdge -> [real: bb13, imaginary: bb4];
|
||||
- falseEdge -> [real: bb13, imaginary: bb3];
|
||||
- }
|
||||
-
|
||||
- bb6: {
|
||||
@ -68,6 +68,7 @@
|
||||
- }
|
||||
-
|
||||
- bb7: {
|
||||
+ bb4: {
|
||||
_0 = const 1_i32;
|
||||
- drop(_7) -> [return: bb18, unwind: bb25];
|
||||
+ drop(_7) -> [return: bb15, unwind: bb22];
|
||||
@ -183,7 +184,7 @@
|
||||
StorageDead(_12);
|
||||
StorageDead(_8);
|
||||
StorageDead(_6);
|
||||
- falseEdge -> [real: bb2, imaginary: bb4];
|
||||
- falseEdge -> [real: bb2, imaginary: bb3];
|
||||
+ goto -> bb2;
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ extern crate stable_mir;
|
||||
|
||||
use std::assert_matches::assert_matches;
|
||||
use mir::{mono::Instance, TerminatorKind::*};
|
||||
use stable_mir::mir::mono::InstanceKind;
|
||||
use rustc_smir::rustc_internal;
|
||||
use stable_mir::ty::{RigidTy, TyKind, Ty, UintTy};
|
||||
use stable_mir::*;
|
||||
@ -35,9 +36,10 @@ fn test_stable_mir() -> ControlFlow<()> {
|
||||
assert_eq!(main_fn.trimmed_name(), "main");
|
||||
|
||||
let instances = get_instances(main_fn.body().unwrap());
|
||||
assert_eq!(instances.len(), 2);
|
||||
assert_eq!(instances.len(), 3);
|
||||
test_fn(instances[0], "from_u32", "std::char::from_u32", "core");
|
||||
test_fn(instances[1], "Vec::<u8>::new", "std::vec::Vec::<u8>::new", "alloc");
|
||||
test_fn(instances[2], "ctpop::<u8>", "std::intrinsics::ctpop::<u8>", "core");
|
||||
test_vec_new(instances[1]);
|
||||
ControlFlow::Continue(())
|
||||
}
|
||||
@ -48,6 +50,14 @@ fn test_fn(instance: Instance, expected_trimmed: &str, expected_qualified: &str,
|
||||
assert_eq!(&trimmed, expected_trimmed);
|
||||
assert_eq!(&qualified, expected_qualified);
|
||||
|
||||
if instance.kind == InstanceKind::Intrinsic {
|
||||
let intrinsic = instance.intrinsic_name().unwrap();
|
||||
let (trimmed_base, _trimmed_args) = trimmed.split_once("::").unwrap();
|
||||
assert_eq!(intrinsic, trimmed_base);
|
||||
return;
|
||||
}
|
||||
assert!(instance.intrinsic_name().is_none());
|
||||
|
||||
let item = CrateItem::try_from(instance).unwrap();
|
||||
let trimmed = item.trimmed_name();
|
||||
let qualified = item.name();
|
||||
@ -119,10 +129,12 @@ fn generate_input(path: &str) -> std::io::Result<()> {
|
||||
write!(
|
||||
file,
|
||||
r#"
|
||||
#![feature(core_intrinsics)]
|
||||
|
||||
fn main() {{
|
||||
let _c = core::char::from_u32(99);
|
||||
let _v = Vec::<u8>::new();
|
||||
let _i = std::intrinsics::ctpop::<u8>(0);
|
||||
}}
|
||||
"#
|
||||
)?;
|
||||
|
@ -1,2 +1,2 @@
|
||||
error: incorrect value `bad-value` for codegen option `instrument-coverage` - either a boolean (`yes`, `no`, `on`, `off`, etc) or (unstable) one of `branch`, `except-unused-generics`, `except-unused-functions` was expected
|
||||
error: incorrect value `bad-value` for codegen option `instrument-coverage` - one of: `y`, `yes`, `on`, `true`, `n`, `no`, `off` or `false` was expected
|
||||
|
||||
|
@ -1,2 +1,2 @@
|
||||
error: incorrect value `` for codegen option `instrument-coverage` - either a boolean (`yes`, `no`, `on`, `off`, etc) or (unstable) one of `branch`, `except-unused-generics`, `except-unused-functions` was expected
|
||||
error: incorrect value `` for codegen option `instrument-coverage` - one of: `y`, `yes`, `on`, `true`, `n`, `no`, `off` or `false` was expected
|
||||
|
||||
|
2
tests/ui/instrument-coverage/coverage-options.bad.stderr
Normal file
2
tests/ui/instrument-coverage/coverage-options.bad.stderr
Normal file
@ -0,0 +1,2 @@
|
||||
error: incorrect value `bad` for unstable option `coverage-options` - `branch` or `no-branch` was expected
|
||||
|
14
tests/ui/instrument-coverage/coverage-options.rs
Normal file
14
tests/ui/instrument-coverage/coverage-options.rs
Normal file
@ -0,0 +1,14 @@
|
||||
//@ needs-profiler-support
|
||||
//@ revisions: branch no-branch bad
|
||||
//@ compile-flags -Cinstrument-coverage
|
||||
|
||||
//@ [branch] check-pass
|
||||
//@ [branch] compile-flags: -Zcoverage-options=branch
|
||||
|
||||
//@ [no-branch] check-pass
|
||||
//@ [no-branch] compile-flags: -Zcoverage-options=no-branch
|
||||
|
||||
//@ [bad] check-fail
|
||||
//@ [bad] compile-flags: -Zcoverage-options=bad
|
||||
|
||||
fn main() {}
|
@ -1,2 +0,0 @@
|
||||
error: `-C instrument-coverage=branch` and `-C instrument-coverage=except-*` require `-Z unstable-options`
|
||||
|
@ -1,2 +0,0 @@
|
||||
error: `-C instrument-coverage=branch` and `-C instrument-coverage=except-*` require `-Z unstable-options`
|
||||
|
@ -1,2 +0,0 @@
|
||||
error: `-C instrument-coverage=branch` and `-C instrument-coverage=except-*` require `-Z unstable-options`
|
||||
|
@ -1,6 +0,0 @@
|
||||
//@ revisions: branch except-unused-functions except-unused-generics
|
||||
//@ [branch] compile-flags: -Cinstrument-coverage=branch
|
||||
//@ [except-unused-functions] compile-flags: -Cinstrument-coverage=except-unused-functions
|
||||
//@ [except-unused-generics] compile-flags: -Cinstrument-coverage=except-unused-generics
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,7 @@
|
||||
fn main() {
|
||||
unsafe {
|
||||
dealloc(ptr2, Layout::(x: !)(1, 1)); //~ ERROR: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `:`
|
||||
//~^ ERROR: expected one of `.`, `;`, `?`, `}`, or an operator, found `)`
|
||||
//~| while parsing this parenthesized list of type arguments starting here
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `:`
|
||||
--> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:3:33
|
||||
|
|
||||
LL | dealloc(ptr2, Layout::(x: !)(1, 1));
|
||||
| --- ^ expected one of 7 possible tokens
|
||||
| |
|
||||
| while parsing this parenthesized list of type arguments starting here
|
||||
|
||||
error: expected one of `.`, `;`, `?`, `}`, or an operator, found `)`
|
||||
--> $DIR/diagnostics-parenthesized-type-arguments-ice-issue-122345.rs:3:43
|
||||
|
|
||||
LL | dealloc(ptr2, Layout::(x: !)(1, 1));
|
||||
| ^ expected one of `.`, `;`, `?`, `}`, or an operator
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
@ -0,0 +1,14 @@
|
||||
//@ run-pass
|
||||
//
|
||||
// Regression test for match lowering to MIR: when gathering candidates, by the time we get to the
|
||||
// range we know the range will only match on the failure case of the switchint. Hence we mustn't
|
||||
// add the `1` to the switchint or the range would be incorrectly sorted.
|
||||
#![allow(unreachable_patterns)]
|
||||
fn main() {
|
||||
match 1 {
|
||||
10 => unreachable!(),
|
||||
0..=5 => {}
|
||||
1 => unreachable!(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
fn function<T: PartialEq>() {
|
||||
foo == 2; //~ ERROR cannot find value `foo` in this scope [E0425]
|
||||
}
|
||||
|
||||
fn main() {}
|
@ -0,0 +1,9 @@
|
||||
error[E0425]: cannot find value `foo` in this scope
|
||||
--> $DIR/dont-match-error-ty-with-calller-supplied-obligation-issue-121941.rs:2:5
|
||||
|
|
||||
LL | foo == 2;
|
||||
| ^^^ not found in this scope
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0425`.
|
@ -854,3 +854,7 @@ project-stable-mir = [
|
||||
"/src/tools/tidy" = ["bootstrap"]
|
||||
"/src/tools/x" = ["bootstrap"]
|
||||
"/src/tools/rustdoc-gui-test" = ["bootstrap", "@onur-ozkan"]
|
||||
|
||||
# Enable tracking of PR review assignment
|
||||
# Documentation at: https://forge.rust-lang.org/triagebot/pr-assignment-tracking.html
|
||||
[pr-tracking]
|
||||
|
Loading…
x
Reference in New Issue
Block a user