Auto merge of #126569 - jieyouxu:rollup-1uvkb2y, r=jieyouxu
Rollup of 8 pull requests Successful merges: - #125258 (Resolve elided lifetimes in assoc const to static if no other lifetimes are in scope) - #126250 (docs(change): Don't mention a Cargo 2024 edition change for 1.79) - #126288 (doc: Added commas where needed) - #126346 (export std::os::fd module on HermitOS) - #126468 (div_euclid, rem_euclid: clarify/extend documentation) - #126531 (Add codegen test for `Request::provide_*`) - #126535 (coverage: Arrange span extraction/refinement as a series of passes) - #126538 (coverage: Several small improvements to graph code) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
fd7eefc275
33 changed files with 733 additions and 595 deletions
|
@ -97,7 +97,6 @@ Cargo
|
||||||
- [Prevent dashes in `lib.name`, always normalizing to `_`.](https://github.com/rust-lang/cargo/pull/12783/)
|
- [Prevent dashes in `lib.name`, always normalizing to `_`.](https://github.com/rust-lang/cargo/pull/12783/)
|
||||||
- [Stabilize MSRV-aware version requirement selection in `cargo add`.](https://github.com/rust-lang/cargo/pull/13608/)
|
- [Stabilize MSRV-aware version requirement selection in `cargo add`.](https://github.com/rust-lang/cargo/pull/13608/)
|
||||||
- [Switch to using `gitoxide` by default for listing files.](https://github.com/rust-lang/cargo/pull/13696/)
|
- [Switch to using `gitoxide` by default for listing files.](https://github.com/rust-lang/cargo/pull/13696/)
|
||||||
- [Error on `[project]` in Edition 2024; `cargo fix --edition` will change it to `[package]`.](https://github.com/rust-lang/cargo/pull/13747/)
|
|
||||||
|
|
||||||
<a id="1.79.0-Rustdoc"></a>
|
<a id="1.79.0-Rustdoc"></a>
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ lint_associated_const_elided_lifetime = {$elided ->
|
||||||
*[false] `'_` cannot be used here
|
*[false] `'_` cannot be used here
|
||||||
}
|
}
|
||||||
.suggestion = use the `'static` lifetime
|
.suggestion = use the `'static` lifetime
|
||||||
|
.note = cannot automatically infer `'static` because of other lifetimes in scope
|
||||||
|
|
||||||
lint_async_fn_in_trait = use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified
|
lint_async_fn_in_trait = use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified
|
||||||
.note = you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future`
|
.note = you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future`
|
||||||
|
|
|
@ -319,11 +319,20 @@ pub(super) fn decorate_lint(sess: &Session, diagnostic: BuiltinLintDiag, diag: &
|
||||||
BuiltinLintDiag::UnusedQualifications { removal_span } => {
|
BuiltinLintDiag::UnusedQualifications { removal_span } => {
|
||||||
lints::UnusedQualifications { removal_span }.decorate_lint(diag);
|
lints::UnusedQualifications { removal_span }.decorate_lint(diag);
|
||||||
}
|
}
|
||||||
BuiltinLintDiag::AssociatedConstElidedLifetime { elided, span: lt_span } => {
|
BuiltinLintDiag::AssociatedConstElidedLifetime {
|
||||||
|
elided,
|
||||||
|
span: lt_span,
|
||||||
|
lifetimes_in_scope,
|
||||||
|
} => {
|
||||||
let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span };
|
let lt_span = if elided { lt_span.shrink_to_hi() } else { lt_span };
|
||||||
let code = if elided { "'static " } else { "'static" };
|
let code = if elided { "'static " } else { "'static" };
|
||||||
lints::AssociatedConstElidedLifetime { span: lt_span, code, elided }
|
lints::AssociatedConstElidedLifetime {
|
||||||
.decorate_lint(diag);
|
span: lt_span,
|
||||||
|
code,
|
||||||
|
elided,
|
||||||
|
lifetimes_in_scope,
|
||||||
|
}
|
||||||
|
.decorate_lint(diag);
|
||||||
}
|
}
|
||||||
BuiltinLintDiag::RedundantImportVisibility { max_vis, span: vis_span, import_vis } => {
|
BuiltinLintDiag::RedundantImportVisibility { max_vis, span: vis_span, import_vis } => {
|
||||||
lints::RedundantImportVisibility { span: vis_span, help: (), max_vis, import_vis }
|
lints::RedundantImportVisibility { span: vis_span, help: (), max_vis, import_vis }
|
||||||
|
|
|
@ -2873,6 +2873,8 @@ pub struct AssociatedConstElidedLifetime {
|
||||||
|
|
||||||
pub code: &'static str,
|
pub code: &'static str,
|
||||||
pub elided: bool,
|
pub elided: bool,
|
||||||
|
#[note]
|
||||||
|
pub lifetimes_in_scope: MultiSpan,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(LintDiagnostic)]
|
#[derive(LintDiagnostic)]
|
||||||
|
|
|
@ -4593,16 +4593,18 @@ declare_lint! {
|
||||||
|
|
||||||
declare_lint! {
|
declare_lint! {
|
||||||
/// The `elided_lifetimes_in_associated_constant` lint detects elided lifetimes
|
/// The `elided_lifetimes_in_associated_constant` lint detects elided lifetimes
|
||||||
/// that were erroneously allowed in associated constants.
|
/// in associated constants when there are other lifetimes in scope. This was
|
||||||
|
/// accidentally supported, and this lint was later relaxed to allow eliding
|
||||||
|
/// lifetimes to `'static` when there are no lifetimes in scope.
|
||||||
///
|
///
|
||||||
/// ### Example
|
/// ### Example
|
||||||
///
|
///
|
||||||
/// ```rust,compile_fail
|
/// ```rust,compile_fail
|
||||||
/// #![deny(elided_lifetimes_in_associated_constant)]
|
/// #![deny(elided_lifetimes_in_associated_constant)]
|
||||||
///
|
///
|
||||||
/// struct Foo;
|
/// struct Foo<'a>(&'a ());
|
||||||
///
|
///
|
||||||
/// impl Foo {
|
/// impl<'a> Foo<'a> {
|
||||||
/// const STR: &str = "hello, world";
|
/// const STR: &str = "hello, world";
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
|
|
|
@ -696,6 +696,7 @@ pub enum BuiltinLintDiag {
|
||||||
AssociatedConstElidedLifetime {
|
AssociatedConstElidedLifetime {
|
||||||
elided: bool,
|
elided: bool,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
lifetimes_in_scope: MultiSpan,
|
||||||
},
|
},
|
||||||
RedundantImportVisibility {
|
RedundantImportVisibility {
|
||||||
span: Span,
|
span: Span,
|
||||||
|
|
|
@ -168,11 +168,6 @@ impl CoverageCounters {
|
||||||
self.counter_increment_sites.len()
|
self.counter_increment_sites.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
pub(super) fn num_expressions(&self) -> usize {
|
|
||||||
self.expressions.len()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_bcb_counter(&mut self, bcb: BasicCoverageBlock, counter_kind: BcbCounter) -> BcbCounter {
|
fn set_bcb_counter(&mut self, bcb: BasicCoverageBlock, counter_kind: BcbCounter) -> BcbCounter {
|
||||||
if let Some(replaced) = self.bcb_counters[bcb].replace(counter_kind) {
|
if let Some(replaced) = self.bcb_counters[bcb].replace(counter_kind) {
|
||||||
bug!(
|
bug!(
|
||||||
|
|
|
@ -14,16 +14,16 @@ use std::ops::{Index, IndexMut};
|
||||||
/// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s
|
/// A coverage-specific simplification of the MIR control flow graph (CFG). The `CoverageGraph`s
|
||||||
/// nodes are `BasicCoverageBlock`s, which encompass one or more MIR `BasicBlock`s.
|
/// nodes are `BasicCoverageBlock`s, which encompass one or more MIR `BasicBlock`s.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) struct CoverageGraph {
|
pub(crate) struct CoverageGraph {
|
||||||
bcbs: IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
|
bcbs: IndexVec<BasicCoverageBlock, BasicCoverageBlockData>,
|
||||||
bb_to_bcb: IndexVec<BasicBlock, Option<BasicCoverageBlock>>,
|
bb_to_bcb: IndexVec<BasicBlock, Option<BasicCoverageBlock>>,
|
||||||
pub successors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
|
pub(crate) successors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
|
||||||
pub predecessors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
|
pub(crate) predecessors: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
|
||||||
dominators: Option<Dominators<BasicCoverageBlock>>,
|
dominators: Option<Dominators<BasicCoverageBlock>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CoverageGraph {
|
impl CoverageGraph {
|
||||||
pub fn from_mir(mir_body: &mir::Body<'_>) -> Self {
|
pub(crate) fn from_mir(mir_body: &mir::Body<'_>) -> Self {
|
||||||
let (bcbs, bb_to_bcb) = Self::compute_basic_coverage_blocks(mir_body);
|
let (bcbs, bb_to_bcb) = Self::compute_basic_coverage_blocks(mir_body);
|
||||||
|
|
||||||
// Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock
|
// Pre-transform MIR `BasicBlock` successors and predecessors into the BasicCoverageBlock
|
||||||
|
@ -135,24 +135,28 @@ impl CoverageGraph {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn iter_enumerated(
|
pub(crate) fn iter_enumerated(
|
||||||
&self,
|
&self,
|
||||||
) -> impl Iterator<Item = (BasicCoverageBlock, &BasicCoverageBlockData)> {
|
) -> impl Iterator<Item = (BasicCoverageBlock, &BasicCoverageBlockData)> {
|
||||||
self.bcbs.iter_enumerated()
|
self.bcbs.iter_enumerated()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn bcb_from_bb(&self, bb: BasicBlock) -> Option<BasicCoverageBlock> {
|
pub(crate) fn bcb_from_bb(&self, bb: BasicBlock) -> Option<BasicCoverageBlock> {
|
||||||
if bb.index() < self.bb_to_bcb.len() { self.bb_to_bcb[bb] } else { None }
|
if bb.index() < self.bb_to_bcb.len() { self.bb_to_bcb[bb] } else { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
|
pub(crate) fn dominates(&self, dom: BasicCoverageBlock, node: BasicCoverageBlock) -> bool {
|
||||||
self.dominators.as_ref().unwrap().dominates(dom, node)
|
self.dominators.as_ref().unwrap().dominates(dom, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn cmp_in_dominator_order(&self, a: BasicCoverageBlock, b: BasicCoverageBlock) -> Ordering {
|
pub(crate) fn cmp_in_dominator_order(
|
||||||
|
&self,
|
||||||
|
a: BasicCoverageBlock,
|
||||||
|
b: BasicCoverageBlock,
|
||||||
|
) -> Ordering {
|
||||||
self.dominators.as_ref().unwrap().cmp_in_dominator_order(a, b)
|
self.dominators.as_ref().unwrap().cmp_in_dominator_order(a, b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -166,7 +170,7 @@ impl CoverageGraph {
|
||||||
///
|
///
|
||||||
/// FIXME: That assumption might not be true for [`TerminatorKind::Yield`]?
|
/// FIXME: That assumption might not be true for [`TerminatorKind::Yield`]?
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(super) fn bcb_has_multiple_in_edges(&self, bcb: BasicCoverageBlock) -> bool {
|
pub(crate) fn bcb_has_multiple_in_edges(&self, bcb: BasicCoverageBlock) -> bool {
|
||||||
// Even though bcb0 conceptually has an extra virtual in-edge due to
|
// Even though bcb0 conceptually has an extra virtual in-edge due to
|
||||||
// being the entry point, we've already asserted that it has no _other_
|
// being the entry point, we've already asserted that it has no _other_
|
||||||
// in-edges, so there's no possibility of it having _multiple_ in-edges.
|
// in-edges, so there's no possibility of it having _multiple_ in-edges.
|
||||||
|
@ -212,7 +216,7 @@ impl graph::StartNode for CoverageGraph {
|
||||||
impl graph::Successors for CoverageGraph {
|
impl graph::Successors for CoverageGraph {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn successors(&self, node: Self::Node) -> impl Iterator<Item = Self::Node> {
|
fn successors(&self, node: Self::Node) -> impl Iterator<Item = Self::Node> {
|
||||||
self.successors[node].iter().cloned()
|
self.successors[node].iter().copied()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -227,7 +231,7 @@ rustc_index::newtype_index! {
|
||||||
/// A node in the control-flow graph of CoverageGraph.
|
/// A node in the control-flow graph of CoverageGraph.
|
||||||
#[orderable]
|
#[orderable]
|
||||||
#[debug_format = "bcb{}"]
|
#[debug_format = "bcb{}"]
|
||||||
pub(super) struct BasicCoverageBlock {
|
pub(crate) struct BasicCoverageBlock {
|
||||||
const START_BCB = 0;
|
const START_BCB = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,23 +263,23 @@ rustc_index::newtype_index! {
|
||||||
/// queries (`dominates()`, `predecessors`, `successors`, etc.) have branch (control flow)
|
/// queries (`dominates()`, `predecessors`, `successors`, etc.) have branch (control flow)
|
||||||
/// significance.
|
/// significance.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(super) struct BasicCoverageBlockData {
|
pub(crate) struct BasicCoverageBlockData {
|
||||||
pub basic_blocks: Vec<BasicBlock>,
|
pub(crate) basic_blocks: Vec<BasicBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BasicCoverageBlockData {
|
impl BasicCoverageBlockData {
|
||||||
pub fn from(basic_blocks: Vec<BasicBlock>) -> Self {
|
fn from(basic_blocks: Vec<BasicBlock>) -> Self {
|
||||||
assert!(basic_blocks.len() > 0);
|
assert!(basic_blocks.len() > 0);
|
||||||
Self { basic_blocks }
|
Self { basic_blocks }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn leader_bb(&self) -> BasicBlock {
|
pub(crate) fn leader_bb(&self) -> BasicBlock {
|
||||||
self.basic_blocks[0]
|
self.basic_blocks[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn last_bb(&self) -> BasicBlock {
|
pub(crate) fn last_bb(&self) -> BasicBlock {
|
||||||
*self.basic_blocks.last().unwrap()
|
*self.basic_blocks.last().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -364,7 +368,7 @@ fn bcb_filtered_successors<'a, 'tcx>(terminator: &'a Terminator<'tcx>) -> Covera
|
||||||
/// CoverageGraph outside all loops. This supports traversing the BCB CFG in a way that
|
/// CoverageGraph outside all loops. This supports traversing the BCB CFG in a way that
|
||||||
/// ensures a loop is completely traversed before processing Blocks after the end of the loop.
|
/// ensures a loop is completely traversed before processing Blocks after the end of the loop.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) struct TraversalContext {
|
struct TraversalContext {
|
||||||
/// BCB with one or more incoming loop backedges, indicating which loop
|
/// BCB with one or more incoming loop backedges, indicating which loop
|
||||||
/// this context is for.
|
/// this context is for.
|
||||||
///
|
///
|
||||||
|
@ -375,7 +379,7 @@ pub(super) struct TraversalContext {
|
||||||
worklist: VecDeque<BasicCoverageBlock>,
|
worklist: VecDeque<BasicCoverageBlock>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) struct TraverseCoverageGraphWithLoops<'a> {
|
pub(crate) struct TraverseCoverageGraphWithLoops<'a> {
|
||||||
basic_coverage_blocks: &'a CoverageGraph,
|
basic_coverage_blocks: &'a CoverageGraph,
|
||||||
|
|
||||||
backedges: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
|
backedges: IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>>,
|
||||||
|
@ -384,7 +388,7 @@ pub(super) struct TraverseCoverageGraphWithLoops<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> TraverseCoverageGraphWithLoops<'a> {
|
impl<'a> TraverseCoverageGraphWithLoops<'a> {
|
||||||
pub(super) fn new(basic_coverage_blocks: &'a CoverageGraph) -> Self {
|
pub(crate) fn new(basic_coverage_blocks: &'a CoverageGraph) -> Self {
|
||||||
let backedges = find_loop_backedges(basic_coverage_blocks);
|
let backedges = find_loop_backedges(basic_coverage_blocks);
|
||||||
|
|
||||||
let worklist = VecDeque::from([basic_coverage_blocks.start_node()]);
|
let worklist = VecDeque::from([basic_coverage_blocks.start_node()]);
|
||||||
|
@ -400,7 +404,7 @@ impl<'a> TraverseCoverageGraphWithLoops<'a> {
|
||||||
|
|
||||||
/// For each loop on the loop context stack (top-down), yields a list of BCBs
|
/// For each loop on the loop context stack (top-down), yields a list of BCBs
|
||||||
/// within that loop that have an outgoing edge back to the loop header.
|
/// within that loop that have an outgoing edge back to the loop header.
|
||||||
pub(super) fn reloop_bcbs_per_loop(&self) -> impl Iterator<Item = &[BasicCoverageBlock]> {
|
pub(crate) fn reloop_bcbs_per_loop(&self) -> impl Iterator<Item = &[BasicCoverageBlock]> {
|
||||||
self.context_stack
|
self.context_stack
|
||||||
.iter()
|
.iter()
|
||||||
.rev()
|
.rev()
|
||||||
|
@ -408,39 +412,38 @@ impl<'a> TraverseCoverageGraphWithLoops<'a> {
|
||||||
.map(|header_bcb| self.backedges[header_bcb].as_slice())
|
.map(|header_bcb| self.backedges[header_bcb].as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn next(&mut self) -> Option<BasicCoverageBlock> {
|
pub(crate) fn next(&mut self) -> Option<BasicCoverageBlock> {
|
||||||
debug!(
|
debug!(
|
||||||
"TraverseCoverageGraphWithLoops::next - context_stack: {:?}",
|
"TraverseCoverageGraphWithLoops::next - context_stack: {:?}",
|
||||||
self.context_stack.iter().rev().collect::<Vec<_>>()
|
self.context_stack.iter().rev().collect::<Vec<_>>()
|
||||||
);
|
);
|
||||||
|
|
||||||
while let Some(context) = self.context_stack.last_mut() {
|
while let Some(context) = self.context_stack.last_mut() {
|
||||||
if let Some(bcb) = context.worklist.pop_front() {
|
let Some(bcb) = context.worklist.pop_front() else {
|
||||||
if !self.visited.insert(bcb) {
|
// This stack level is exhausted; pop it and try the next one.
|
||||||
debug!("Already visited: {bcb:?}");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
debug!("Visiting {bcb:?}");
|
|
||||||
|
|
||||||
if self.backedges[bcb].len() > 0 {
|
|
||||||
debug!("{bcb:?} is a loop header! Start a new TraversalContext...");
|
|
||||||
self.context_stack.push(TraversalContext {
|
|
||||||
loop_header: Some(bcb),
|
|
||||||
worklist: VecDeque::new(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
self.add_successors_to_worklists(bcb);
|
|
||||||
return Some(bcb);
|
|
||||||
} else {
|
|
||||||
// Strip contexts with empty worklists from the top of the stack
|
|
||||||
self.context_stack.pop();
|
self.context_stack.pop();
|
||||||
|
continue;
|
||||||
|
};
|
||||||
|
|
||||||
|
if !self.visited.insert(bcb) {
|
||||||
|
debug!("Already visited: {bcb:?}");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
debug!("Visiting {bcb:?}");
|
||||||
|
|
||||||
|
if self.backedges[bcb].len() > 0 {
|
||||||
|
debug!("{bcb:?} is a loop header! Start a new TraversalContext...");
|
||||||
|
self.context_stack
|
||||||
|
.push(TraversalContext { loop_header: Some(bcb), worklist: VecDeque::new() });
|
||||||
|
}
|
||||||
|
self.add_successors_to_worklists(bcb);
|
||||||
|
return Some(bcb);
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_successors_to_worklists(&mut self, bcb: BasicCoverageBlock) {
|
fn add_successors_to_worklists(&mut self, bcb: BasicCoverageBlock) {
|
||||||
let successors = &self.basic_coverage_blocks.successors[bcb];
|
let successors = &self.basic_coverage_blocks.successors[bcb];
|
||||||
debug!("{:?} has {} successors:", bcb, successors.len());
|
debug!("{:?} has {} successors:", bcb, successors.len());
|
||||||
|
|
||||||
|
@ -494,11 +497,11 @@ impl<'a> TraverseCoverageGraphWithLoops<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_complete(&self) -> bool {
|
pub(crate) fn is_complete(&self) -> bool {
|
||||||
self.visited.count() == self.visited.domain_size()
|
self.visited.count() == self.visited.domain_size()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unvisited(&self) -> Vec<BasicCoverageBlock> {
|
pub(crate) fn unvisited(&self) -> Vec<BasicCoverageBlock> {
|
||||||
let mut unvisited_set: BitSet<BasicCoverageBlock> =
|
let mut unvisited_set: BitSet<BasicCoverageBlock> =
|
||||||
BitSet::new_filled(self.visited.domain_size());
|
BitSet::new_filled(self.visited.domain_size());
|
||||||
unvisited_set.subtract(&self.visited);
|
unvisited_set.subtract(&self.visited);
|
||||||
|
@ -506,7 +509,7 @@ impl<'a> TraverseCoverageGraphWithLoops<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn find_loop_backedges(
|
fn find_loop_backedges(
|
||||||
basic_coverage_blocks: &CoverageGraph,
|
basic_coverage_blocks: &CoverageGraph,
|
||||||
) -> IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>> {
|
) -> IndexVec<BasicCoverageBlock, Vec<BasicCoverageBlock>> {
|
||||||
let num_bcbs = basic_coverage_blocks.num_nodes();
|
let num_bcbs = basic_coverage_blocks.num_nodes();
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
use rustc_data_structures::captures::Captures;
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_middle::mir;
|
use rustc_middle::mir;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
|
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
|
||||||
use crate::coverage::mappings;
|
use crate::coverage::mappings;
|
||||||
use crate::coverage::spans::from_mir::SpanFromMir;
|
use crate::coverage::spans::from_mir::{
|
||||||
|
extract_covspans_and_holes_from_mir, ExtractedCovspans, Hole, SpanFromMir,
|
||||||
|
};
|
||||||
use crate::coverage::ExtractedHirInfo;
|
use crate::coverage::ExtractedHirInfo;
|
||||||
|
|
||||||
mod from_mir;
|
mod from_mir;
|
||||||
|
@ -19,50 +25,181 @@ pub(super) fn extract_refined_covspans(
|
||||||
basic_coverage_blocks: &CoverageGraph,
|
basic_coverage_blocks: &CoverageGraph,
|
||||||
code_mappings: &mut impl Extend<mappings::CodeMapping>,
|
code_mappings: &mut impl Extend<mappings::CodeMapping>,
|
||||||
) {
|
) {
|
||||||
let sorted_span_buckets =
|
let ExtractedCovspans { mut covspans, mut holes } =
|
||||||
from_mir::mir_to_initial_sorted_coverage_spans(mir_body, hir_info, basic_coverage_blocks);
|
extract_covspans_and_holes_from_mir(mir_body, hir_info, basic_coverage_blocks);
|
||||||
for bucket in sorted_span_buckets {
|
|
||||||
let refined_spans = refine_sorted_spans(bucket);
|
// First, perform the passes that need macro information.
|
||||||
code_mappings.extend(refined_spans.into_iter().map(|RefinedCovspan { span, bcb }| {
|
covspans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb));
|
||||||
|
remove_unwanted_macro_spans(&mut covspans);
|
||||||
|
split_visible_macro_spans(&mut covspans);
|
||||||
|
|
||||||
|
// We no longer need the extra information in `SpanFromMir`, so convert to `Covspan`.
|
||||||
|
let mut covspans = covspans.into_iter().map(SpanFromMir::into_covspan).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let compare_covspans = |a: &Covspan, b: &Covspan| {
|
||||||
|
compare_spans(a.span, b.span)
|
||||||
|
// After deduplication, we want to keep only the most-dominated BCB.
|
||||||
|
.then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb).reverse())
|
||||||
|
};
|
||||||
|
covspans.sort_by(compare_covspans);
|
||||||
|
|
||||||
|
// Among covspans with the same span, keep only one,
|
||||||
|
// preferring the one with the most-dominated BCB.
|
||||||
|
// (Ideally we should try to preserve _all_ non-dominating BCBs, but that
|
||||||
|
// requires a lot more complexity in the span refiner, for little benefit.)
|
||||||
|
covspans.dedup_by(|b, a| a.span.source_equal(b.span));
|
||||||
|
|
||||||
|
// Sort the holes, and merge overlapping/adjacent holes.
|
||||||
|
holes.sort_by(|a, b| compare_spans(a.span, b.span));
|
||||||
|
holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b));
|
||||||
|
|
||||||
|
// Split the covspans into separate buckets that don't overlap any holes.
|
||||||
|
let buckets = divide_spans_into_buckets(covspans, &holes);
|
||||||
|
|
||||||
|
for mut covspans in buckets {
|
||||||
|
// Make sure each individual bucket is internally sorted.
|
||||||
|
covspans.sort_by(compare_covspans);
|
||||||
|
let _span = debug_span!("processing bucket", ?covspans).entered();
|
||||||
|
|
||||||
|
let mut covspans = remove_unwanted_overlapping_spans(covspans);
|
||||||
|
debug!(?covspans, "after removing overlaps");
|
||||||
|
|
||||||
|
// Do one last merge pass, to simplify the output.
|
||||||
|
covspans.dedup_by(|b, a| a.merge_if_eligible(b));
|
||||||
|
debug!(?covspans, "after merge");
|
||||||
|
|
||||||
|
code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| {
|
||||||
// Each span produced by the refiner represents an ordinary code region.
|
// Each span produced by the refiner represents an ordinary code region.
|
||||||
mappings::CodeMapping { span, bcb }
|
mappings::CodeMapping { span, bcb }
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
/// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate
|
||||||
struct RefinedCovspan {
|
/// multiple condition/consequent blocks that have the span of the whole macro
|
||||||
span: Span,
|
/// invocation, which is unhelpful. Keeping only the first such span seems to
|
||||||
bcb: BasicCoverageBlock,
|
/// give better mappings, so remove the others.
|
||||||
|
///
|
||||||
|
/// (The input spans should be sorted in BCB dominator order, so that the
|
||||||
|
/// retained "first" span is likely to dominate the others.)
|
||||||
|
fn remove_unwanted_macro_spans(covspans: &mut Vec<SpanFromMir>) {
|
||||||
|
let mut seen_macro_spans = FxHashSet::default();
|
||||||
|
covspans.retain(|covspan| {
|
||||||
|
// Ignore (retain) non-macro-expansion spans.
|
||||||
|
if covspan.visible_macro.is_none() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retain only the first macro-expanded covspan with this span.
|
||||||
|
seen_macro_spans.insert(covspan.span)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RefinedCovspan {
|
/// When a span corresponds to a macro invocation that is visible from the
|
||||||
fn is_mergeable(&self, other: &Self) -> bool {
|
/// function body, split it into two parts. The first part covers just the
|
||||||
self.bcb == other.bcb
|
/// macro name plus `!`, and the second part covers the rest of the macro
|
||||||
|
/// invocation. This seems to give better results for code that uses macros.
|
||||||
|
fn split_visible_macro_spans(covspans: &mut Vec<SpanFromMir>) {
|
||||||
|
let mut extra_spans = vec![];
|
||||||
|
|
||||||
|
covspans.retain(|covspan| {
|
||||||
|
let Some(visible_macro) = covspan.visible_macro else { return true };
|
||||||
|
|
||||||
|
let split_len = visible_macro.as_str().len() as u32 + 1;
|
||||||
|
let (before, after) = covspan.span.split_at(split_len);
|
||||||
|
if !covspan.span.contains(before) || !covspan.span.contains(after) {
|
||||||
|
// Something is unexpectedly wrong with the split point.
|
||||||
|
// The debug assertion in `split_at` will have already caught this,
|
||||||
|
// but in release builds it's safer to do nothing and maybe get a
|
||||||
|
// bug report for unexpected coverage, rather than risk an ICE.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
extra_spans.push(SpanFromMir::new(before, covspan.visible_macro, covspan.bcb));
|
||||||
|
extra_spans.push(SpanFromMir::new(after, covspan.visible_macro, covspan.bcb));
|
||||||
|
false // Discard the original covspan that we just split.
|
||||||
|
});
|
||||||
|
|
||||||
|
// The newly-split spans are added at the end, so any previous sorting
|
||||||
|
// is not preserved.
|
||||||
|
covspans.extend(extra_spans);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Uses the holes to divide the given covspans into buckets, such that:
|
||||||
|
/// - No span in any hole overlaps a bucket (truncating the spans if necessary).
|
||||||
|
/// - The spans in each bucket are strictly after all spans in previous buckets,
|
||||||
|
/// and strictly before all spans in subsequent buckets.
|
||||||
|
///
|
||||||
|
/// The resulting buckets are sorted relative to each other, but might not be
|
||||||
|
/// internally sorted.
|
||||||
|
#[instrument(level = "debug")]
|
||||||
|
fn divide_spans_into_buckets(input_covspans: Vec<Covspan>, holes: &[Hole]) -> Vec<Vec<Covspan>> {
|
||||||
|
debug_assert!(input_covspans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
|
||||||
|
debug_assert!(holes.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
|
||||||
|
|
||||||
|
// Now we're ready to start carving holes out of the initial coverage spans,
|
||||||
|
// and grouping them in buckets separated by the holes.
|
||||||
|
|
||||||
|
let mut input_covspans = VecDeque::from(input_covspans);
|
||||||
|
let mut fragments = vec![];
|
||||||
|
|
||||||
|
// For each hole:
|
||||||
|
// - Identify the spans that are entirely or partly before the hole.
|
||||||
|
// - Put those spans in a corresponding bucket, truncated to the start of the hole.
|
||||||
|
// - If one of those spans also extends after the hole, put the rest of it
|
||||||
|
// in a "fragments" vector that is processed by the next hole.
|
||||||
|
let mut buckets = (0..holes.len()).map(|_| vec![]).collect::<Vec<_>>();
|
||||||
|
for (hole, bucket) in holes.iter().zip(&mut buckets) {
|
||||||
|
let fragments_from_prev = std::mem::take(&mut fragments);
|
||||||
|
|
||||||
|
// Only inspect spans that precede or overlap this hole,
|
||||||
|
// leaving the rest to be inspected by later holes.
|
||||||
|
// (This relies on the spans and holes both being sorted.)
|
||||||
|
let relevant_input_covspans =
|
||||||
|
drain_front_while(&mut input_covspans, |c| c.span.lo() < hole.span.hi());
|
||||||
|
|
||||||
|
for covspan in fragments_from_prev.into_iter().chain(relevant_input_covspans) {
|
||||||
|
let (before, after) = covspan.split_around_hole_span(hole.span);
|
||||||
|
bucket.extend(before);
|
||||||
|
fragments.extend(after);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn merge_from(&mut self, other: &Self) {
|
// After finding the spans before each hole, any remaining fragments/spans
|
||||||
debug_assert!(self.is_mergeable(other));
|
// form their own final bucket, after the final hole.
|
||||||
self.span = self.span.to(other.span);
|
// (If there were no holes, this will just be all of the initial spans.)
|
||||||
}
|
fragments.extend(input_covspans);
|
||||||
|
buckets.push(fragments);
|
||||||
|
|
||||||
|
buckets
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Similar to `.drain(..)`, but stops just before it would remove an item not
|
||||||
|
/// satisfying the predicate.
|
||||||
|
fn drain_front_while<'a, T>(
|
||||||
|
queue: &'a mut VecDeque<T>,
|
||||||
|
mut pred_fn: impl FnMut(&T) -> bool,
|
||||||
|
) -> impl Iterator<Item = T> + Captures<'a> {
|
||||||
|
std::iter::from_fn(move || if pred_fn(queue.front()?) { queue.pop_front() } else { None })
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes one of the buckets of (sorted) spans extracted from MIR, and "refines"
|
/// Takes one of the buckets of (sorted) spans extracted from MIR, and "refines"
|
||||||
/// those spans by removing spans that overlap in unwanted ways, and by merging
|
/// those spans by removing spans that overlap in unwanted ways.
|
||||||
/// compatible adjacent spans.
|
|
||||||
#[instrument(level = "debug")]
|
#[instrument(level = "debug")]
|
||||||
fn refine_sorted_spans(sorted_spans: Vec<SpanFromMir>) -> Vec<RefinedCovspan> {
|
fn remove_unwanted_overlapping_spans(sorted_spans: Vec<Covspan>) -> Vec<Covspan> {
|
||||||
|
debug_assert!(sorted_spans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
|
||||||
|
|
||||||
// Holds spans that have been read from the input vector, but haven't yet
|
// Holds spans that have been read from the input vector, but haven't yet
|
||||||
// been committed to the output vector.
|
// been committed to the output vector.
|
||||||
let mut pending = vec![];
|
let mut pending = vec![];
|
||||||
let mut refined = vec![];
|
let mut refined = vec![];
|
||||||
|
|
||||||
for curr in sorted_spans {
|
for curr in sorted_spans {
|
||||||
pending.retain(|prev: &SpanFromMir| {
|
pending.retain(|prev: &Covspan| {
|
||||||
if prev.span.hi() <= curr.span.lo() {
|
if prev.span.hi() <= curr.span.lo() {
|
||||||
// There's no overlap between the previous/current covspans,
|
// There's no overlap between the previous/current covspans,
|
||||||
// so move the previous one into the refined list.
|
// so move the previous one into the refined list.
|
||||||
refined.push(RefinedCovspan { span: prev.span, bcb: prev.bcb });
|
refined.push(prev.clone());
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, retain the previous covspan only if it has the
|
// Otherwise, retain the previous covspan only if it has the
|
||||||
|
@ -75,22 +212,57 @@ fn refine_sorted_spans(sorted_spans: Vec<SpanFromMir>) -> Vec<RefinedCovspan> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drain the rest of the pending list into the refined list.
|
// Drain the rest of the pending list into the refined list.
|
||||||
for prev in pending {
|
refined.extend(pending);
|
||||||
refined.push(RefinedCovspan { span: prev.span, bcb: prev.bcb });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do one last merge pass, to simplify the output.
|
|
||||||
debug!(?refined, "before merge");
|
|
||||||
refined.dedup_by(|b, a| {
|
|
||||||
if a.is_mergeable(b) {
|
|
||||||
debug!(?a, ?b, "merging list-adjacent refined spans");
|
|
||||||
a.merge_from(b);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
debug!(?refined, "after merge");
|
|
||||||
|
|
||||||
refined
|
refined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct Covspan {
|
||||||
|
span: Span,
|
||||||
|
bcb: BasicCoverageBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Covspan {
|
||||||
|
/// Splits this covspan into 0-2 parts:
|
||||||
|
/// - The part that is strictly before the hole span, if any.
|
||||||
|
/// - The part that is strictly after the hole span, if any.
|
||||||
|
fn split_around_hole_span(&self, hole_span: Span) -> (Option<Self>, Option<Self>) {
|
||||||
|
let before = try {
|
||||||
|
let span = self.span.trim_end(hole_span)?;
|
||||||
|
Self { span, ..*self }
|
||||||
|
};
|
||||||
|
let after = try {
|
||||||
|
let span = self.span.trim_start(hole_span)?;
|
||||||
|
Self { span, ..*self }
|
||||||
|
};
|
||||||
|
|
||||||
|
(before, after)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If `self` and `other` can be merged (i.e. they have the same BCB),
|
||||||
|
/// mutates `self.span` to also include `other.span` and returns true.
|
||||||
|
///
|
||||||
|
/// Note that compatible covspans can be merged even if their underlying
|
||||||
|
/// spans are not overlapping/adjacent; any space between them will also be
|
||||||
|
/// part of the merged covspan.
|
||||||
|
fn merge_if_eligible(&mut self, other: &Self) -> bool {
|
||||||
|
if self.bcb != other.bcb {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.span = self.span.to(other.span);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compares two spans in (lo ascending, hi descending) order.
|
||||||
|
fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering {
|
||||||
|
// First sort by span start.
|
||||||
|
Ord::cmp(&a.lo(), &b.lo())
|
||||||
|
// If span starts are the same, sort by span end in reverse order.
|
||||||
|
// This ensures that if spans A and B are adjacent in the list,
|
||||||
|
// and they overlap but are not equal, then either:
|
||||||
|
// - Span A extends further left, or
|
||||||
|
// - Both have the same start and span A extends further right
|
||||||
|
.then_with(|| Ord::cmp(&a.hi(), &b.hi()).reverse())
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
use std::collections::VecDeque;
|
|
||||||
|
|
||||||
use rustc_data_structures::captures::Captures;
|
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
|
||||||
use rustc_middle::bug;
|
use rustc_middle::bug;
|
||||||
use rustc_middle::mir::coverage::CoverageKind;
|
use rustc_middle::mir::coverage::CoverageKind;
|
||||||
use rustc_middle::mir::{
|
use rustc_middle::mir::{
|
||||||
|
@ -13,25 +9,25 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
|
||||||
use crate::coverage::graph::{
|
use crate::coverage::graph::{
|
||||||
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
|
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
|
||||||
};
|
};
|
||||||
|
use crate::coverage::spans::Covspan;
|
||||||
use crate::coverage::ExtractedHirInfo;
|
use crate::coverage::ExtractedHirInfo;
|
||||||
|
|
||||||
|
pub(crate) struct ExtractedCovspans {
|
||||||
|
pub(crate) covspans: Vec<SpanFromMir>,
|
||||||
|
pub(crate) holes: Vec<Hole>,
|
||||||
|
}
|
||||||
|
|
||||||
/// Traverses the MIR body to produce an initial collection of coverage-relevant
|
/// Traverses the MIR body to produce an initial collection of coverage-relevant
|
||||||
/// spans, each associated with a node in the coverage graph (BCB) and possibly
|
/// spans, each associated with a node in the coverage graph (BCB) and possibly
|
||||||
/// other metadata.
|
/// other metadata.
|
||||||
///
|
pub(crate) fn extract_covspans_and_holes_from_mir(
|
||||||
/// The returned spans are divided into one or more buckets, such that:
|
|
||||||
/// - The spans in each bucket are strictly after all spans in previous buckets,
|
|
||||||
/// and strictly before all spans in subsequent buckets.
|
|
||||||
/// - The contents of each bucket are also sorted, in a specific order that is
|
|
||||||
/// expected by the subsequent span-refinement step.
|
|
||||||
pub(super) fn mir_to_initial_sorted_coverage_spans(
|
|
||||||
mir_body: &mir::Body<'_>,
|
mir_body: &mir::Body<'_>,
|
||||||
hir_info: &ExtractedHirInfo,
|
hir_info: &ExtractedHirInfo,
|
||||||
basic_coverage_blocks: &CoverageGraph,
|
basic_coverage_blocks: &CoverageGraph,
|
||||||
) -> Vec<Vec<SpanFromMir>> {
|
) -> ExtractedCovspans {
|
||||||
let &ExtractedHirInfo { body_span, .. } = hir_info;
|
let &ExtractedHirInfo { body_span, .. } = hir_info;
|
||||||
|
|
||||||
let mut initial_spans = vec![];
|
let mut covspans = vec![];
|
||||||
let mut holes = vec![];
|
let mut holes = vec![];
|
||||||
|
|
||||||
for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() {
|
for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() {
|
||||||
|
@ -40,150 +36,21 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
|
||||||
body_span,
|
body_span,
|
||||||
bcb,
|
bcb,
|
||||||
bcb_data,
|
bcb_data,
|
||||||
&mut initial_spans,
|
&mut covspans,
|
||||||
&mut holes,
|
&mut holes,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only add the signature span if we found at least one span in the body.
|
// Only add the signature span if we found at least one span in the body.
|
||||||
if !initial_spans.is_empty() || !holes.is_empty() {
|
if !covspans.is_empty() || !holes.is_empty() {
|
||||||
// If there is no usable signature span, add a fake one (before refinement)
|
// If there is no usable signature span, add a fake one (before refinement)
|
||||||
// to avoid an ugly gap between the body start and the first real span.
|
// to avoid an ugly gap between the body start and the first real span.
|
||||||
// FIXME: Find a more principled way to solve this problem.
|
// FIXME: Find a more principled way to solve this problem.
|
||||||
let fn_sig_span = hir_info.fn_sig_span_extended.unwrap_or_else(|| body_span.shrink_to_lo());
|
let fn_sig_span = hir_info.fn_sig_span_extended.unwrap_or_else(|| body_span.shrink_to_lo());
|
||||||
initial_spans.push(SpanFromMir::for_fn_sig(fn_sig_span));
|
covspans.push(SpanFromMir::for_fn_sig(fn_sig_span));
|
||||||
}
|
}
|
||||||
|
|
||||||
initial_spans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb));
|
ExtractedCovspans { covspans, holes }
|
||||||
remove_unwanted_macro_spans(&mut initial_spans);
|
|
||||||
split_visible_macro_spans(&mut initial_spans);
|
|
||||||
|
|
||||||
let compare_covspans = |a: &SpanFromMir, b: &SpanFromMir| {
|
|
||||||
compare_spans(a.span, b.span)
|
|
||||||
// After deduplication, we want to keep only the most-dominated BCB.
|
|
||||||
.then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb).reverse())
|
|
||||||
};
|
|
||||||
initial_spans.sort_by(compare_covspans);
|
|
||||||
|
|
||||||
// Among covspans with the same span, keep only one,
|
|
||||||
// preferring the one with the most-dominated BCB.
|
|
||||||
// (Ideally we should try to preserve _all_ non-dominating BCBs, but that
|
|
||||||
// requires a lot more complexity in the span refiner, for little benefit.)
|
|
||||||
initial_spans.dedup_by(|b, a| a.span.source_equal(b.span));
|
|
||||||
|
|
||||||
// Sort the holes, and merge overlapping/adjacent holes.
|
|
||||||
holes.sort_by(|a, b| compare_spans(a.span, b.span));
|
|
||||||
holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b));
|
|
||||||
|
|
||||||
// Now we're ready to start carving holes out of the initial coverage spans,
|
|
||||||
// and grouping them in buckets separated by the holes.
|
|
||||||
|
|
||||||
let mut initial_spans = VecDeque::from(initial_spans);
|
|
||||||
let mut fragments: Vec<SpanFromMir> = vec![];
|
|
||||||
|
|
||||||
// For each hole:
|
|
||||||
// - Identify the spans that are entirely or partly before the hole.
|
|
||||||
// - Put those spans in a corresponding bucket, truncated to the start of the hole.
|
|
||||||
// - If one of those spans also extends after the hole, put the rest of it
|
|
||||||
// in a "fragments" vector that is processed by the next hole.
|
|
||||||
let mut buckets = (0..holes.len()).map(|_| vec![]).collect::<Vec<_>>();
|
|
||||||
for (hole, bucket) in holes.iter().zip(&mut buckets) {
|
|
||||||
let fragments_from_prev = std::mem::take(&mut fragments);
|
|
||||||
|
|
||||||
// Only inspect spans that precede or overlap this hole,
|
|
||||||
// leaving the rest to be inspected by later holes.
|
|
||||||
// (This relies on the spans and holes both being sorted.)
|
|
||||||
let relevant_initial_spans =
|
|
||||||
drain_front_while(&mut initial_spans, |c| c.span.lo() < hole.span.hi());
|
|
||||||
|
|
||||||
for covspan in fragments_from_prev.into_iter().chain(relevant_initial_spans) {
|
|
||||||
let (before, after) = covspan.split_around_hole_span(hole.span);
|
|
||||||
bucket.extend(before);
|
|
||||||
fragments.extend(after);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// After finding the spans before each hole, any remaining fragments/spans
|
|
||||||
// form their own final bucket, after the final hole.
|
|
||||||
// (If there were no holes, this will just be all of the initial spans.)
|
|
||||||
fragments.extend(initial_spans);
|
|
||||||
buckets.push(fragments);
|
|
||||||
|
|
||||||
// Make sure each individual bucket is still internally sorted.
|
|
||||||
for bucket in &mut buckets {
|
|
||||||
bucket.sort_by(compare_covspans);
|
|
||||||
}
|
|
||||||
buckets
|
|
||||||
}
|
|
||||||
|
|
||||||
fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering {
|
|
||||||
// First sort by span start.
|
|
||||||
Ord::cmp(&a.lo(), &b.lo())
|
|
||||||
// If span starts are the same, sort by span end in reverse order.
|
|
||||||
// This ensures that if spans A and B are adjacent in the list,
|
|
||||||
// and they overlap but are not equal, then either:
|
|
||||||
// - Span A extends further left, or
|
|
||||||
// - Both have the same start and span A extends further right
|
|
||||||
.then_with(|| Ord::cmp(&a.hi(), &b.hi()).reverse())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Similar to `.drain(..)`, but stops just before it would remove an item not
|
|
||||||
/// satisfying the predicate.
|
|
||||||
fn drain_front_while<'a, T>(
|
|
||||||
queue: &'a mut VecDeque<T>,
|
|
||||||
mut pred_fn: impl FnMut(&T) -> bool,
|
|
||||||
) -> impl Iterator<Item = T> + Captures<'a> {
|
|
||||||
std::iter::from_fn(move || if pred_fn(queue.front()?) { queue.pop_front() } else { None })
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate
|
|
||||||
/// multiple condition/consequent blocks that have the span of the whole macro
|
|
||||||
/// invocation, which is unhelpful. Keeping only the first such span seems to
|
|
||||||
/// give better mappings, so remove the others.
|
|
||||||
///
|
|
||||||
/// (The input spans should be sorted in BCB dominator order, so that the
|
|
||||||
/// retained "first" span is likely to dominate the others.)
|
|
||||||
fn remove_unwanted_macro_spans(initial_spans: &mut Vec<SpanFromMir>) {
|
|
||||||
let mut seen_macro_spans = FxHashSet::default();
|
|
||||||
initial_spans.retain(|covspan| {
|
|
||||||
// Ignore (retain) non-macro-expansion spans.
|
|
||||||
if covspan.visible_macro.is_none() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retain only the first macro-expanded covspan with this span.
|
|
||||||
seen_macro_spans.insert(covspan.span)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// When a span corresponds to a macro invocation that is visible from the
|
|
||||||
/// function body, split it into two parts. The first part covers just the
|
|
||||||
/// macro name plus `!`, and the second part covers the rest of the macro
|
|
||||||
/// invocation. This seems to give better results for code that uses macros.
|
|
||||||
fn split_visible_macro_spans(initial_spans: &mut Vec<SpanFromMir>) {
|
|
||||||
let mut extra_spans = vec![];
|
|
||||||
|
|
||||||
initial_spans.retain(|covspan| {
|
|
||||||
let Some(visible_macro) = covspan.visible_macro else { return true };
|
|
||||||
|
|
||||||
let split_len = visible_macro.as_str().len() as u32 + 1;
|
|
||||||
let (before, after) = covspan.span.split_at(split_len);
|
|
||||||
if !covspan.span.contains(before) || !covspan.span.contains(after) {
|
|
||||||
// Something is unexpectedly wrong with the split point.
|
|
||||||
// The debug assertion in `split_at` will have already caught this,
|
|
||||||
// but in release builds it's safer to do nothing and maybe get a
|
|
||||||
// bug report for unexpected coverage, rather than risk an ICE.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
extra_spans.push(SpanFromMir::new(before, covspan.visible_macro, covspan.bcb));
|
|
||||||
extra_spans.push(SpanFromMir::new(after, covspan.visible_macro, covspan.bcb));
|
|
||||||
false // Discard the original covspan that we just split.
|
|
||||||
});
|
|
||||||
|
|
||||||
// The newly-split spans are added at the end, so any previous sorting
|
|
||||||
// is not preserved.
|
|
||||||
initial_spans.extend(extra_spans);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a set of coverage spans from the filtered set of `Statement`s and `Terminator`s of
|
// Generate a set of coverage spans from the filtered set of `Statement`s and `Terminator`s of
|
||||||
|
@ -402,12 +269,12 @@ fn unexpand_into_body_span_with_prev(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
struct Hole {
|
pub(crate) struct Hole {
|
||||||
span: Span,
|
pub(crate) span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Hole {
|
impl Hole {
|
||||||
fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool {
|
pub(crate) fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool {
|
||||||
if !self.span.overlaps_or_adjacent(other.span) {
|
if !self.span.overlaps_or_adjacent(other.span) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -418,7 +285,7 @@ impl Hole {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) struct SpanFromMir {
|
pub(crate) struct SpanFromMir {
|
||||||
/// A span that has been extracted from MIR and then "un-expanded" back to
|
/// A span that has been extracted from MIR and then "un-expanded" back to
|
||||||
/// within the current function's `body_span`. After various intermediate
|
/// within the current function's `body_span`. After various intermediate
|
||||||
/// processing steps, this span is emitted as part of the final coverage
|
/// processing steps, this span is emitted as part of the final coverage
|
||||||
|
@ -426,9 +293,9 @@ pub(super) struct SpanFromMir {
|
||||||
///
|
///
|
||||||
/// With the exception of `fn_sig_span`, this should always be contained
|
/// With the exception of `fn_sig_span`, this should always be contained
|
||||||
/// within `body_span`.
|
/// within `body_span`.
|
||||||
pub(super) span: Span,
|
pub(crate) span: Span,
|
||||||
visible_macro: Option<Symbol>,
|
pub(crate) visible_macro: Option<Symbol>,
|
||||||
pub(super) bcb: BasicCoverageBlock,
|
pub(crate) bcb: BasicCoverageBlock,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SpanFromMir {
|
impl SpanFromMir {
|
||||||
|
@ -436,23 +303,12 @@ impl SpanFromMir {
|
||||||
Self::new(fn_sig_span, None, START_BCB)
|
Self::new(fn_sig_span, None, START_BCB)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(span: Span, visible_macro: Option<Symbol>, bcb: BasicCoverageBlock) -> Self {
|
pub(crate) fn new(span: Span, visible_macro: Option<Symbol>, bcb: BasicCoverageBlock) -> Self {
|
||||||
Self { span, visible_macro, bcb }
|
Self { span, visible_macro, bcb }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Splits this span into 0-2 parts:
|
pub(crate) fn into_covspan(self) -> Covspan {
|
||||||
/// - The part that is strictly before the hole span, if any.
|
let Self { span, visible_macro: _, bcb } = self;
|
||||||
/// - The part that is strictly after the hole span, if any.
|
Covspan { span, bcb }
|
||||||
fn split_around_hole_span(&self, hole_span: Span) -> (Option<Self>, Option<Self>) {
|
|
||||||
let before = try {
|
|
||||||
let span = self.span.trim_end(hole_span)?;
|
|
||||||
Self { span, ..*self }
|
|
||||||
};
|
|
||||||
let after = try {
|
|
||||||
let span = self.span.trim_start(hole_span)?;
|
|
||||||
Self { span, ..*self }
|
|
||||||
};
|
|
||||||
|
|
||||||
(before, after)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@
|
||||||
//! globals is comparatively simpler. The easiest way is to wrap the test in a closure argument
|
//! globals is comparatively simpler. The easiest way is to wrap the test in a closure argument
|
||||||
//! to: `rustc_span::create_default_session_globals_then(|| { test_here(); })`.
|
//! to: `rustc_span::create_default_session_globals_then(|| { test_here(); })`.
|
||||||
|
|
||||||
use super::counters;
|
|
||||||
use super::graph::{self, BasicCoverageBlock};
|
use super::graph::{self, BasicCoverageBlock};
|
||||||
|
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -551,108 +550,3 @@ fn test_covgraph_switchint_loop_then_inner_loop_else_break() {
|
||||||
assert_successors(&basic_coverage_blocks, bcb(5), &[bcb(1)]);
|
assert_successors(&basic_coverage_blocks, bcb(5), &[bcb(1)]);
|
||||||
assert_successors(&basic_coverage_blocks, bcb(6), &[bcb(4)]);
|
assert_successors(&basic_coverage_blocks, bcb(6), &[bcb(4)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_find_loop_backedges_none() {
|
|
||||||
let mir_body = goto_switchint();
|
|
||||||
let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
|
|
||||||
if false {
|
|
||||||
eprintln!(
|
|
||||||
"basic_coverage_blocks = {:?}",
|
|
||||||
basic_coverage_blocks.iter_enumerated().collect::<Vec<_>>()
|
|
||||||
);
|
|
||||||
eprintln!("successors = {:?}", basic_coverage_blocks.successors);
|
|
||||||
}
|
|
||||||
let backedges = graph::find_loop_backedges(&basic_coverage_blocks);
|
|
||||||
assert_eq!(
|
|
||||||
backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::<usize>(),
|
|
||||||
0,
|
|
||||||
"backedges: {:?}",
|
|
||||||
backedges
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_find_loop_backedges_one() {
|
|
||||||
let mir_body = switchint_then_loop_else_return();
|
|
||||||
let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
|
|
||||||
let backedges = graph::find_loop_backedges(&basic_coverage_blocks);
|
|
||||||
assert_eq!(
|
|
||||||
backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::<usize>(),
|
|
||||||
1,
|
|
||||||
"backedges: {:?}",
|
|
||||||
backedges
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(backedges[bcb(1)], &[bcb(3)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_find_loop_backedges_two() {
|
|
||||||
let mir_body = switchint_loop_then_inner_loop_else_break();
|
|
||||||
let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
|
|
||||||
let backedges = graph::find_loop_backedges(&basic_coverage_blocks);
|
|
||||||
assert_eq!(
|
|
||||||
backedges.iter_enumerated().map(|(_bcb, backedges)| backedges.len()).sum::<usize>(),
|
|
||||||
2,
|
|
||||||
"backedges: {:?}",
|
|
||||||
backedges
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(backedges[bcb(1)], &[bcb(5)]);
|
|
||||||
assert_eq!(backedges[bcb(4)], &[bcb(6)]);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_traverse_coverage_with_loops() {
|
|
||||||
let mir_body = switchint_loop_then_inner_loop_else_break();
|
|
||||||
let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
|
|
||||||
let mut traversed_in_order = Vec::new();
|
|
||||||
let mut traversal = graph::TraverseCoverageGraphWithLoops::new(&basic_coverage_blocks);
|
|
||||||
while let Some(bcb) = traversal.next() {
|
|
||||||
traversed_in_order.push(bcb);
|
|
||||||
}
|
|
||||||
|
|
||||||
// bcb0 is visited first. Then bcb1 starts the first loop, and all remaining nodes, *except*
|
|
||||||
// bcb6 are inside the first loop.
|
|
||||||
assert_eq!(
|
|
||||||
*traversed_in_order.last().expect("should have elements"),
|
|
||||||
bcb(6),
|
|
||||||
"bcb6 should not be visited until all nodes inside the first loop have been visited"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_make_bcb_counters() {
|
|
||||||
rustc_span::create_default_session_globals_then(|| {
|
|
||||||
let mir_body = goto_switchint();
|
|
||||||
let basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
|
|
||||||
// Historically this test would use `spans` internals to set up fake
|
|
||||||
// coverage spans for BCBs 1 and 2. Now we skip that step and just tell
|
|
||||||
// BCB counter construction that those BCBs have spans.
|
|
||||||
let bcb_has_coverage_spans = |bcb: BasicCoverageBlock| (1..=2).contains(&bcb.as_usize());
|
|
||||||
let coverage_counters = counters::CoverageCounters::make_bcb_counters(
|
|
||||||
&basic_coverage_blocks,
|
|
||||||
bcb_has_coverage_spans,
|
|
||||||
);
|
|
||||||
assert_eq!(coverage_counters.num_expressions(), 0);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
0, // bcb1 has a `Counter` with id = 0
|
|
||||||
match coverage_counters.bcb_counter(bcb(1)).expect("should have a counter") {
|
|
||||||
counters::BcbCounter::Counter { id, .. } => id,
|
|
||||||
_ => panic!("expected a Counter"),
|
|
||||||
}
|
|
||||||
.as_u32()
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(
|
|
||||||
1, // bcb2 has a `Counter` with id = 1
|
|
||||||
match coverage_counters.bcb_counter(bcb(2)).expect("should have a counter") {
|
|
||||||
counters::BcbCounter::Counter { id, .. } => id,
|
|
||||||
_ => panic!("expected a Counter"),
|
|
||||||
}
|
|
||||||
.as_u32()
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
|
@ -310,9 +310,10 @@ enum LifetimeRibKind {
|
||||||
/// error on default object bounds (e.g., `Box<dyn Foo>`).
|
/// error on default object bounds (e.g., `Box<dyn Foo>`).
|
||||||
AnonymousReportError,
|
AnonymousReportError,
|
||||||
|
|
||||||
/// Resolves elided lifetimes to `'static`, but gives a warning that this behavior
|
/// Resolves elided lifetimes to `'static` if there are no other lifetimes in scope,
|
||||||
/// is a bug and will be reverted soon.
|
/// otherwise give a warning that the previous behavior of introducing a new early-bound
|
||||||
AnonymousWarn(NodeId),
|
/// lifetime is a bug and will be removed (if `emit_lint` is enabled).
|
||||||
|
StaticIfNoLifetimeInScope { lint_id: NodeId, emit_lint: bool },
|
||||||
|
|
||||||
/// Signal we cannot find which should be the anonymous lifetime.
|
/// Signal we cannot find which should be the anonymous lifetime.
|
||||||
ElisionFailure,
|
ElisionFailure,
|
||||||
|
@ -1212,7 +1213,7 @@ impl<'a: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'a, '_, 'ast,
|
||||||
}
|
}
|
||||||
LifetimeRibKind::AnonymousCreateParameter { .. }
|
LifetimeRibKind::AnonymousCreateParameter { .. }
|
||||||
| LifetimeRibKind::AnonymousReportError
|
| LifetimeRibKind::AnonymousReportError
|
||||||
| LifetimeRibKind::AnonymousWarn(_)
|
| LifetimeRibKind::StaticIfNoLifetimeInScope { .. }
|
||||||
| LifetimeRibKind::Elided(_)
|
| LifetimeRibKind::Elided(_)
|
||||||
| LifetimeRibKind::ElisionFailure
|
| LifetimeRibKind::ElisionFailure
|
||||||
| LifetimeRibKind::ConcreteAnonConst(_)
|
| LifetimeRibKind::ConcreteAnonConst(_)
|
||||||
|
@ -1580,7 +1581,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||||
// lifetime would be illegal.
|
// lifetime would be illegal.
|
||||||
LifetimeRibKind::Item
|
LifetimeRibKind::Item
|
||||||
| LifetimeRibKind::AnonymousReportError
|
| LifetimeRibKind::AnonymousReportError
|
||||||
| LifetimeRibKind::AnonymousWarn(_)
|
| LifetimeRibKind::StaticIfNoLifetimeInScope { .. }
|
||||||
| LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many),
|
| LifetimeRibKind::ElisionFailure => Some(LifetimeUseSet::Many),
|
||||||
// An anonymous lifetime is legal here, and bound to the right
|
// An anonymous lifetime is legal here, and bound to the right
|
||||||
// place, go ahead.
|
// place, go ahead.
|
||||||
|
@ -1643,7 +1644,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||||
| LifetimeRibKind::Generics { .. }
|
| LifetimeRibKind::Generics { .. }
|
||||||
| LifetimeRibKind::ElisionFailure
|
| LifetimeRibKind::ElisionFailure
|
||||||
| LifetimeRibKind::AnonymousReportError
|
| LifetimeRibKind::AnonymousReportError
|
||||||
| LifetimeRibKind::AnonymousWarn(_) => {}
|
| LifetimeRibKind::StaticIfNoLifetimeInScope { .. } => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1677,16 +1678,36 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||||
self.record_lifetime_res(lifetime.id, res, elision_candidate);
|
self.record_lifetime_res(lifetime.id, res, elision_candidate);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
LifetimeRibKind::AnonymousWarn(node_id) => {
|
LifetimeRibKind::StaticIfNoLifetimeInScope { lint_id: node_id, emit_lint } => {
|
||||||
self.r.lint_buffer.buffer_lint(
|
let mut lifetimes_in_scope = vec![];
|
||||||
lint::builtin::ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
|
for rib in &self.lifetime_ribs[..i] {
|
||||||
node_id,
|
lifetimes_in_scope.extend(rib.bindings.iter().map(|(ident, _)| ident.span));
|
||||||
lifetime.ident.span,
|
// Consider any anonymous lifetimes, too
|
||||||
lint::BuiltinLintDiag::AssociatedConstElidedLifetime {
|
if let LifetimeRibKind::AnonymousCreateParameter { binder, .. } = rib.kind
|
||||||
elided,
|
&& let Some(extra) = self.r.extra_lifetime_params_map.get(&binder)
|
||||||
span: lifetime.ident.span,
|
{
|
||||||
},
|
lifetimes_in_scope.extend(extra.iter().map(|(ident, _, _)| ident.span));
|
||||||
);
|
}
|
||||||
|
}
|
||||||
|
if lifetimes_in_scope.is_empty() {
|
||||||
|
self.record_lifetime_res(
|
||||||
|
lifetime.id,
|
||||||
|
LifetimeRes::Static,
|
||||||
|
elision_candidate,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
} else if emit_lint {
|
||||||
|
self.r.lint_buffer.buffer_lint(
|
||||||
|
lint::builtin::ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
|
||||||
|
node_id,
|
||||||
|
lifetime.ident.span,
|
||||||
|
lint::BuiltinLintDiag::AssociatedConstElidedLifetime {
|
||||||
|
elided,
|
||||||
|
span: lifetime.ident.span,
|
||||||
|
lifetimes_in_scope: lifetimes_in_scope.into(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
LifetimeRibKind::AnonymousReportError => {
|
LifetimeRibKind::AnonymousReportError => {
|
||||||
if elided {
|
if elided {
|
||||||
|
@ -1904,7 +1925,7 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||||
// impl Foo for std::cell::Ref<u32> // note lack of '_
|
// impl Foo for std::cell::Ref<u32> // note lack of '_
|
||||||
// async fn foo(_: std::cell::Ref<u32>) { ... }
|
// async fn foo(_: std::cell::Ref<u32>) { ... }
|
||||||
LifetimeRibKind::AnonymousCreateParameter { report_in_path: true, .. }
|
LifetimeRibKind::AnonymousCreateParameter { report_in_path: true, .. }
|
||||||
| LifetimeRibKind::AnonymousWarn(_) => {
|
| LifetimeRibKind::StaticIfNoLifetimeInScope { .. } => {
|
||||||
let sess = self.r.tcx.sess;
|
let sess = self.r.tcx.sess;
|
||||||
let subdiag = rustc_errors::elided_lifetime_in_path_suggestion(
|
let subdiag = rustc_errors::elided_lifetime_in_path_suggestion(
|
||||||
sess.source_map(),
|
sess.source_map(),
|
||||||
|
@ -2838,19 +2859,27 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||||
kind: LifetimeBinderKind::ConstItem,
|
kind: LifetimeBinderKind::ConstItem,
|
||||||
},
|
},
|
||||||
|this| {
|
|this| {
|
||||||
this.visit_generics(generics);
|
this.with_lifetime_rib(
|
||||||
this.visit_ty(ty);
|
LifetimeRibKind::StaticIfNoLifetimeInScope {
|
||||||
|
lint_id: item.id,
|
||||||
|
emit_lint: false,
|
||||||
|
},
|
||||||
|
|this| {
|
||||||
|
this.visit_generics(generics);
|
||||||
|
this.visit_ty(ty);
|
||||||
|
|
||||||
// Only impose the restrictions of `ConstRibKind` for an
|
// Only impose the restrictions of `ConstRibKind` for an
|
||||||
// actual constant expression in a provided default.
|
// actual constant expression in a provided default.
|
||||||
if let Some(expr) = expr {
|
if let Some(expr) = expr {
|
||||||
// We allow arbitrary const expressions inside of associated consts,
|
// We allow arbitrary const expressions inside of associated consts,
|
||||||
// even if they are potentially not const evaluatable.
|
// even if they are potentially not const evaluatable.
|
||||||
//
|
//
|
||||||
// Type parameters can already be used and as associated consts are
|
// Type parameters can already be used and as associated consts are
|
||||||
// not used as part of the type system, this is far less surprising.
|
// not used as part of the type system, this is far less surprising.
|
||||||
this.resolve_const_body(expr, None);
|
this.resolve_const_body(expr, None);
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -3030,30 +3059,37 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||||
kind: LifetimeBinderKind::ConstItem,
|
kind: LifetimeBinderKind::ConstItem,
|
||||||
},
|
},
|
||||||
|this| {
|
|this| {
|
||||||
this.with_lifetime_rib(LifetimeRibKind::AnonymousWarn(item.id), |this| {
|
this.with_lifetime_rib(
|
||||||
// If this is a trait impl, ensure the const
|
LifetimeRibKind::StaticIfNoLifetimeInScope {
|
||||||
// exists in trait
|
lint_id: item.id,
|
||||||
this.check_trait_item(
|
// In impls, it's not a hard error yet due to backcompat.
|
||||||
item.id,
|
emit_lint: true,
|
||||||
item.ident,
|
},
|
||||||
&item.kind,
|
|this| {
|
||||||
ValueNS,
|
// If this is a trait impl, ensure the const
|
||||||
item.span,
|
// exists in trait
|
||||||
seen_trait_items,
|
this.check_trait_item(
|
||||||
|i, s, c| ConstNotMemberOfTrait(i, s, c),
|
item.id,
|
||||||
);
|
item.ident,
|
||||||
|
&item.kind,
|
||||||
|
ValueNS,
|
||||||
|
item.span,
|
||||||
|
seen_trait_items,
|
||||||
|
|i, s, c| ConstNotMemberOfTrait(i, s, c),
|
||||||
|
);
|
||||||
|
|
||||||
this.visit_generics(generics);
|
this.visit_generics(generics);
|
||||||
this.visit_ty(ty);
|
this.visit_ty(ty);
|
||||||
if let Some(expr) = expr {
|
if let Some(expr) = expr {
|
||||||
// We allow arbitrary const expressions inside of associated consts,
|
// We allow arbitrary const expressions inside of associated consts,
|
||||||
// even if they are potentially not const evaluatable.
|
// even if they are potentially not const evaluatable.
|
||||||
//
|
//
|
||||||
// Type parameters can already be used and as associated consts are
|
// Type parameters can already be used and as associated consts are
|
||||||
// not used as part of the type system, this is far less surprising.
|
// not used as part of the type system, this is far less surprising.
|
||||||
this.resolve_const_body(expr, None);
|
this.resolve_const_body(expr, None);
|
||||||
}
|
}
|
||||||
});
|
},
|
||||||
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -928,7 +928,7 @@ pub(crate) mod tags {
|
||||||
/// An `Option` with a type tag `I`.
|
/// An `Option` with a type tag `I`.
|
||||||
///
|
///
|
||||||
/// Since this struct implements `Erased`, the type can be erased to make a dynamically typed
|
/// Since this struct implements `Erased`, the type can be erased to make a dynamically typed
|
||||||
/// option. The type can be checked dynamically using `Erased::tag_id` and since this is statically
|
/// option. The type can be checked dynamically using `Tagged::tag_id` and since this is statically
|
||||||
/// checked for the concrete type, there is some degree of type safety.
|
/// checked for the concrete type, there is some degree of type safety.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
pub(crate) struct TaggedOption<'a, I: tags::Type<'a>>(pub Option<I::Reified>);
|
pub(crate) struct TaggedOption<'a, I: tags::Type<'a>>(pub Option<I::Reified>);
|
||||||
|
|
|
@ -2784,8 +2784,10 @@ macro_rules! int_impl {
|
||||||
///
|
///
|
||||||
/// In other words, the result is `self / rhs` rounded to the integer `q`
|
/// In other words, the result is `self / rhs` rounded to the integer `q`
|
||||||
/// such that `self >= q * rhs`.
|
/// such that `self >= q * rhs`.
|
||||||
/// If `self > 0`, this is equal to round towards zero (the default in Rust);
|
/// If `self > 0`, this is equal to rounding towards zero (the default in Rust);
|
||||||
/// if `self < 0`, this is equal to round towards +/- infinity.
|
/// if `self < 0`, this is equal to rounding away from zero (towards +/- infinity).
|
||||||
|
/// If `rhs > 0`, this is equal to rounding towards -infinity;
|
||||||
|
/// if `rhs < 0`, this is equal to rounding towards +infinity.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
|
@ -2823,8 +2825,8 @@ macro_rules! int_impl {
|
||||||
/// Calculates the least nonnegative remainder of `self (mod rhs)`.
|
/// Calculates the least nonnegative remainder of `self (mod rhs)`.
|
||||||
///
|
///
|
||||||
/// This is done as if by the Euclidean division algorithm -- given
|
/// This is done as if by the Euclidean division algorithm -- given
|
||||||
/// `r = self.rem_euclid(rhs)`, `self = rhs * self.div_euclid(rhs) + r`, and
|
/// `r = self.rem_euclid(rhs)`, the result satisfies
|
||||||
/// `0 <= r < abs(rhs)`.
|
/// `self = rhs * self.div_euclid(rhs) + r` and `0 <= r < abs(rhs)`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
|
|
|
@ -237,7 +237,7 @@
|
||||||
//! pointer. For code which *does* cast a usize to a pointer, the scope of the change depends
|
//! pointer. For code which *does* cast a usize to a pointer, the scope of the change depends
|
||||||
//! on exactly what you're doing.
|
//! on exactly what you're doing.
|
||||||
//!
|
//!
|
||||||
//! In general you just need to make sure that if you want to convert a usize address to a
|
//! In general, you just need to make sure that if you want to convert a usize address to a
|
||||||
//! pointer and then use that pointer to read/write memory, you need to keep around a pointer
|
//! pointer and then use that pointer to read/write memory, you need to keep around a pointer
|
||||||
//! that has sufficient provenance to perform that read/write itself. In this way all of your
|
//! that has sufficient provenance to perform that read/write itself. In this way all of your
|
||||||
//! casts from an address to a pointer are essentially just applying offsets/indexing.
|
//! casts from an address to a pointer are essentially just applying offsets/indexing.
|
||||||
|
@ -309,7 +309,7 @@
|
||||||
//! i.e. the usual "ZSTs are fake, do what you want" rules apply *but* this only applies
|
//! i.e. the usual "ZSTs are fake, do what you want" rules apply *but* this only applies
|
||||||
//! for actual forgery (integers cast to pointers). If you borrow some struct's field
|
//! for actual forgery (integers cast to pointers). If you borrow some struct's field
|
||||||
//! that *happens* to be zero-sized, the resulting pointer will have provenance tied to
|
//! that *happens* to be zero-sized, the resulting pointer will have provenance tied to
|
||||||
//! that allocation and it will still get invalidated if the allocation gets deallocated.
|
//! that allocation, and it will still get invalidated if the allocation gets deallocated.
|
||||||
//! In the future we may introduce an API to make such a forged allocation explicit.
|
//! In the future we may introduce an API to make such a forged allocation explicit.
|
||||||
//!
|
//!
|
||||||
//! * [`wrapping_offset`][] a pointer outside its provenance. This includes pointers
|
//! * [`wrapping_offset`][] a pointer outside its provenance. This includes pointers
|
||||||
|
@ -698,7 +698,7 @@ pub const fn dangling_mut<T>() -> *mut T {
|
||||||
///
|
///
|
||||||
/// If there is no 'exposed' provenance that justifies the way this pointer will be used,
|
/// If there is no 'exposed' provenance that justifies the way this pointer will be used,
|
||||||
/// the program has undefined behavior. In particular, the aliasing rules still apply: pointers
|
/// the program has undefined behavior. In particular, the aliasing rules still apply: pointers
|
||||||
/// and references that have been invalidated due to aliasing accesses cannot be used any more,
|
/// and references that have been invalidated due to aliasing accesses cannot be used anymore,
|
||||||
/// even if they have been exposed!
|
/// even if they have been exposed!
|
||||||
///
|
///
|
||||||
/// Note that there is no algorithm that decides which provenance will be used. You can think of this
|
/// Note that there is no algorithm that decides which provenance will be used. You can think of this
|
||||||
|
@ -1097,7 +1097,7 @@ const unsafe fn swap_nonoverlapping_simple_untyped<T>(x: *mut T, y: *mut T, coun
|
||||||
// If we end up here, it's because we're using a simple type -- like
|
// If we end up here, it's because we're using a simple type -- like
|
||||||
// a small power-of-two-sized thing -- or a special type with particularly
|
// a small power-of-two-sized thing -- or a special type with particularly
|
||||||
// large alignment, particularly SIMD types.
|
// large alignment, particularly SIMD types.
|
||||||
// Thus we're fine just reading-and-writing it, as either it's small
|
// Thus, we're fine just reading-and-writing it, as either it's small
|
||||||
// and that works well anyway or it's special and the type's author
|
// and that works well anyway or it's special and the type's author
|
||||||
// presumably wanted things to be done in the larger chunk.
|
// presumably wanted things to be done in the larger chunk.
|
||||||
|
|
||||||
|
@ -1290,7 +1290,7 @@ pub const unsafe fn read<T>(src: *const T) -> T {
|
||||||
// provides enough information to know that this is a typed operation.
|
// provides enough information to know that this is a typed operation.
|
||||||
|
|
||||||
// However, as of March 2023 the compiler was not capable of taking advantage
|
// However, as of March 2023 the compiler was not capable of taking advantage
|
||||||
// of that information. Thus the implementation here switched to an intrinsic,
|
// of that information. Thus, the implementation here switched to an intrinsic,
|
||||||
// which lowers to `_0 = *src` in MIR, to address a few issues:
|
// which lowers to `_0 = *src` in MIR, to address a few issues:
|
||||||
//
|
//
|
||||||
// - Using `MaybeUninit::assume_init` after a `copy_nonoverlapping` was not
|
// - Using `MaybeUninit::assume_init` after a `copy_nonoverlapping` was not
|
||||||
|
@ -1570,7 +1570,7 @@ pub const unsafe fn write<T>(dst: *mut T, src: T) {
|
||||||
/// As a result, using `&packed.unaligned as *const FieldType` causes immediate
|
/// As a result, using `&packed.unaligned as *const FieldType` causes immediate
|
||||||
/// *undefined behavior* in your program.
|
/// *undefined behavior* in your program.
|
||||||
///
|
///
|
||||||
/// Instead you must use the [`ptr::addr_of_mut!`](addr_of_mut)
|
/// Instead, you must use the [`ptr::addr_of_mut!`](addr_of_mut)
|
||||||
/// macro to create the pointer. You may use that returned pointer together with
|
/// macro to create the pointer. You may use that returned pointer together with
|
||||||
/// this function.
|
/// this function.
|
||||||
///
|
///
|
||||||
|
|
|
@ -1,13 +1,4 @@
|
||||||
#![stable(feature = "os_fd", since = "1.66.0")]
|
#![stable(feature = "rust1", since = "1.0.0")]
|
||||||
|
|
||||||
mod net;
|
#[stable(feature = "rust1", since = "1.0.0")]
|
||||||
#[path = "../../fd/owned.rs"]
|
pub use crate::os::fd::*;
|
||||||
mod owned;
|
|
||||||
#[path = "../../fd/raw.rs"]
|
|
||||||
mod raw;
|
|
||||||
|
|
||||||
// Export the types and traits for the public API.
|
|
||||||
#[stable(feature = "os_fd", since = "1.66.0")]
|
|
||||||
pub use owned::*;
|
|
||||||
#[stable(feature = "os_fd", since = "1.66.0")]
|
|
||||||
pub use raw::*;
|
|
||||||
|
|
|
@ -160,7 +160,7 @@ pub(crate) mod watchos;
|
||||||
#[cfg(target_os = "xous")]
|
#[cfg(target_os = "xous")]
|
||||||
pub mod xous;
|
pub mod xous;
|
||||||
|
|
||||||
#[cfg(any(unix, target_os = "wasi", doc))]
|
#[cfg(any(unix, target_os = "hermit", target_os = "wasi", doc))]
|
||||||
pub mod fd;
|
pub mod fd;
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "android", doc))]
|
#[cfg(any(target_os = "linux", target_os = "android", doc))]
|
||||||
|
|
50
tests/codegen/error-provide.rs
Normal file
50
tests/codegen/error-provide.rs
Normal file
|
@ -0,0 +1,50 @@
|
||||||
|
// Codegen test for #126242
|
||||||
|
|
||||||
|
//@ compile-flags: -O
|
||||||
|
#![crate_type = "lib"]
|
||||||
|
#![feature(error_generic_member_access)]
|
||||||
|
use std::error::Request;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct MyBacktrace1 {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct MyBacktrace2 {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct MyBacktrace3 {}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct MyError {
|
||||||
|
backtrace1: MyBacktrace1,
|
||||||
|
backtrace2: MyBacktrace2,
|
||||||
|
backtrace3: MyBacktrace3,
|
||||||
|
other: MyBacktrace3,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for MyError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Example Error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for MyError {
|
||||||
|
// CHECK-LABEL: @provide
|
||||||
|
#[no_mangle]
|
||||||
|
fn provide<'a>(&'a self, request: &mut Request<'a>) {
|
||||||
|
// LLVM should be able to optimize multiple .provide_* calls into a switch table
|
||||||
|
// and eliminate redundant ones, rather than compare one-by-one.
|
||||||
|
|
||||||
|
// CHECK-NEXT: start:
|
||||||
|
// CHECK-NEXT: %[[SCRUTINEE:[^ ]+]] = load i64, ptr
|
||||||
|
// CHECK-NEXT: switch i64 %[[SCRUTINEE]], label %{{.*}} [
|
||||||
|
// CHECK-COUNT-3: i64 {{.*}}, label %{{.*}}
|
||||||
|
// CHECK-NEXT: ]
|
||||||
|
request
|
||||||
|
.provide_ref::<MyBacktrace1>(&self.backtrace1)
|
||||||
|
.provide_ref::<MyBacktrace3>(&self.other)
|
||||||
|
.provide_ref::<MyBacktrace2>(&self.backtrace2)
|
||||||
|
.provide_ref::<MyBacktrace3>(&self.backtrace3);
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,10 @@
|
||||||
|
//@ check-pass
|
||||||
|
|
||||||
struct S;
|
struct S;
|
||||||
|
|
||||||
impl S {
|
impl S {
|
||||||
const C: &&str = &"";
|
const C: &&str = &"";
|
||||||
//~^ WARN `&` without an explicit lifetime name cannot be used here
|
// Now resolves to `&'static &'static str`.
|
||||||
//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
|
||||||
//~| WARN `&` without an explicit lifetime name cannot be used here
|
|
||||||
//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
|
||||||
//~| ERROR in type `&&str`, reference has a longer lifetime than the data it references
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
warning: `&` without an explicit lifetime name cannot be used here
|
|
||||||
--> $DIR/double-elided.rs:4:14
|
|
||||||
|
|
|
||||||
LL | const C: &&str = &"";
|
|
||||||
| ^
|
|
||||||
|
|
|
||||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
|
||||||
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
|
|
||||||
= note: `#[warn(elided_lifetimes_in_associated_constant)]` on by default
|
|
||||||
help: use the `'static` lifetime
|
|
||||||
|
|
|
||||||
LL | const C: &'static &str = &"";
|
|
||||||
| +++++++
|
|
||||||
|
|
||||||
warning: `&` without an explicit lifetime name cannot be used here
|
|
||||||
--> $DIR/double-elided.rs:4:15
|
|
||||||
|
|
|
||||||
LL | const C: &&str = &"";
|
|
||||||
| ^
|
|
||||||
|
|
|
||||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
|
||||||
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
|
|
||||||
help: use the `'static` lifetime
|
|
||||||
|
|
|
||||||
LL | const C: &&'static str = &"";
|
|
||||||
| +++++++
|
|
||||||
|
|
||||||
error[E0491]: in type `&&str`, reference has a longer lifetime than the data it references
|
|
||||||
--> $DIR/double-elided.rs:4:5
|
|
||||||
|
|
|
||||||
LL | const C: &&str = &"";
|
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^
|
|
||||||
|
|
|
||||||
note: the pointer is valid for the anonymous lifetime as defined here
|
|
||||||
--> $DIR/double-elided.rs:4:14
|
|
||||||
|
|
|
||||||
LL | const C: &&str = &"";
|
|
||||||
| ^
|
|
||||||
note: but the referenced data is only valid for the anonymous lifetime as defined here
|
|
||||||
--> $DIR/double-elided.rs:4:14
|
|
||||||
|
|
|
||||||
LL | const C: &&str = &"";
|
|
||||||
| ^
|
|
||||||
|
|
||||||
error: aborting due to 1 previous error; 2 warnings emitted
|
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0491`.
|
|
|
@ -5,8 +5,6 @@ trait Trait {
|
||||||
impl Trait for () {
|
impl Trait for () {
|
||||||
const ASSOC: &dyn Fn(_) = 1i32;
|
const ASSOC: &dyn Fn(_) = 1i32;
|
||||||
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated constants
|
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for associated constants
|
||||||
//~| WARN `&` without an explicit lifetime name cannot be used here
|
|
||||||
//~| WARN this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -1,23 +1,9 @@
|
||||||
warning: `&` without an explicit lifetime name cannot be used here
|
|
||||||
--> $DIR/infer-placeholder-in-non-suggestable-pos.rs:6:18
|
|
||||||
|
|
|
||||||
LL | const ASSOC: &dyn Fn(_) = 1i32;
|
|
||||||
| ^
|
|
||||||
|
|
|
||||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
|
||||||
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
|
|
||||||
= note: `#[warn(elided_lifetimes_in_associated_constant)]` on by default
|
|
||||||
help: use the `'static` lifetime
|
|
||||||
|
|
|
||||||
LL | const ASSOC: &'static dyn Fn(_) = 1i32;
|
|
||||||
| +++++++
|
|
||||||
|
|
||||||
error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants
|
error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated constants
|
||||||
--> $DIR/infer-placeholder-in-non-suggestable-pos.rs:6:26
|
--> $DIR/infer-placeholder-in-non-suggestable-pos.rs:6:26
|
||||||
|
|
|
|
||||||
LL | const ASSOC: &dyn Fn(_) = 1i32;
|
LL | const ASSOC: &dyn Fn(_) = 1i32;
|
||||||
| ^ not allowed in type signatures
|
| ^ not allowed in type signatures
|
||||||
|
|
||||||
error: aborting due to 1 previous error; 1 warning emitted
|
error: aborting due to 1 previous error
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0121`.
|
For more information about this error, try `rustc --explain E0121`.
|
||||||
|
|
|
@ -6,6 +6,11 @@ LL | const FOO: Foo<'_> = Foo { x: PhantomData::<&()> };
|
||||||
|
|
|
|
||||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
|
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
|
||||||
|
note: cannot automatically infer `'static` because of other lifetimes in scope
|
||||||
|
--> $DIR/assoc-const-elided-lifetime.rs:9:6
|
||||||
|
|
|
||||||
|
LL | impl<'a> Foo<'a> {
|
||||||
|
| ^^
|
||||||
note: the lint level is defined here
|
note: the lint level is defined here
|
||||||
--> $DIR/assoc-const-elided-lifetime.rs:1:9
|
--> $DIR/assoc-const-elided-lifetime.rs:1:9
|
||||||
|
|
|
|
||||||
|
@ -24,6 +29,13 @@ LL | const BAR: &() = &();
|
||||||
|
|
|
|
||||||
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
|
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
|
||||||
|
note: cannot automatically infer `'static` because of other lifetimes in scope
|
||||||
|
--> $DIR/assoc-const-elided-lifetime.rs:9:6
|
||||||
|
|
|
||||||
|
LL | impl<'a> Foo<'a> {
|
||||||
|
| ^^
|
||||||
|
LL | const FOO: Foo<'_> = Foo { x: PhantomData::<&()> };
|
||||||
|
| ^^
|
||||||
help: use the `'static` lifetime
|
help: use the `'static` lifetime
|
||||||
|
|
|
|
||||||
LL | const BAR: &'static () = &();
|
LL | const BAR: &'static () = &();
|
||||||
|
|
22
tests/ui/consts/static-default-lifetime/elided-lifetime.rs
Normal file
22
tests/ui/consts/static-default-lifetime/elided-lifetime.rs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
#![deny(elided_lifetimes_in_associated_constant)]
|
||||||
|
|
||||||
|
struct Foo<'a>(&'a ());
|
||||||
|
|
||||||
|
impl Foo<'_> {
|
||||||
|
const STATIC: &str = "";
|
||||||
|
//~^ ERROR `&` without an explicit lifetime name cannot be used here
|
||||||
|
//~| WARN this was previously accepted by the compiler but is being phased out
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Bar {
|
||||||
|
const STATIC: &str;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Bar for Foo<'_> {
|
||||||
|
const STATIC: &str = "";
|
||||||
|
//~^ ERROR `&` without an explicit lifetime name cannot be used here
|
||||||
|
//~| WARN this was previously accepted by the compiler but is being phased out
|
||||||
|
//~| ERROR const not compatible with trait
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,59 @@
|
||||||
|
error: `&` without an explicit lifetime name cannot be used here
|
||||||
|
--> $DIR/elided-lifetime.rs:6:19
|
||||||
|
|
|
||||||
|
LL | const STATIC: &str = "";
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
|
||||||
|
note: cannot automatically infer `'static` because of other lifetimes in scope
|
||||||
|
--> $DIR/elided-lifetime.rs:5:10
|
||||||
|
|
|
||||||
|
LL | impl Foo<'_> {
|
||||||
|
| ^^
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/elided-lifetime.rs:1:9
|
||||||
|
|
|
||||||
|
LL | #![deny(elided_lifetimes_in_associated_constant)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
help: use the `'static` lifetime
|
||||||
|
|
|
||||||
|
LL | const STATIC: &'static str = "";
|
||||||
|
| +++++++
|
||||||
|
|
||||||
|
error: `&` without an explicit lifetime name cannot be used here
|
||||||
|
--> $DIR/elided-lifetime.rs:16:19
|
||||||
|
|
|
||||||
|
LL | const STATIC: &str = "";
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
|
||||||
|
note: cannot automatically infer `'static` because of other lifetimes in scope
|
||||||
|
--> $DIR/elided-lifetime.rs:15:18
|
||||||
|
|
|
||||||
|
LL | impl Bar for Foo<'_> {
|
||||||
|
| ^^
|
||||||
|
help: use the `'static` lifetime
|
||||||
|
|
|
||||||
|
LL | const STATIC: &'static str = "";
|
||||||
|
| +++++++
|
||||||
|
|
||||||
|
error[E0308]: const not compatible with trait
|
||||||
|
--> $DIR/elided-lifetime.rs:16:5
|
||||||
|
|
|
||||||
|
LL | const STATIC: &str = "";
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ lifetime mismatch
|
||||||
|
|
|
||||||
|
= note: expected reference `&'static _`
|
||||||
|
found reference `&_`
|
||||||
|
note: the anonymous lifetime as defined here...
|
||||||
|
--> $DIR/elided-lifetime.rs:15:18
|
||||||
|
|
|
||||||
|
LL | impl Bar for Foo<'_> {
|
||||||
|
| ^^
|
||||||
|
= note: ...does not necessarily outlive the static lifetime
|
||||||
|
|
||||||
|
error: aborting due to 3 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
|
@ -0,0 +1,19 @@
|
||||||
|
#![deny(elided_lifetimes_in_associated_constant)]
|
||||||
|
#![feature(generic_const_items)]
|
||||||
|
//~^ WARN the feature `generic_const_items` is incomplete
|
||||||
|
|
||||||
|
struct A;
|
||||||
|
impl A {
|
||||||
|
const GAC_TYPE<T>: &str = "";
|
||||||
|
const GAC_LIFETIME<'a>: &str = "";
|
||||||
|
//~^ ERROR `&` without an explicit lifetime name cannot be used here
|
||||||
|
//~| WARN this was previously accepted by the compiler but is being phased out
|
||||||
|
}
|
||||||
|
|
||||||
|
trait Trait {
|
||||||
|
const GAC_TYPE<T>: &str = "";
|
||||||
|
const GAC_LIFETIME<'a>: &str = "";
|
||||||
|
//~^ ERROR missing lifetime specifier
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,46 @@
|
||||||
|
error[E0106]: missing lifetime specifier
|
||||||
|
--> $DIR/generic-associated-const.rs:15:29
|
||||||
|
|
|
||||||
|
LL | const GAC_LIFETIME<'a>: &str = "";
|
||||||
|
| ^ expected named lifetime parameter
|
||||||
|
|
|
||||||
|
help: consider using the `'a` lifetime
|
||||||
|
|
|
||||||
|
LL | const GAC_LIFETIME<'a>: &'a str = "";
|
||||||
|
| ++
|
||||||
|
|
||||||
|
warning: the feature `generic_const_items` is incomplete and may not be safe to use and/or cause compiler crashes
|
||||||
|
--> $DIR/generic-associated-const.rs:2:12
|
||||||
|
|
|
||||||
|
LL | #![feature(generic_const_items)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #113521 <https://github.com/rust-lang/rust/issues/113521> for more information
|
||||||
|
= note: `#[warn(incomplete_features)]` on by default
|
||||||
|
|
||||||
|
error: `&` without an explicit lifetime name cannot be used here
|
||||||
|
--> $DIR/generic-associated-const.rs:8:29
|
||||||
|
|
|
||||||
|
LL | const GAC_LIFETIME<'a>: &str = "";
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
|
||||||
|
note: cannot automatically infer `'static` because of other lifetimes in scope
|
||||||
|
--> $DIR/generic-associated-const.rs:8:24
|
||||||
|
|
|
||||||
|
LL | const GAC_LIFETIME<'a>: &str = "";
|
||||||
|
| ^^
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/generic-associated-const.rs:1:9
|
||||||
|
|
|
||||||
|
LL | #![deny(elided_lifetimes_in_associated_constant)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
help: use the `'static` lifetime
|
||||||
|
|
|
||||||
|
LL | const GAC_LIFETIME<'a>: &'static str = "";
|
||||||
|
| +++++++
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors; 1 warning emitted
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0106`.
|
21
tests/ui/consts/static-default-lifetime/inner-item.rs
Normal file
21
tests/ui/consts/static-default-lifetime/inner-item.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
//@ check-pass
|
||||||
|
|
||||||
|
struct Foo<'a>(&'a ());
|
||||||
|
|
||||||
|
impl<'a> Foo<'a> {
|
||||||
|
fn hello(self) {
|
||||||
|
const INNER: &str = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Foo<'_> {
|
||||||
|
fn implicit(self) {
|
||||||
|
const INNER: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fn_lifetime(&self) {
|
||||||
|
const INNER: &str = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
20
tests/ui/consts/static-default-lifetime/static-trait-impl.rs
Normal file
20
tests/ui/consts/static-default-lifetime/static-trait-impl.rs
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#![deny(elided_lifetimes_in_associated_constant)]
|
||||||
|
|
||||||
|
trait Bar<'a> {
|
||||||
|
const STATIC: &'a str;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct A;
|
||||||
|
impl Bar<'_> for A {
|
||||||
|
const STATIC: &str = "";
|
||||||
|
//~^ ERROR `&` without an explicit lifetime name cannot be used here
|
||||||
|
//~| WARN this was previously accepted by the compiler but is being phased out
|
||||||
|
//~| ERROR const not compatible with trait
|
||||||
|
}
|
||||||
|
|
||||||
|
struct B;
|
||||||
|
impl Bar<'static> for B {
|
||||||
|
const STATIC: &str = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
|
@ -0,0 +1,45 @@
|
||||||
|
error: `&` without an explicit lifetime name cannot be used here
|
||||||
|
--> $DIR/static-trait-impl.rs:9:19
|
||||||
|
|
|
||||||
|
LL | const STATIC: &str = "";
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
|
||||||
|
= note: for more information, see issue #115010 <https://github.com/rust-lang/rust/issues/115010>
|
||||||
|
note: cannot automatically infer `'static` because of other lifetimes in scope
|
||||||
|
--> $DIR/static-trait-impl.rs:8:10
|
||||||
|
|
|
||||||
|
LL | impl Bar<'_> for A {
|
||||||
|
| ^^
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/static-trait-impl.rs:1:9
|
||||||
|
|
|
||||||
|
LL | #![deny(elided_lifetimes_in_associated_constant)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
help: use the `'static` lifetime
|
||||||
|
|
|
||||||
|
LL | const STATIC: &'static str = "";
|
||||||
|
| +++++++
|
||||||
|
|
||||||
|
error[E0308]: const not compatible with trait
|
||||||
|
--> $DIR/static-trait-impl.rs:9:5
|
||||||
|
|
|
||||||
|
LL | const STATIC: &str = "";
|
||||||
|
| ^^^^^^^^^^^^^^^^^^ lifetime mismatch
|
||||||
|
|
|
||||||
|
= note: expected reference `&_`
|
||||||
|
found reference `&_`
|
||||||
|
note: the anonymous lifetime as defined here...
|
||||||
|
--> $DIR/static-trait-impl.rs:8:10
|
||||||
|
|
|
||||||
|
LL | impl Bar<'_> for A {
|
||||||
|
| ^^
|
||||||
|
note: ...does not necessarily outlive the anonymous lifetime as defined here
|
||||||
|
--> $DIR/static-trait-impl.rs:8:10
|
||||||
|
|
|
||||||
|
LL | impl Bar<'_> for A {
|
||||||
|
| ^^
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0308`.
|
|
@ -1,57 +1,25 @@
|
||||||
error[E0106]: missing lifetime specifier
|
error[E0726]: implicit elided lifetime not allowed here
|
||||||
--> $DIR/missing-lifetime-in-assoc-const-type.rs:6:14
|
|
||||||
|
|
|
||||||
LL | const A: &str = "";
|
|
||||||
| ^ expected named lifetime parameter
|
|
||||||
|
|
|
||||||
help: consider introducing a named lifetime parameter
|
|
||||||
|
|
|
||||||
LL ~ trait ZstAssert<'a>: Sized {
|
|
||||||
LL ~ const A: &'a str = "";
|
|
||||||
|
|
|
||||||
|
|
||||||
error[E0106]: missing lifetime specifier
|
|
||||||
--> $DIR/missing-lifetime-in-assoc-const-type.rs:7:14
|
--> $DIR/missing-lifetime-in-assoc-const-type.rs:7:14
|
||||||
|
|
|
|
||||||
LL | const B: S = S { s: &() };
|
LL | const B: S = S { s: &() };
|
||||||
| ^ expected named lifetime parameter
|
| ^ expected lifetime parameter
|
||||||
|
|
|
|
||||||
help: consider introducing a named lifetime parameter
|
help: indicate the anonymous lifetime
|
||||||
|
|
|
||||||
LL ~ trait ZstAssert<'a>: Sized {
|
|
||||||
LL | const A: &str = "";
|
|
||||||
LL ~ const B: S<'a> = S { s: &() };
|
|
||||||
|
|
|
|
||||||
|
LL | const B: S<'_> = S { s: &() };
|
||||||
|
| ++++
|
||||||
|
|
||||||
error[E0106]: missing lifetime specifier
|
error[E0726]: implicit elided lifetime not allowed here
|
||||||
--> $DIR/missing-lifetime-in-assoc-const-type.rs:8:15
|
|
||||||
|
|
|
||||||
LL | const C: &'_ str = "";
|
|
||||||
| ^^ expected named lifetime parameter
|
|
||||||
|
|
|
||||||
help: consider introducing a named lifetime parameter
|
|
||||||
|
|
|
||||||
LL ~ trait ZstAssert<'a>: Sized {
|
|
||||||
LL | const A: &str = "";
|
|
||||||
LL | const B: S = S { s: &() };
|
|
||||||
LL ~ const C: &'a str = "";
|
|
||||||
|
|
|
||||||
|
|
||||||
error[E0106]: missing lifetime specifiers
|
|
||||||
--> $DIR/missing-lifetime-in-assoc-const-type.rs:9:14
|
--> $DIR/missing-lifetime-in-assoc-const-type.rs:9:14
|
||||||
|
|
|
|
||||||
LL | const D: T = T { a: &(), b: &() };
|
LL | const D: T = T { a: &(), b: &() };
|
||||||
| ^ expected 2 lifetime parameters
|
| ^ expected lifetime parameters
|
||||||
|
|
|
|
||||||
help: consider introducing a named lifetime parameter
|
help: indicate the anonymous lifetimes
|
||||||
|
|
|
||||||
LL ~ trait ZstAssert<'a>: Sized {
|
|
||||||
LL | const A: &str = "";
|
|
||||||
LL | const B: S = S { s: &() };
|
|
||||||
LL | const C: &'_ str = "";
|
|
||||||
LL ~ const D: T<'a, 'a> = T { a: &(), b: &() };
|
|
||||||
|
|
|
|
||||||
|
LL | const D: T<'_, '_> = T { a: &(), b: &() };
|
||||||
|
| ++++++++
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0106`.
|
For more information about this error, try `rustc --explain E0726`.
|
||||||
|
|
|
@ -1,47 +1,25 @@
|
||||||
error[E0106]: missing lifetime specifier
|
error[E0726]: implicit elided lifetime not allowed here
|
||||||
--> $DIR/missing-lifetime-in-assoc-const-type.rs:6:14
|
|
||||||
|
|
|
||||||
LL | const A: &str = "";
|
|
||||||
| ^ expected named lifetime parameter
|
|
||||||
|
|
|
||||||
help: consider introducing a named lifetime parameter
|
|
||||||
|
|
|
||||||
LL | const A<'a>: &'a str = "";
|
|
||||||
| ++++ ++
|
|
||||||
|
|
||||||
error[E0106]: missing lifetime specifier
|
|
||||||
--> $DIR/missing-lifetime-in-assoc-const-type.rs:7:14
|
--> $DIR/missing-lifetime-in-assoc-const-type.rs:7:14
|
||||||
|
|
|
|
||||||
LL | const B: S = S { s: &() };
|
LL | const B: S = S { s: &() };
|
||||||
| ^ expected named lifetime parameter
|
| ^ expected lifetime parameter
|
||||||
|
|
|
|
||||||
help: consider introducing a named lifetime parameter
|
help: indicate the anonymous lifetime
|
||||||
|
|
|
|
||||||
LL | const B<'a>: S<'a> = S { s: &() };
|
LL | const B: S<'_> = S { s: &() };
|
||||||
| ++++ ++++
|
| ++++
|
||||||
|
|
||||||
error[E0106]: missing lifetime specifier
|
error[E0726]: implicit elided lifetime not allowed here
|
||||||
--> $DIR/missing-lifetime-in-assoc-const-type.rs:8:15
|
|
||||||
|
|
|
||||||
LL | const C: &'_ str = "";
|
|
||||||
| ^^ expected named lifetime parameter
|
|
||||||
|
|
|
||||||
help: consider introducing a named lifetime parameter
|
|
||||||
|
|
|
||||||
LL | const C<'a>: &'a str = "";
|
|
||||||
| ++++ ~~
|
|
||||||
|
|
||||||
error[E0106]: missing lifetime specifiers
|
|
||||||
--> $DIR/missing-lifetime-in-assoc-const-type.rs:9:14
|
--> $DIR/missing-lifetime-in-assoc-const-type.rs:9:14
|
||||||
|
|
|
|
||||||
LL | const D: T = T { a: &(), b: &() };
|
LL | const D: T = T { a: &(), b: &() };
|
||||||
| ^ expected 2 lifetime parameters
|
| ^ expected lifetime parameters
|
||||||
|
|
|
|
||||||
help: consider introducing a named lifetime parameter
|
help: indicate the anonymous lifetimes
|
||||||
|
|
|
|
||||||
LL | const D<'a>: T<'a, 'a> = T { a: &(), b: &() };
|
LL | const D: T<'_, '_> = T { a: &(), b: &() };
|
||||||
| ++++ ++++++++
|
| ++++++++
|
||||||
|
|
||||||
error: aborting due to 4 previous errors
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0106`.
|
For more information about this error, try `rustc --explain E0726`.
|
||||||
|
|
|
@ -3,10 +3,10 @@
|
||||||
#![cfg_attr(generic_const_items, feature(generic_const_items), allow(incomplete_features))]
|
#![cfg_attr(generic_const_items, feature(generic_const_items), allow(incomplete_features))]
|
||||||
|
|
||||||
trait ZstAssert: Sized {
|
trait ZstAssert: Sized {
|
||||||
const A: &str = ""; //~ ERROR missing lifetime specifier
|
const A: &str = "";
|
||||||
const B: S = S { s: &() }; //~ ERROR missing lifetime specifier
|
const B: S = S { s: &() }; //~ ERROR implicit elided lifetime not allowed here
|
||||||
const C: &'_ str = ""; //~ ERROR missing lifetime specifier
|
const C: &'_ str = "";
|
||||||
const D: T = T { a: &(), b: &() }; //~ ERROR missing lifetime specifier
|
const D: T = T { a: &(), b: &() }; //~ ERROR implicit elided lifetime not allowed here
|
||||||
}
|
}
|
||||||
|
|
||||||
struct S<'a> {
|
struct S<'a> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue