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:
bors 2024-03-13 07:17:22 +00:00
commit 762d3170f6
98 changed files with 604 additions and 555 deletions

View File

@ -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)

View File

@ -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())
});

View File

@ -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);

View File

@ -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

View File

@ -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 {

View File

@ -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,
}
}

View File

@ -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);
}

View File

@ -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.

View File

@ -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()
}

View File

@ -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();

View File

@ -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],
}
}

View File

@ -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(),
})

View File

@ -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

View File

@ -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,

View File

@ -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],

View File

@ -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 {

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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| {

View File

@ -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)]

View File

@ -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)]

View File

@ -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

View File

@ -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
///

View File

@ -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

View File

@ -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,
}
}

View File

@ -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
///

View File

@ -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
///

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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"]

View File

@ -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;

View File

@ -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 {

View File

@ -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"

View File

@ -1,4 +1,4 @@
use crate::sys::locks::Mutex;
use crate::sys::sync::Mutex;
use crate::time::Duration;
pub struct Condvar {}

View File

@ -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")]

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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};

View File

@ -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;

View File

@ -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};
}
}

View File

@ -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.

View File

@ -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;

View File

@ -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

View File

@ -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.

View File

@ -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" {#+ #}

View File

@ -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>()`.

View File

@ -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

View File

@ -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>]

View File

@ -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)]

View File

@ -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: {

View File

@ -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: {

View File

@ -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: {

View File

@ -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: {

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);
}}
"#
)?;

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,2 @@
error: incorrect value `bad` for unstable option `coverage-options` - `branch` or `no-branch` was expected

View 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() {}

View File

@ -1,2 +0,0 @@
error: `-C instrument-coverage=branch` and `-C instrument-coverage=except-*` require `-Z unstable-options`

View File

@ -1,2 +0,0 @@
error: `-C instrument-coverage=branch` and `-C instrument-coverage=except-*` require `-Z unstable-options`

View File

@ -1,2 +0,0 @@
error: `-C instrument-coverage=branch` and `-C instrument-coverage=except-*` require `-Z unstable-options`

View File

@ -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() {}

View File

@ -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
}
}

View File

@ -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

View File

@ -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!(),
}
}

View File

@ -0,0 +1,5 @@
fn function<T: PartialEq>() {
foo == 2; //~ ERROR cannot find value `foo` in this scope [E0425]
}
fn main() {}

View File

@ -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`.

View File

@ -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]