mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-02 07:22:42 +00:00
Auto merge of #114331 - matthiaskrgr:rollup-rnrmwcx, r=matthiaskrgr
Rollup of 7 pull requests Successful merges: - #100455 (Implement RefUnwindSafe for Backtrace) - #113428 (coverage: Replace `ExpressionOperandId` with enum `Operand`) - #114283 (Use parking lot's rwlock even without parallel-rustc) - #114288 (Improve diagnostic for wrong borrow on binary operations) - #114296 (interpret: fix alignment handling for Repeat expressions) - #114306 ([rustc_data_structures][perf] Simplify base_n::push_str.) - #114320 (Cover statements for stable_mir) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
4896daa398
@ -1,4 +1,4 @@
|
||||
use rustc_middle::mir::coverage::{CounterValueReference, MappedExpressionIndex};
|
||||
use rustc_middle::mir::coverage::{CounterId, MappedExpressionIndex};
|
||||
|
||||
/// Must match the layout of `LLVMRustCounterKind`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
@ -36,11 +36,9 @@ impl Counter {
|
||||
Self { kind: CounterKind::Zero, id: 0 }
|
||||
}
|
||||
|
||||
/// Constructs a new `Counter` of kind `CounterValueReference`, and converts
|
||||
/// the given 1-based counter_id to the required 0-based equivalent for
|
||||
/// the `Counter` encoding.
|
||||
pub fn counter_value_reference(counter_id: CounterValueReference) -> Self {
|
||||
Self { kind: CounterKind::CounterValueReference, id: counter_id.zero_based_index() }
|
||||
/// Constructs a new `Counter` of kind `CounterValueReference`.
|
||||
pub fn counter_value_reference(counter_id: CounterId) -> Self {
|
||||
Self { kind: CounterKind::CounterValueReference, id: counter_id.as_u32() }
|
||||
}
|
||||
|
||||
/// Constructs a new `Counter` of kind `Expression`.
|
||||
|
@ -3,24 +3,22 @@ pub use super::ffi::*;
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::coverage::{
|
||||
CodeRegion, CounterValueReference, ExpressionOperandId, InjectedExpressionId,
|
||||
InjectedExpressionIndex, MappedExpressionIndex, Op,
|
||||
CodeRegion, CounterId, ExpressionId, MappedExpressionIndex, Op, Operand,
|
||||
};
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Expression {
|
||||
lhs: ExpressionOperandId,
|
||||
lhs: Operand,
|
||||
op: Op,
|
||||
rhs: ExpressionOperandId,
|
||||
rhs: Operand,
|
||||
region: Option<CodeRegion>,
|
||||
}
|
||||
|
||||
/// Collects all of the coverage regions associated with (a) injected counters, (b) counter
|
||||
/// expressions (additions or subtraction), and (c) unreachable regions (always counted as zero),
|
||||
/// for a given Function. Counters and counter expressions have non-overlapping `id`s because they
|
||||
/// can both be operands in an expression. This struct also stores the `function_source_hash`,
|
||||
/// for a given Function. This struct also stores the `function_source_hash`,
|
||||
/// computed during instrumentation, and forwarded with counters.
|
||||
///
|
||||
/// Note, it may be important to understand LLVM's definitions of `unreachable` regions versus "gap
|
||||
@ -34,8 +32,8 @@ pub struct FunctionCoverage<'tcx> {
|
||||
instance: Instance<'tcx>,
|
||||
source_hash: u64,
|
||||
is_used: bool,
|
||||
counters: IndexVec<CounterValueReference, Option<CodeRegion>>,
|
||||
expressions: IndexVec<InjectedExpressionIndex, Option<Expression>>,
|
||||
counters: IndexVec<CounterId, Option<CodeRegion>>,
|
||||
expressions: IndexVec<ExpressionId, Option<Expression>>,
|
||||
unreachable_regions: Vec<CodeRegion>,
|
||||
}
|
||||
|
||||
@ -82,48 +80,36 @@ impl<'tcx> FunctionCoverage<'tcx> {
|
||||
}
|
||||
|
||||
/// Adds a code region to be counted by an injected counter intrinsic.
|
||||
pub fn add_counter(&mut self, id: CounterValueReference, region: CodeRegion) {
|
||||
pub fn add_counter(&mut self, id: CounterId, region: CodeRegion) {
|
||||
if let Some(previous_region) = self.counters[id].replace(region.clone()) {
|
||||
assert_eq!(previous_region, region, "add_counter: code region for id changed");
|
||||
}
|
||||
}
|
||||
|
||||
/// Both counters and "counter expressions" (or simply, "expressions") can be operands in other
|
||||
/// expressions. Expression IDs start from `u32::MAX` and go down, so the range of expression
|
||||
/// IDs will not overlap with the range of counter IDs. Counters and expressions can be added in
|
||||
/// any order, and expressions can still be assigned contiguous (though descending) IDs, without
|
||||
/// knowing what the last counter ID will be.
|
||||
///
|
||||
/// When storing the expression data in the `expressions` vector in the `FunctionCoverage`
|
||||
/// struct, its vector index is computed, from the given expression ID, by subtracting from
|
||||
/// `u32::MAX`.
|
||||
///
|
||||
/// Since the expression operands (`lhs` and `rhs`) can reference either counters or
|
||||
/// expressions, an operand that references an expression also uses its original ID, descending
|
||||
/// from `u32::MAX`. Theses operands are translated only during code generation, after all
|
||||
/// counters and expressions have been added.
|
||||
/// expressions. These are tracked as separate variants of `Operand`, so there is no ambiguity
|
||||
/// between operands that are counter IDs and operands that are expression IDs.
|
||||
pub fn add_counter_expression(
|
||||
&mut self,
|
||||
expression_id: InjectedExpressionId,
|
||||
lhs: ExpressionOperandId,
|
||||
expression_id: ExpressionId,
|
||||
lhs: Operand,
|
||||
op: Op,
|
||||
rhs: ExpressionOperandId,
|
||||
rhs: Operand,
|
||||
region: Option<CodeRegion>,
|
||||
) {
|
||||
debug!(
|
||||
"add_counter_expression({:?}, lhs={:?}, op={:?}, rhs={:?} at {:?}",
|
||||
expression_id, lhs, op, rhs, region
|
||||
);
|
||||
let expression_index = self.expression_index(u32::from(expression_id));
|
||||
debug_assert!(
|
||||
expression_index.as_usize() < self.expressions.len(),
|
||||
"expression_index {} is out of range for expressions.len() = {}
|
||||
expression_id.as_usize() < self.expressions.len(),
|
||||
"expression_id {} is out of range for expressions.len() = {}
|
||||
for {:?}",
|
||||
expression_index.as_usize(),
|
||||
expression_id.as_usize(),
|
||||
self.expressions.len(),
|
||||
self,
|
||||
);
|
||||
if let Some(previous_expression) = self.expressions[expression_index].replace(Expression {
|
||||
if let Some(previous_expression) = self.expressions[expression_id].replace(Expression {
|
||||
lhs,
|
||||
op,
|
||||
rhs,
|
||||
@ -186,14 +172,11 @@ impl<'tcx> FunctionCoverage<'tcx> {
|
||||
|
||||
// This closure converts any `Expression` operand (`lhs` or `rhs` of the `Op::Add` or
|
||||
// `Op::Subtract` operation) into its native `llvm::coverage::Counter::CounterKind` type
|
||||
// and value. Operand ID value `0` maps to `CounterKind::Zero`; values in the known range
|
||||
// of injected LLVM counters map to `CounterKind::CounterValueReference` (and the value
|
||||
// matches the injected counter index); and any other value is converted into a
|
||||
// `CounterKind::Expression` with the expression's `new_index`.
|
||||
// and value.
|
||||
//
|
||||
// Expressions will be returned from this function in a sequential vector (array) of
|
||||
// `CounterExpression`, so the expression IDs must be mapped from their original,
|
||||
// potentially sparse set of indexes, originally in reverse order from `u32::MAX`.
|
||||
// potentially sparse set of indexes.
|
||||
//
|
||||
// An `Expression` as an operand will have already been encountered as an `Expression` with
|
||||
// operands, so its new_index will already have been generated (as a 1-up index value).
|
||||
@ -206,34 +189,19 @@ impl<'tcx> FunctionCoverage<'tcx> {
|
||||
// `expression_index`s lower than the referencing `Expression`. Therefore, it is
|
||||
// reasonable to look up the new index of an expression operand while the `new_indexes`
|
||||
// vector is only complete up to the current `ExpressionIndex`.
|
||||
let id_to_counter = |new_indexes: &IndexSlice<
|
||||
InjectedExpressionIndex,
|
||||
Option<MappedExpressionIndex>,
|
||||
>,
|
||||
id: ExpressionOperandId| {
|
||||
if id == ExpressionOperandId::ZERO {
|
||||
Some(Counter::zero())
|
||||
} else if id.index() < self.counters.len() {
|
||||
debug_assert!(
|
||||
id.index() > 0,
|
||||
"ExpressionOperandId indexes for counters are 1-based, but this id={}",
|
||||
id.index()
|
||||
);
|
||||
// Note: Some codegen-injected Counters may be only referenced by `Expression`s,
|
||||
// and may not have their own `CodeRegion`s,
|
||||
let index = CounterValueReference::from(id.index());
|
||||
// Note, the conversion to LLVM `Counter` adjusts the index to be zero-based.
|
||||
Some(Counter::counter_value_reference(index))
|
||||
} else {
|
||||
let index = self.expression_index(u32::from(id));
|
||||
type NewIndexes = IndexSlice<ExpressionId, Option<MappedExpressionIndex>>;
|
||||
let id_to_counter = |new_indexes: &NewIndexes, operand: Operand| match operand {
|
||||
Operand::Zero => Some(Counter::zero()),
|
||||
Operand::Counter(id) => Some(Counter::counter_value_reference(id)),
|
||||
Operand::Expression(id) => {
|
||||
self.expressions
|
||||
.get(index)
|
||||
.get(id)
|
||||
.expect("expression id is out of range")
|
||||
.as_ref()
|
||||
// If an expression was optimized out, assume it would have produced a count
|
||||
// of zero. This ensures that expressions dependent on optimized-out
|
||||
// expressions are still valid.
|
||||
.map_or(Some(Counter::zero()), |_| new_indexes[index].map(Counter::expression))
|
||||
.map_or(Some(Counter::zero()), |_| new_indexes[id].map(Counter::expression))
|
||||
}
|
||||
};
|
||||
|
||||
@ -340,9 +308,4 @@ impl<'tcx> FunctionCoverage<'tcx> {
|
||||
fn unreachable_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> {
|
||||
self.unreachable_regions.iter().map(|region| (Counter::zero(), region))
|
||||
}
|
||||
|
||||
fn expression_index(&self, id_descending_from_max: u32) -> InjectedExpressionIndex {
|
||||
debug_assert!(id_descending_from_max >= self.counters.len() as u32);
|
||||
InjectedExpressionIndex::from(u32::MAX - id_descending_from_max)
|
||||
}
|
||||
}
|
||||
|
@ -16,9 +16,7 @@ use rustc_hir as hir;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_llvm::RustString;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::mir::coverage::{
|
||||
CodeRegion, CounterValueReference, CoverageKind, ExpressionOperandId, InjectedExpressionId, Op,
|
||||
};
|
||||
use rustc_middle::mir::coverage::{CodeRegion, CounterId, CoverageKind, ExpressionId, Op, Operand};
|
||||
use rustc_middle::mir::Coverage;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
|
||||
@ -33,7 +31,7 @@ mod ffi;
|
||||
pub(crate) mod map_data;
|
||||
pub mod mapgen;
|
||||
|
||||
const UNUSED_FUNCTION_COUNTER_ID: CounterValueReference = CounterValueReference::START;
|
||||
const UNUSED_FUNCTION_COUNTER_ID: CounterId = CounterId::START;
|
||||
|
||||
const VAR_ALIGN_BYTES: usize = 8;
|
||||
|
||||
@ -125,7 +123,7 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
||||
let fn_name = bx.get_pgo_func_name_var(instance);
|
||||
let hash = bx.const_u64(function_source_hash);
|
||||
let num_counters = bx.const_u32(coverageinfo.num_counters);
|
||||
let index = bx.const_u32(id.zero_based_index());
|
||||
let index = bx.const_u32(id.as_u32());
|
||||
debug!(
|
||||
"codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})",
|
||||
fn_name, hash, num_counters, index,
|
||||
@ -178,7 +176,7 @@ impl<'tcx> Builder<'_, '_, 'tcx> {
|
||||
fn add_coverage_counter(
|
||||
&mut self,
|
||||
instance: Instance<'tcx>,
|
||||
id: CounterValueReference,
|
||||
id: CounterId,
|
||||
region: CodeRegion,
|
||||
) -> bool {
|
||||
if let Some(coverage_context) = self.coverage_context() {
|
||||
@ -202,10 +200,10 @@ impl<'tcx> Builder<'_, '_, 'tcx> {
|
||||
fn add_coverage_counter_expression(
|
||||
&mut self,
|
||||
instance: Instance<'tcx>,
|
||||
id: InjectedExpressionId,
|
||||
lhs: ExpressionOperandId,
|
||||
id: ExpressionId,
|
||||
lhs: Operand,
|
||||
op: Op,
|
||||
rhs: ExpressionOperandId,
|
||||
rhs: Operand,
|
||||
region: Option<CodeRegion>,
|
||||
) -> bool {
|
||||
if let Some(coverage_context) = self.coverage_context() {
|
||||
|
@ -208,13 +208,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
||||
let rest_ptr = first_ptr.offset(elem_size, self)?;
|
||||
// For the alignment of `rest_ptr`, we crucially do *not* use `first.align` as
|
||||
// that place might be more aligned than its type mandates (a `u8` array could
|
||||
// be 4-aligned if it sits at the right spot in a struct). Instead we use
|
||||
// `first.layout.align`, i.e., the alignment given by the type.
|
||||
// be 4-aligned if it sits at the right spot in a struct). We have to also factor
|
||||
// in element size.
|
||||
self.mem_copy_repeatedly(
|
||||
first_ptr,
|
||||
first.align,
|
||||
dest.align,
|
||||
rest_ptr,
|
||||
first.layout.align.abi,
|
||||
dest.align.restrict_for_offset(elem_size),
|
||||
elem_size,
|
||||
length - 1,
|
||||
/*nonoverlapping:*/ true,
|
||||
|
@ -16,22 +16,21 @@ const BASE_64: &[u8; MAX_BASE] =
|
||||
pub fn push_str(mut n: u128, base: usize, output: &mut String) {
|
||||
debug_assert!(base >= 2 && base <= MAX_BASE);
|
||||
let mut s = [0u8; 128];
|
||||
let mut index = 0;
|
||||
let mut index = s.len();
|
||||
|
||||
let base = base as u128;
|
||||
|
||||
loop {
|
||||
index -= 1;
|
||||
s[index] = BASE_64[(n % base) as usize];
|
||||
index += 1;
|
||||
n /= base;
|
||||
|
||||
if n == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
s[0..index].reverse();
|
||||
|
||||
output.push_str(str::from_utf8(&s[0..index]).unwrap());
|
||||
output.push_str(str::from_utf8(&s[index..]).unwrap());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -43,37 +43,23 @@ impl<I: Idx, T: Copy> AppendOnlyIndexVec<I, T> {
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct AppendOnlyVec<T: Copy> {
|
||||
#[cfg(not(parallel_compiler))]
|
||||
vec: elsa::vec::FrozenVec<T>,
|
||||
#[cfg(parallel_compiler)]
|
||||
vec: elsa::sync::LockFreeFrozenVec<T>,
|
||||
vec: parking_lot::RwLock<Vec<T>>,
|
||||
}
|
||||
|
||||
impl<T: Copy> AppendOnlyVec<T> {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
#[cfg(not(parallel_compiler))]
|
||||
vec: elsa::vec::FrozenVec::new(),
|
||||
#[cfg(parallel_compiler)]
|
||||
vec: elsa::sync::LockFreeFrozenVec::new(),
|
||||
}
|
||||
Self { vec: Default::default() }
|
||||
}
|
||||
|
||||
pub fn push(&self, val: T) -> usize {
|
||||
#[cfg(not(parallel_compiler))]
|
||||
let i = self.vec.len();
|
||||
#[cfg(not(parallel_compiler))]
|
||||
self.vec.push(val);
|
||||
#[cfg(parallel_compiler)]
|
||||
let i = self.vec.push(val);
|
||||
i
|
||||
let mut v = self.vec.write();
|
||||
let n = v.len();
|
||||
v.push(val);
|
||||
n
|
||||
}
|
||||
|
||||
pub fn get(&self, i: usize) -> Option<T> {
|
||||
#[cfg(not(parallel_compiler))]
|
||||
return self.vec.get_copy(i);
|
||||
#[cfg(parallel_compiler)]
|
||||
return self.vec.get(i);
|
||||
self.vec.read().get(i).copied()
|
||||
}
|
||||
|
||||
pub fn iter_enumerated(&self) -> impl Iterator<Item = (usize, T)> + '_ {
|
||||
|
@ -4,7 +4,7 @@ use super::method::MethodCallee;
|
||||
use super::{has_expected_num_generic_args, FnCtxt};
|
||||
use crate::Expectation;
|
||||
use rustc_ast as ast;
|
||||
use rustc_errors::{self, struct_span_err, Applicability, Diagnostic};
|
||||
use rustc_errors::{self, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder};
|
||||
use rustc_hir as hir;
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::traits::ObligationCauseCode;
|
||||
@ -380,33 +380,93 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
};
|
||||
|
||||
let mut suggest_deref_binop = |lhs_deref_ty: Ty<'tcx>| {
|
||||
if self
|
||||
.lookup_op_method(
|
||||
lhs_deref_ty,
|
||||
Some((rhs_expr, rhs_ty)),
|
||||
Op::Binary(op, is_assign),
|
||||
expected,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
let msg = format!(
|
||||
"`{}{}` can be used on `{}` if you dereference the left-hand side",
|
||||
op.node.as_str(),
|
||||
match is_assign {
|
||||
IsAssign::Yes => "=",
|
||||
IsAssign::No => "",
|
||||
},
|
||||
lhs_deref_ty,
|
||||
);
|
||||
err.span_suggestion_verbose(
|
||||
lhs_expr.span.shrink_to_lo(),
|
||||
msg,
|
||||
"*",
|
||||
rustc_errors::Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
};
|
||||
let suggest_deref_binop =
|
||||
|err: &mut DiagnosticBuilder<'_, _>, lhs_deref_ty: Ty<'tcx>| {
|
||||
if self
|
||||
.lookup_op_method(
|
||||
lhs_deref_ty,
|
||||
Some((rhs_expr, rhs_ty)),
|
||||
Op::Binary(op, is_assign),
|
||||
expected,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
let msg = format!(
|
||||
"`{}{}` can be used on `{}` if you dereference the left-hand side",
|
||||
op.node.as_str(),
|
||||
match is_assign {
|
||||
IsAssign::Yes => "=",
|
||||
IsAssign::No => "",
|
||||
},
|
||||
lhs_deref_ty,
|
||||
);
|
||||
err.span_suggestion_verbose(
|
||||
lhs_expr.span.shrink_to_lo(),
|
||||
msg,
|
||||
"*",
|
||||
rustc_errors::Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
let suggest_different_borrow =
|
||||
|err: &mut DiagnosticBuilder<'_, _>,
|
||||
lhs_adjusted_ty,
|
||||
lhs_new_mutbl: Option<ast::Mutability>,
|
||||
rhs_adjusted_ty,
|
||||
rhs_new_mutbl: Option<ast::Mutability>| {
|
||||
if self
|
||||
.lookup_op_method(
|
||||
lhs_adjusted_ty,
|
||||
Some((rhs_expr, rhs_adjusted_ty)),
|
||||
Op::Binary(op, is_assign),
|
||||
expected,
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
let op_str = op.node.as_str();
|
||||
err.note(format!("an implementation for `{lhs_adjusted_ty} {op_str} {rhs_adjusted_ty}` exists"));
|
||||
|
||||
if let Some(lhs_new_mutbl) = lhs_new_mutbl
|
||||
&& let Some(rhs_new_mutbl) = rhs_new_mutbl
|
||||
&& lhs_new_mutbl.is_not()
|
||||
&& rhs_new_mutbl.is_not() {
|
||||
err.multipart_suggestion_verbose(
|
||||
"consider reborrowing both sides",
|
||||
vec![
|
||||
(lhs_expr.span.shrink_to_lo(), "&*".to_string()),
|
||||
(rhs_expr.span.shrink_to_lo(), "&*".to_string())
|
||||
],
|
||||
rustc_errors::Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
let mut suggest_new_borrow = |new_mutbl: ast::Mutability, sp: Span| {
|
||||
// Can reborrow (&mut -> &)
|
||||
if new_mutbl.is_not() {
|
||||
err.span_suggestion_verbose(
|
||||
sp.shrink_to_lo(),
|
||||
"consider reborrowing this side",
|
||||
"&*",
|
||||
rustc_errors::Applicability::MachineApplicable,
|
||||
);
|
||||
// Works on &mut but have &
|
||||
} else {
|
||||
err.span_help(
|
||||
sp,
|
||||
"consider making this expression a mutable borrow",
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(lhs_new_mutbl) = lhs_new_mutbl {
|
||||
suggest_new_borrow(lhs_new_mutbl, lhs_expr.span);
|
||||
}
|
||||
if let Some(rhs_new_mutbl) = rhs_new_mutbl {
|
||||
suggest_new_borrow(rhs_new_mutbl, rhs_expr.span);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let is_compatible_after_call = |lhs_ty, rhs_ty| {
|
||||
self.lookup_op_method(
|
||||
@ -429,15 +489,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
} else if is_assign == IsAssign::Yes
|
||||
&& let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty)
|
||||
{
|
||||
suggest_deref_binop(lhs_deref_ty);
|
||||
suggest_deref_binop(&mut err, lhs_deref_ty);
|
||||
} else if is_assign == IsAssign::No
|
||||
&& let Ref(_, lhs_deref_ty, _) = lhs_ty.kind()
|
||||
&& let Ref(region, lhs_deref_ty, mutbl) = lhs_ty.kind()
|
||||
{
|
||||
if self.type_is_copy_modulo_regions(
|
||||
self.param_env,
|
||||
*lhs_deref_ty,
|
||||
) {
|
||||
suggest_deref_binop(*lhs_deref_ty);
|
||||
suggest_deref_binop(&mut err, *lhs_deref_ty);
|
||||
} else {
|
||||
let lhs_inv_mutbl = mutbl.invert();
|
||||
let lhs_inv_mutbl_ty = Ty::new_ref(
|
||||
self.tcx,
|
||||
*region,
|
||||
ty::TypeAndMut {
|
||||
ty: *lhs_deref_ty,
|
||||
mutbl: lhs_inv_mutbl,
|
||||
},
|
||||
);
|
||||
|
||||
suggest_different_borrow(
|
||||
&mut err,
|
||||
lhs_inv_mutbl_ty,
|
||||
Some(lhs_inv_mutbl),
|
||||
rhs_ty,
|
||||
None,
|
||||
);
|
||||
|
||||
if let Ref(region, rhs_deref_ty, mutbl) = rhs_ty.kind() {
|
||||
let rhs_inv_mutbl = mutbl.invert();
|
||||
let rhs_inv_mutbl_ty = Ty::new_ref(
|
||||
self.tcx,
|
||||
*region,
|
||||
ty::TypeAndMut {
|
||||
ty: *rhs_deref_ty,
|
||||
mutbl: rhs_inv_mutbl,
|
||||
},
|
||||
);
|
||||
|
||||
suggest_different_borrow(
|
||||
&mut err,
|
||||
lhs_ty,
|
||||
None,
|
||||
rhs_inv_mutbl_ty,
|
||||
Some(rhs_inv_mutbl),
|
||||
);
|
||||
suggest_different_borrow(
|
||||
&mut err,
|
||||
lhs_inv_mutbl_ty,
|
||||
Some(lhs_inv_mutbl),
|
||||
rhs_inv_mutbl_ty,
|
||||
Some(rhs_inv_mutbl),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| {
|
||||
is_compatible_after_call(lhs_ty, rhs_ty)
|
||||
|
@ -6,69 +6,43 @@ use rustc_span::Symbol;
|
||||
use std::fmt::{self, Debug, Formatter};
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// An ExpressionOperandId value is assigned directly from either a
|
||||
/// CounterValueReference.as_u32() (which ascend from 1) or an ExpressionOperandId.as_u32()
|
||||
/// (which _*descend*_ from u32::MAX). Id value `0` (zero) represents a virtual counter with a
|
||||
/// constant value of `0`.
|
||||
/// ID of a coverage counter. Values ascend from 0.
|
||||
///
|
||||
/// Note that LLVM handles counter IDs as `uint32_t`, so there is no need
|
||||
/// to use a larger representation on the Rust side.
|
||||
#[derive(HashStable)]
|
||||
#[max = 0xFFFF_FFFF]
|
||||
#[debug_format = "ExpressionOperandId({})"]
|
||||
pub struct ExpressionOperandId {
|
||||
}
|
||||
#[debug_format = "CounterId({})"]
|
||||
pub struct CounterId {}
|
||||
}
|
||||
|
||||
impl ExpressionOperandId {
|
||||
/// An expression operand for a "zero counter", as described in the following references:
|
||||
///
|
||||
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#counter>
|
||||
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#tag>
|
||||
/// * <https://github.com/rust-lang/llvm-project/blob/rustc/13.0-2021-09-30/llvm/docs/CoverageMappingFormat.rst#counter-expressions>
|
||||
///
|
||||
/// This operand can be used to count two or more separate code regions with a single counter,
|
||||
/// if they run sequentially with no branches, by injecting the `Counter` in a `BasicBlock` for
|
||||
/// one of the code regions, and inserting `CounterExpression`s ("add ZERO to the counter") in
|
||||
/// the coverage map for the other code regions.
|
||||
pub const ZERO: Self = Self::from_u32(0);
|
||||
}
|
||||
impl CounterId {
|
||||
pub const START: Self = Self::from_u32(0);
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
#[derive(HashStable)]
|
||||
#[max = 0xFFFF_FFFF]
|
||||
#[debug_format = "CounterValueReference({})"]
|
||||
pub struct CounterValueReference {}
|
||||
}
|
||||
|
||||
impl CounterValueReference {
|
||||
/// Counters start at 1 to reserve 0 for ExpressionOperandId::ZERO.
|
||||
pub const START: Self = Self::from_u32(1);
|
||||
|
||||
/// Returns explicitly-requested zero-based version of the counter id, used
|
||||
/// during codegen. LLVM expects zero-based indexes.
|
||||
pub fn zero_based_index(self) -> u32 {
|
||||
let one_based_index = self.as_u32();
|
||||
debug_assert!(one_based_index > 0);
|
||||
one_based_index - 1
|
||||
#[inline(always)]
|
||||
pub fn next_id(self) -> Self {
|
||||
Self::from_u32(self.as_u32() + 1)
|
||||
}
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// InjectedExpressionId.as_u32() converts to ExpressionOperandId.as_u32()
|
||||
/// ID of a coverage-counter expression. Values ascend from 0.
|
||||
///
|
||||
/// Values descend from u32::MAX.
|
||||
/// Note that LLVM handles expression IDs as `uint32_t`, so there is no need
|
||||
/// to use a larger representation on the Rust side.
|
||||
#[derive(HashStable)]
|
||||
#[max = 0xFFFF_FFFF]
|
||||
#[debug_format = "InjectedExpressionId({})"]
|
||||
pub struct InjectedExpressionId {}
|
||||
#[debug_format = "ExpressionId({})"]
|
||||
pub struct ExpressionId {}
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
/// InjectedExpressionIndex.as_u32() translates to u32::MAX - ExpressionOperandId.as_u32()
|
||||
///
|
||||
/// Values ascend from 0.
|
||||
#[derive(HashStable)]
|
||||
#[max = 0xFFFF_FFFF]
|
||||
#[debug_format = "InjectedExpressionIndex({})"]
|
||||
pub struct InjectedExpressionIndex {}
|
||||
impl ExpressionId {
|
||||
pub const START: Self = Self::from_u32(0);
|
||||
|
||||
#[inline(always)]
|
||||
pub fn next_id(self) -> Self {
|
||||
Self::from_u32(self.as_u32() + 1)
|
||||
}
|
||||
}
|
||||
|
||||
rustc_index::newtype_index! {
|
||||
@ -81,17 +55,25 @@ rustc_index::newtype_index! {
|
||||
pub struct MappedExpressionIndex {}
|
||||
}
|
||||
|
||||
impl From<CounterValueReference> for ExpressionOperandId {
|
||||
#[inline]
|
||||
fn from(v: CounterValueReference) -> ExpressionOperandId {
|
||||
ExpressionOperandId::from(v.as_u32())
|
||||
}
|
||||
/// Operand of a coverage-counter expression.
|
||||
///
|
||||
/// Operands can be a constant zero value, an actual coverage counter, or another
|
||||
/// expression. Counter/expression operands are referred to by ID.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub enum Operand {
|
||||
Zero,
|
||||
Counter(CounterId),
|
||||
Expression(ExpressionId),
|
||||
}
|
||||
|
||||
impl From<InjectedExpressionId> for ExpressionOperandId {
|
||||
#[inline]
|
||||
fn from(v: InjectedExpressionId) -> ExpressionOperandId {
|
||||
ExpressionOperandId::from(v.as_u32())
|
||||
impl Debug for Operand {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Zero => write!(f, "Zero"),
|
||||
Self::Counter(id) => f.debug_tuple("Counter").field(&id.as_u32()).finish(),
|
||||
Self::Expression(id) => f.debug_tuple("Expression").field(&id.as_u32()).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -99,23 +81,27 @@ impl From<InjectedExpressionId> for ExpressionOperandId {
|
||||
pub enum CoverageKind {
|
||||
Counter {
|
||||
function_source_hash: u64,
|
||||
id: CounterValueReference,
|
||||
/// ID of this counter within its enclosing function.
|
||||
/// Expressions in the same function can refer to it as an operand.
|
||||
id: CounterId,
|
||||
},
|
||||
Expression {
|
||||
id: InjectedExpressionId,
|
||||
lhs: ExpressionOperandId,
|
||||
/// ID of this coverage-counter expression within its enclosing function.
|
||||
/// Other expressions in the same function can refer to it as an operand.
|
||||
id: ExpressionId,
|
||||
lhs: Operand,
|
||||
op: Op,
|
||||
rhs: ExpressionOperandId,
|
||||
rhs: Operand,
|
||||
},
|
||||
Unreachable,
|
||||
}
|
||||
|
||||
impl CoverageKind {
|
||||
pub fn as_operand_id(&self) -> ExpressionOperandId {
|
||||
pub fn as_operand(&self) -> Operand {
|
||||
use CoverageKind::*;
|
||||
match *self {
|
||||
Counter { id, .. } => ExpressionOperandId::from(id),
|
||||
Expression { id, .. } => ExpressionOperandId::from(id),
|
||||
Counter { id, .. } => Operand::Counter(id),
|
||||
Expression { id, .. } => Operand::Expression(id),
|
||||
Unreachable => bug!("Unreachable coverage cannot be part of an expression"),
|
||||
}
|
||||
}
|
||||
@ -132,14 +118,14 @@ impl Debug for CoverageKind {
|
||||
Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()),
|
||||
Expression { id, lhs, op, rhs } => write!(
|
||||
fmt,
|
||||
"Expression({:?}) = {} {} {}",
|
||||
"Expression({:?}) = {:?} {} {:?}",
|
||||
id.index(),
|
||||
lhs.index(),
|
||||
lhs,
|
||||
match op {
|
||||
Op::Add => "+",
|
||||
Op::Subtract => "-",
|
||||
},
|
||||
rhs.index(),
|
||||
rhs,
|
||||
),
|
||||
Unreachable => write!(fmt, "Unreachable"),
|
||||
}
|
||||
|
@ -470,10 +470,8 @@ TrivialTypeTraversalAndLiftImpls! {
|
||||
::rustc_hir::Unsafety,
|
||||
::rustc_target::asm::InlineAsmRegOrRegClass,
|
||||
::rustc_target::spec::abi::Abi,
|
||||
crate::mir::coverage::ExpressionOperandId,
|
||||
crate::mir::coverage::CounterValueReference,
|
||||
crate::mir::coverage::InjectedExpressionId,
|
||||
crate::mir::coverage::InjectedExpressionIndex,
|
||||
crate::mir::coverage::CounterId,
|
||||
crate::mir::coverage::ExpressionId,
|
||||
crate::mir::coverage::MappedExpressionIndex,
|
||||
crate::mir::Local,
|
||||
crate::mir::Promoted,
|
||||
|
@ -16,8 +16,8 @@ use rustc_middle::mir::coverage::*;
|
||||
/// `Coverage` statements.
|
||||
pub(super) struct CoverageCounters {
|
||||
function_source_hash: u64,
|
||||
next_counter_id: u32,
|
||||
num_expressions: u32,
|
||||
next_counter_id: CounterId,
|
||||
next_expression_id: ExpressionId,
|
||||
pub debug_counters: DebugCounters,
|
||||
}
|
||||
|
||||
@ -25,8 +25,8 @@ impl CoverageCounters {
|
||||
pub fn new(function_source_hash: u64) -> Self {
|
||||
Self {
|
||||
function_source_hash,
|
||||
next_counter_id: CounterValueReference::START.as_u32(),
|
||||
num_expressions: 0,
|
||||
next_counter_id: CounterId::START,
|
||||
next_expression_id: ExpressionId::START,
|
||||
debug_counters: DebugCounters::new(),
|
||||
}
|
||||
}
|
||||
@ -65,9 +65,9 @@ impl CoverageCounters {
|
||||
|
||||
fn make_expression<F>(
|
||||
&mut self,
|
||||
lhs: ExpressionOperandId,
|
||||
lhs: Operand,
|
||||
op: Op,
|
||||
rhs: ExpressionOperandId,
|
||||
rhs: Operand,
|
||||
debug_block_label_fn: F,
|
||||
) -> CoverageKind
|
||||
where
|
||||
@ -81,33 +81,30 @@ impl CoverageCounters {
|
||||
expression
|
||||
}
|
||||
|
||||
pub fn make_identity_counter(&mut self, counter_operand: ExpressionOperandId) -> CoverageKind {
|
||||
pub fn make_identity_counter(&mut self, counter_operand: Operand) -> CoverageKind {
|
||||
let some_debug_block_label = if self.debug_counters.is_enabled() {
|
||||
self.debug_counters.some_block_label(counter_operand).cloned()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
self.make_expression(counter_operand, Op::Add, ExpressionOperandId::ZERO, || {
|
||||
self.make_expression(counter_operand, Op::Add, Operand::Zero, || {
|
||||
some_debug_block_label.clone()
|
||||
})
|
||||
}
|
||||
|
||||
/// Counter IDs start from one and go up.
|
||||
fn next_counter(&mut self) -> CounterValueReference {
|
||||
assert!(self.next_counter_id < u32::MAX - self.num_expressions);
|
||||
fn next_counter(&mut self) -> CounterId {
|
||||
let next = self.next_counter_id;
|
||||
self.next_counter_id += 1;
|
||||
CounterValueReference::from(next)
|
||||
self.next_counter_id = next.next_id();
|
||||
next
|
||||
}
|
||||
|
||||
/// Expression IDs start from u32::MAX and go down because an Expression can reference
|
||||
/// (add or subtract counts) of both Counter regions and Expression regions. The counter
|
||||
/// expression operand IDs must be unique across both types.
|
||||
fn next_expression(&mut self) -> InjectedExpressionId {
|
||||
assert!(self.next_counter_id < u32::MAX - self.num_expressions);
|
||||
let next = u32::MAX - self.num_expressions;
|
||||
self.num_expressions += 1;
|
||||
InjectedExpressionId::from(next)
|
||||
/// Expression IDs start from 0 and go up.
|
||||
/// (Counter IDs and Expression IDs are distinguished by the `Operand` enum.)
|
||||
fn next_expression(&mut self) -> ExpressionId {
|
||||
let next = self.next_expression_id;
|
||||
self.next_expression_id = next.next_id();
|
||||
next
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,7 +196,7 @@ impl<'a> BcbCounters<'a> {
|
||||
&mut self,
|
||||
traversal: &mut TraverseCoverageGraphWithLoops,
|
||||
branching_bcb: BasicCoverageBlock,
|
||||
branching_counter_operand: ExpressionOperandId,
|
||||
branching_counter_operand: Operand,
|
||||
collect_intermediate_expressions: &mut Vec<CoverageKind>,
|
||||
) -> Result<(), Error> {
|
||||
let branches = self.bcb_branches(branching_bcb);
|
||||
@ -261,7 +258,7 @@ impl<'a> BcbCounters<'a> {
|
||||
" [new intermediate expression: {}]",
|
||||
self.format_counter(&intermediate_expression)
|
||||
);
|
||||
let intermediate_expression_operand = intermediate_expression.as_operand_id();
|
||||
let intermediate_expression_operand = intermediate_expression.as_operand();
|
||||
collect_intermediate_expressions.push(intermediate_expression);
|
||||
some_sumup_counter_operand.replace(intermediate_expression_operand);
|
||||
}
|
||||
@ -298,7 +295,7 @@ impl<'a> BcbCounters<'a> {
|
||||
&mut self,
|
||||
bcb: BasicCoverageBlock,
|
||||
collect_intermediate_expressions: &mut Vec<CoverageKind>,
|
||||
) -> Result<ExpressionOperandId, Error> {
|
||||
) -> Result<Operand, Error> {
|
||||
self.recursive_get_or_make_counter_operand(bcb, collect_intermediate_expressions, 1)
|
||||
}
|
||||
|
||||
@ -307,7 +304,7 @@ impl<'a> BcbCounters<'a> {
|
||||
bcb: BasicCoverageBlock,
|
||||
collect_intermediate_expressions: &mut Vec<CoverageKind>,
|
||||
debug_indent_level: usize,
|
||||
) -> Result<ExpressionOperandId, Error> {
|
||||
) -> Result<Operand, Error> {
|
||||
// If the BCB already has a counter, return it.
|
||||
if let Some(counter_kind) = self.basic_coverage_blocks[bcb].counter() {
|
||||
debug!(
|
||||
@ -316,7 +313,7 @@ impl<'a> BcbCounters<'a> {
|
||||
bcb,
|
||||
self.format_counter(counter_kind),
|
||||
);
|
||||
return Ok(counter_kind.as_operand_id());
|
||||
return Ok(counter_kind.as_operand());
|
||||
}
|
||||
|
||||
// A BCB with only one incoming edge gets a simple `Counter` (via `make_counter()`).
|
||||
@ -383,7 +380,7 @@ impl<'a> BcbCounters<'a> {
|
||||
NESTED_INDENT.repeat(debug_indent_level),
|
||||
self.format_counter(&intermediate_expression)
|
||||
);
|
||||
let intermediate_expression_operand = intermediate_expression.as_operand_id();
|
||||
let intermediate_expression_operand = intermediate_expression.as_operand();
|
||||
collect_intermediate_expressions.push(intermediate_expression);
|
||||
some_sumup_edge_counter_operand.replace(intermediate_expression_operand);
|
||||
}
|
||||
@ -408,7 +405,7 @@ impl<'a> BcbCounters<'a> {
|
||||
from_bcb: BasicCoverageBlock,
|
||||
to_bcb: BasicCoverageBlock,
|
||||
collect_intermediate_expressions: &mut Vec<CoverageKind>,
|
||||
) -> Result<ExpressionOperandId, Error> {
|
||||
) -> Result<Operand, Error> {
|
||||
self.recursive_get_or_make_edge_counter_operand(
|
||||
from_bcb,
|
||||
to_bcb,
|
||||
@ -423,7 +420,7 @@ impl<'a> BcbCounters<'a> {
|
||||
to_bcb: BasicCoverageBlock,
|
||||
collect_intermediate_expressions: &mut Vec<CoverageKind>,
|
||||
debug_indent_level: usize,
|
||||
) -> Result<ExpressionOperandId, Error> {
|
||||
) -> Result<Operand, Error> {
|
||||
// If the source BCB has only one successor (assumed to be the given target), an edge
|
||||
// counter is unnecessary. Just get or make a counter for the source BCB.
|
||||
let successors = self.bcb_successors(from_bcb).iter();
|
||||
@ -444,7 +441,7 @@ impl<'a> BcbCounters<'a> {
|
||||
to_bcb,
|
||||
self.format_counter(counter_kind)
|
||||
);
|
||||
return Ok(counter_kind.as_operand_id());
|
||||
return Ok(counter_kind.as_operand());
|
||||
}
|
||||
|
||||
// Make a new counter to count this edge.
|
||||
|
@ -246,7 +246,7 @@ impl Default for ExpressionFormat {
|
||||
}
|
||||
}
|
||||
|
||||
/// If enabled, this struct maintains a map from `CoverageKind` IDs (as `ExpressionOperandId`) to
|
||||
/// If enabled, this struct maintains a map from `CoverageKind` IDs (as `Operand`) to
|
||||
/// the `CoverageKind` data and optional label (normally, the counter's associated
|
||||
/// `BasicCoverageBlock` format string, if any).
|
||||
///
|
||||
@ -258,7 +258,7 @@ impl Default for ExpressionFormat {
|
||||
/// `DebugCounters` supports a recursive rendering of `Expression` counters, so they can be
|
||||
/// presented as nested expressions such as `(bcb3 - (bcb0 + bcb1))`.
|
||||
pub(super) struct DebugCounters {
|
||||
some_counters: Option<FxHashMap<ExpressionOperandId, DebugCounter>>,
|
||||
some_counters: Option<FxHashMap<Operand, DebugCounter>>,
|
||||
}
|
||||
|
||||
impl DebugCounters {
|
||||
@ -277,14 +277,14 @@ impl DebugCounters {
|
||||
|
||||
pub fn add_counter(&mut self, counter_kind: &CoverageKind, some_block_label: Option<String>) {
|
||||
if let Some(counters) = &mut self.some_counters {
|
||||
let id = counter_kind.as_operand_id();
|
||||
let id = counter_kind.as_operand();
|
||||
counters
|
||||
.try_insert(id, DebugCounter::new(counter_kind.clone(), some_block_label))
|
||||
.expect("attempt to add the same counter_kind to DebugCounters more than once");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn some_block_label(&self, operand: ExpressionOperandId) -> Option<&String> {
|
||||
pub fn some_block_label(&self, operand: Operand) -> Option<&String> {
|
||||
self.some_counters.as_ref().and_then(|counters| {
|
||||
counters.get(&operand).and_then(|debug_counter| debug_counter.some_block_label.as_ref())
|
||||
})
|
||||
@ -323,24 +323,24 @@ impl DebugCounters {
|
||||
}
|
||||
}
|
||||
|
||||
let id = counter_kind.as_operand_id();
|
||||
let id = counter_kind.as_operand();
|
||||
if self.some_counters.is_some() && (counter_format.block || !counter_format.id) {
|
||||
let counters = self.some_counters.as_ref().unwrap();
|
||||
if let Some(DebugCounter { some_block_label: Some(block_label), .. }) =
|
||||
counters.get(&id)
|
||||
{
|
||||
return if counter_format.id {
|
||||
format!("{}#{}", block_label, id.index())
|
||||
format!("{}#{:?}", block_label, id)
|
||||
} else {
|
||||
block_label.to_string()
|
||||
};
|
||||
}
|
||||
}
|
||||
format!("#{}", id.index())
|
||||
format!("#{:?}", id)
|
||||
}
|
||||
|
||||
fn format_operand(&self, operand: ExpressionOperandId) -> String {
|
||||
if operand.index() == 0 {
|
||||
fn format_operand(&self, operand: Operand) -> String {
|
||||
if matches!(operand, Operand::Zero) {
|
||||
return String::from("0");
|
||||
}
|
||||
if let Some(counters) = &self.some_counters {
|
||||
@ -358,7 +358,7 @@ impl DebugCounters {
|
||||
return self.format_counter_kind(counter_kind);
|
||||
}
|
||||
}
|
||||
format!("#{}", operand.index())
|
||||
format!("#{:?}", operand)
|
||||
}
|
||||
}
|
||||
|
||||
@ -485,8 +485,7 @@ impl GraphvizData {
|
||||
/// _not_ used are retained in the `unused_expressions` Vec, to be included in debug output (logs
|
||||
/// and/or a `CoverageGraph` graphviz output).
|
||||
pub(super) struct UsedExpressions {
|
||||
some_used_expression_operands:
|
||||
Option<FxHashMap<ExpressionOperandId, Vec<InjectedExpressionId>>>,
|
||||
some_used_expression_operands: Option<FxHashMap<Operand, Vec<ExpressionId>>>,
|
||||
some_unused_expressions:
|
||||
Option<Vec<(CoverageKind, Option<BasicCoverageBlock>, BasicCoverageBlock)>>,
|
||||
}
|
||||
@ -517,7 +516,7 @@ impl UsedExpressions {
|
||||
|
||||
pub fn expression_is_used(&self, expression: &CoverageKind) -> bool {
|
||||
if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() {
|
||||
used_expression_operands.contains_key(&expression.as_operand_id())
|
||||
used_expression_operands.contains_key(&expression.as_operand())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
@ -530,7 +529,7 @@ impl UsedExpressions {
|
||||
target_bcb: BasicCoverageBlock,
|
||||
) {
|
||||
if let Some(used_expression_operands) = self.some_used_expression_operands.as_ref() {
|
||||
if !used_expression_operands.contains_key(&expression.as_operand_id()) {
|
||||
if !used_expression_operands.contains_key(&expression.as_operand()) {
|
||||
self.some_unused_expressions.as_mut().unwrap().push((
|
||||
expression.clone(),
|
||||
edge_from_bcb,
|
||||
|
@ -345,10 +345,7 @@ impl BasicCoverageBlockData {
|
||||
&mir_body[self.last_bb()].terminator()
|
||||
}
|
||||
|
||||
pub fn set_counter(
|
||||
&mut self,
|
||||
counter_kind: CoverageKind,
|
||||
) -> Result<ExpressionOperandId, Error> {
|
||||
pub fn set_counter(&mut self, counter_kind: CoverageKind) -> Result<Operand, Error> {
|
||||
debug_assert!(
|
||||
// If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
|
||||
// have an expression (to be injected into an existing `BasicBlock` represented by this
|
||||
@ -356,7 +353,7 @@ impl BasicCoverageBlockData {
|
||||
self.edge_from_bcbs.is_none() || counter_kind.is_expression(),
|
||||
"attempt to add a `Counter` to a BCB target with existing incoming edge counters"
|
||||
);
|
||||
let operand = counter_kind.as_operand_id();
|
||||
let operand = counter_kind.as_operand();
|
||||
if let Some(replaced) = self.counter_kind.replace(counter_kind) {
|
||||
Error::from_string(format!(
|
||||
"attempt to set a BasicCoverageBlock coverage counter more than once; \
|
||||
@ -381,7 +378,7 @@ impl BasicCoverageBlockData {
|
||||
&mut self,
|
||||
from_bcb: BasicCoverageBlock,
|
||||
counter_kind: CoverageKind,
|
||||
) -> Result<ExpressionOperandId, Error> {
|
||||
) -> Result<Operand, Error> {
|
||||
if level_enabled!(tracing::Level::DEBUG) {
|
||||
// If the BCB has an edge counter (to be injected into a new `BasicBlock`), it can also
|
||||
// have an expression (to be injected into an existing `BasicBlock` represented by this
|
||||
@ -393,7 +390,7 @@ impl BasicCoverageBlockData {
|
||||
));
|
||||
}
|
||||
}
|
||||
let operand = counter_kind.as_operand_id();
|
||||
let operand = counter_kind.as_operand();
|
||||
if let Some(replaced) =
|
||||
self.edge_from_bcbs.get_or_insert_default().insert(from_bcb, counter_kind)
|
||||
{
|
||||
|
@ -304,7 +304,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
||||
let counter_kind = if let Some(&counter_operand) = bcb_counters[bcb].as_ref() {
|
||||
self.coverage_counters.make_identity_counter(counter_operand)
|
||||
} else if let Some(counter_kind) = self.bcb_data_mut(bcb).take_counter() {
|
||||
bcb_counters[bcb] = Some(counter_kind.as_operand_id());
|
||||
bcb_counters[bcb] = Some(counter_kind.as_operand());
|
||||
debug_used_expressions.add_expression_operands(&counter_kind);
|
||||
counter_kind
|
||||
} else {
|
||||
|
@ -43,43 +43,25 @@ struct CoverageVisitor {
|
||||
}
|
||||
|
||||
impl CoverageVisitor {
|
||||
/// Updates `num_counters` to the maximum encountered zero-based counter_id plus 1. Note the
|
||||
/// final computed number of counters should be the number of all `CoverageKind::Counter`
|
||||
/// statements in the MIR *plus one* for the implicit `ZERO` counter.
|
||||
/// Updates `num_counters` to the maximum encountered counter ID plus 1.
|
||||
#[inline(always)]
|
||||
fn update_num_counters(&mut self, counter_id: u32) {
|
||||
fn update_num_counters(&mut self, counter_id: CounterId) {
|
||||
let counter_id = counter_id.as_u32();
|
||||
self.info.num_counters = std::cmp::max(self.info.num_counters, counter_id + 1);
|
||||
}
|
||||
|
||||
/// Computes an expression index for each expression ID, and updates `num_expressions` to the
|
||||
/// maximum encountered index plus 1.
|
||||
/// Updates `num_expressions` to the maximum encountered expression ID plus 1.
|
||||
#[inline(always)]
|
||||
fn update_num_expressions(&mut self, expression_id: u32) {
|
||||
let expression_index = u32::MAX - expression_id;
|
||||
self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_index + 1);
|
||||
fn update_num_expressions(&mut self, expression_id: ExpressionId) {
|
||||
let expression_id = expression_id.as_u32();
|
||||
self.info.num_expressions = std::cmp::max(self.info.num_expressions, expression_id + 1);
|
||||
}
|
||||
|
||||
fn update_from_expression_operand(&mut self, operand_id: u32) {
|
||||
if operand_id >= self.info.num_counters {
|
||||
let operand_as_expression_index = u32::MAX - operand_id;
|
||||
if operand_as_expression_index >= self.info.num_expressions {
|
||||
// The operand ID is outside the known range of counter IDs and also outside the
|
||||
// known range of expression IDs. In either case, the result of a missing operand
|
||||
// (if and when used in an expression) will be zero, so from a computation
|
||||
// perspective, it doesn't matter whether it is interpreted as a counter or an
|
||||
// expression.
|
||||
//
|
||||
// However, the `num_counters` and `num_expressions` query results are used to
|
||||
// allocate arrays when generating the coverage map (during codegen), so choose
|
||||
// the type that grows either `num_counters` or `num_expressions` the least.
|
||||
if operand_id - self.info.num_counters
|
||||
< operand_as_expression_index - self.info.num_expressions
|
||||
{
|
||||
self.update_num_counters(operand_id)
|
||||
} else {
|
||||
self.update_num_expressions(operand_id)
|
||||
}
|
||||
}
|
||||
fn update_from_expression_operand(&mut self, operand: Operand) {
|
||||
match operand {
|
||||
Operand::Counter(id) => self.update_num_counters(id),
|
||||
Operand::Expression(id) => self.update_num_expressions(id),
|
||||
Operand::Zero => {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,19 +82,15 @@ impl CoverageVisitor {
|
||||
if self.add_missing_operands {
|
||||
match coverage.kind {
|
||||
CoverageKind::Expression { lhs, rhs, .. } => {
|
||||
self.update_from_expression_operand(u32::from(lhs));
|
||||
self.update_from_expression_operand(u32::from(rhs));
|
||||
self.update_from_expression_operand(lhs);
|
||||
self.update_from_expression_operand(rhs);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
match coverage.kind {
|
||||
CoverageKind::Counter { id, .. } => {
|
||||
self.update_num_counters(u32::from(id));
|
||||
}
|
||||
CoverageKind::Expression { id, .. } => {
|
||||
self.update_num_expressions(u32::from(id));
|
||||
}
|
||||
CoverageKind::Counter { id, .. } => self.update_num_counters(id),
|
||||
CoverageKind::Expression { id, .. } => self.update_num_expressions(id),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
@ -123,8 +101,7 @@ fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) ->
|
||||
let mir_body = tcx.instance_mir(instance_def);
|
||||
|
||||
let mut coverage_visitor = CoverageVisitor {
|
||||
// num_counters always has at least the `ZERO` counter.
|
||||
info: CoverageInfo { num_counters: 1, num_expressions: 0 },
|
||||
info: CoverageInfo { num_counters: 0, num_expressions: 0 },
|
||||
add_missing_operands: false,
|
||||
};
|
||||
|
||||
|
@ -683,7 +683,7 @@ fn test_make_bcb_counters() {
|
||||
|
||||
let_bcb!(1);
|
||||
assert_eq!(
|
||||
1, // coincidentally, bcb1 has a `Counter` with id = 1
|
||||
0, // bcb1 has a `Counter` with id = 0
|
||||
match basic_coverage_blocks[bcb1].counter().expect("should have a counter") {
|
||||
CoverageKind::Counter { id, .. } => id,
|
||||
_ => panic!("expected a Counter"),
|
||||
@ -693,7 +693,7 @@ fn test_make_bcb_counters() {
|
||||
|
||||
let_bcb!(2);
|
||||
assert_eq!(
|
||||
2, // coincidentally, bcb2 has a `Counter` with id = 2
|
||||
1, // bcb2 has a `Counter` with id = 1
|
||||
match basic_coverage_blocks[bcb2].counter().expect("should have a counter") {
|
||||
CoverageKind::Counter { id, .. } => id,
|
||||
_ => panic!("expected a Counter"),
|
||||
|
@ -8,11 +8,13 @@
|
||||
//! For now, we are developing everything inside `rustc`, thus, we keep this module private.
|
||||
|
||||
use crate::rustc_internal::{self, opaque};
|
||||
use crate::stable_mir::mir::{CopyNonOverlapping, UserTypeProjection, VariantIdx};
|
||||
use crate::stable_mir::ty::{FloatTy, IntTy, Movability, RigidTy, TyKind, UintTy};
|
||||
use crate::stable_mir::{self, Context};
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::mir::coverage::CodeRegion;
|
||||
use rustc_middle::mir::{self};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, Variance};
|
||||
use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
|
||||
use rustc_target::abi::FieldIdx;
|
||||
use tracing::debug;
|
||||
@ -110,17 +112,38 @@ impl<'tcx> Stable<'tcx> for mir::Statement<'tcx> {
|
||||
Assign(assign) => {
|
||||
stable_mir::mir::Statement::Assign(assign.0.stable(tables), assign.1.stable(tables))
|
||||
}
|
||||
FakeRead(_) => todo!(),
|
||||
SetDiscriminant { .. } => todo!(),
|
||||
Deinit(_) => todo!(),
|
||||
StorageLive(_) => todo!(),
|
||||
StorageDead(_) => todo!(),
|
||||
Retag(_, _) => todo!(),
|
||||
PlaceMention(_) => todo!(),
|
||||
AscribeUserType(_, _) => todo!(),
|
||||
Coverage(_) => todo!(),
|
||||
Intrinsic(_) => todo!(),
|
||||
ConstEvalCounter => todo!(),
|
||||
FakeRead(fake_read_place) => stable_mir::mir::Statement::FakeRead(
|
||||
fake_read_place.0.stable(tables),
|
||||
fake_read_place.1.stable(tables),
|
||||
),
|
||||
SetDiscriminant { place: plc, variant_index: idx } => {
|
||||
stable_mir::mir::Statement::SetDiscriminant {
|
||||
place: plc.as_ref().stable(tables),
|
||||
variant_index: idx.stable(tables),
|
||||
}
|
||||
}
|
||||
Deinit(place) => stable_mir::mir::Statement::Deinit(place.stable(tables)),
|
||||
StorageLive(place) => stable_mir::mir::Statement::StorageLive(place.stable(tables)),
|
||||
StorageDead(place) => stable_mir::mir::Statement::StorageDead(place.stable(tables)),
|
||||
Retag(retag, place) => {
|
||||
stable_mir::mir::Statement::Retag(retag.stable(tables), place.stable(tables))
|
||||
}
|
||||
PlaceMention(place) => stable_mir::mir::Statement::PlaceMention(place.stable(tables)),
|
||||
AscribeUserType(place_projection, variance) => {
|
||||
stable_mir::mir::Statement::AscribeUserType {
|
||||
place: place_projection.as_ref().0.stable(tables),
|
||||
projections: place_projection.as_ref().1.stable(tables),
|
||||
variance: variance.stable(tables),
|
||||
}
|
||||
}
|
||||
Coverage(coverage) => stable_mir::mir::Statement::Coverage(stable_mir::mir::Coverage {
|
||||
kind: coverage.kind.stable(tables),
|
||||
code_region: coverage.code_region.as_ref().map(|reg| reg.stable(tables)),
|
||||
}),
|
||||
Intrinsic(intrinstic) => {
|
||||
stable_mir::mir::Statement::Intrinsic(intrinstic.stable(tables))
|
||||
}
|
||||
ConstEvalCounter => stable_mir::mir::Statement::ConstEvalCounter,
|
||||
Nop => stable_mir::mir::Statement::Nop,
|
||||
}
|
||||
}
|
||||
@ -364,6 +387,22 @@ impl<'tcx> Stable<'tcx> for rustc_hir::Unsafety {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for mir::FakeReadCause {
|
||||
type T = stable_mir::mir::FakeReadCause;
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
use mir::FakeReadCause::*;
|
||||
match self {
|
||||
ForMatchGuard => stable_mir::mir::FakeReadCause::ForMatchGuard,
|
||||
ForMatchedPlace(local_def_id) => {
|
||||
stable_mir::mir::FakeReadCause::ForMatchedPlace(opaque(local_def_id))
|
||||
}
|
||||
ForGuardBinding => stable_mir::mir::FakeReadCause::ForGuardBinding,
|
||||
ForLet(local_def_id) => stable_mir::mir::FakeReadCause::ForLet(opaque(local_def_id)),
|
||||
ForIndex => stable_mir::mir::FakeReadCause::ForIndex,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for FieldIdx {
|
||||
type T = usize;
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
@ -393,6 +432,110 @@ impl<'tcx> Stable<'tcx> for mir::Place<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for mir::coverage::CoverageKind {
|
||||
type T = stable_mir::mir::CoverageKind;
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
use rustc_middle::mir::coverage::CoverageKind;
|
||||
match self {
|
||||
CoverageKind::Counter { function_source_hash, id } => {
|
||||
stable_mir::mir::CoverageKind::Counter {
|
||||
function_source_hash: *function_source_hash as usize,
|
||||
id: opaque(id),
|
||||
}
|
||||
}
|
||||
CoverageKind::Expression { id, lhs, op, rhs } => {
|
||||
stable_mir::mir::CoverageKind::Expression {
|
||||
id: opaque(id),
|
||||
lhs: opaque(lhs),
|
||||
op: op.stable(tables),
|
||||
rhs: opaque(rhs),
|
||||
}
|
||||
}
|
||||
CoverageKind::Unreachable => stable_mir::mir::CoverageKind::Unreachable,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for mir::UserTypeProjection {
|
||||
type T = stable_mir::mir::UserTypeProjection;
|
||||
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
UserTypeProjection { base: self.base.as_usize(), projection: format!("{:?}", self.projs) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for mir::coverage::Op {
|
||||
type T = stable_mir::mir::Op;
|
||||
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
use rustc_middle::mir::coverage::Op::*;
|
||||
match self {
|
||||
Subtract => stable_mir::mir::Op::Subtract,
|
||||
Add => stable_mir::mir::Op::Add,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for mir::Local {
|
||||
type T = stable_mir::mir::Local;
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
self.as_usize()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_target::abi::VariantIdx {
|
||||
type T = VariantIdx;
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
self.as_usize()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for Variance {
|
||||
type T = stable_mir::mir::Variance;
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
match self {
|
||||
Variance::Bivariant => stable_mir::mir::Variance::Bivariant,
|
||||
Variance::Contravariant => stable_mir::mir::Variance::Contravariant,
|
||||
Variance::Covariant => stable_mir::mir::Variance::Covariant,
|
||||
Variance::Invariant => stable_mir::mir::Variance::Invariant,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for mir::RetagKind {
|
||||
type T = stable_mir::mir::RetagKind;
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
use rustc_middle::mir::RetagKind;
|
||||
match self {
|
||||
RetagKind::FnEntry => stable_mir::mir::RetagKind::FnEntry,
|
||||
RetagKind::TwoPhase => stable_mir::mir::RetagKind::TwoPhase,
|
||||
RetagKind::Raw => stable_mir::mir::RetagKind::Raw,
|
||||
RetagKind::Default => stable_mir::mir::RetagKind::Default,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_middle::ty::UserTypeAnnotationIndex {
|
||||
type T = usize;
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
self.as_usize()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for CodeRegion {
|
||||
type T = stable_mir::mir::CodeRegion;
|
||||
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
stable_mir::mir::CodeRegion {
|
||||
file_name: self.file_name.as_str().to_string(),
|
||||
start_line: self.start_line as usize,
|
||||
start_col: self.start_col as usize,
|
||||
end_line: self.end_line as usize,
|
||||
end_col: self.end_col as usize,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for mir::UnwindAction {
|
||||
type T = stable_mir::mir::UnwindAction;
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
@ -406,6 +549,26 @@ impl<'tcx> Stable<'tcx> for mir::UnwindAction {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for mir::NonDivergingIntrinsic<'tcx> {
|
||||
type T = stable_mir::mir::NonDivergingIntrinsic;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
use rustc_middle::mir::NonDivergingIntrinsic;
|
||||
match self {
|
||||
NonDivergingIntrinsic::Assume(op) => {
|
||||
stable_mir::mir::NonDivergingIntrinsic::Assume(op.stable(tables))
|
||||
}
|
||||
NonDivergingIntrinsic::CopyNonOverlapping(copy_non_overlapping) => {
|
||||
stable_mir::mir::NonDivergingIntrinsic::CopyNonOverlapping(CopyNonOverlapping {
|
||||
src: copy_non_overlapping.src.stable(tables),
|
||||
dst: copy_non_overlapping.dst.stable(tables),
|
||||
count: copy_non_overlapping.count.stable(tables),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for mir::AssertMessage<'tcx> {
|
||||
type T = stable_mir::mir::AssertMessage;
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
|
@ -1,3 +1,4 @@
|
||||
use crate::rustc_internal::Opaque;
|
||||
use crate::stable_mir::ty::{
|
||||
AdtDef, ClosureDef, Const, GeneratorDef, GenericArgs, Movability, Region,
|
||||
};
|
||||
@ -133,9 +134,101 @@ pub enum AsyncGeneratorKind {
|
||||
Fn,
|
||||
}
|
||||
|
||||
pub(crate) type LocalDefId = Opaque;
|
||||
pub(crate) type CounterValueReference = Opaque;
|
||||
pub(crate) type InjectedExpressionId = Opaque;
|
||||
pub(crate) type ExpressionOperandId = Opaque;
|
||||
|
||||
/// The FakeReadCause describes the type of pattern why a FakeRead statement exists.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum FakeReadCause {
|
||||
ForMatchGuard,
|
||||
ForMatchedPlace(LocalDefId),
|
||||
ForGuardBinding,
|
||||
ForLet(LocalDefId),
|
||||
ForIndex,
|
||||
}
|
||||
|
||||
/// Describes what kind of retag is to be performed
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum RetagKind {
|
||||
FnEntry,
|
||||
TwoPhase,
|
||||
Raw,
|
||||
Default,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Variance {
|
||||
Covariant,
|
||||
Invariant,
|
||||
Contravariant,
|
||||
Bivariant,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Op {
|
||||
Subtract,
|
||||
Add,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum CoverageKind {
|
||||
Counter {
|
||||
function_source_hash: usize,
|
||||
id: CounterValueReference,
|
||||
},
|
||||
Expression {
|
||||
id: InjectedExpressionId,
|
||||
lhs: ExpressionOperandId,
|
||||
op: Op,
|
||||
rhs: ExpressionOperandId,
|
||||
},
|
||||
Unreachable,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CodeRegion {
|
||||
pub file_name: String,
|
||||
pub start_line: usize,
|
||||
pub start_col: usize,
|
||||
pub end_line: usize,
|
||||
pub end_col: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Coverage {
|
||||
pub kind: CoverageKind,
|
||||
pub code_region: Option<CodeRegion>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CopyNonOverlapping {
|
||||
pub src: Operand,
|
||||
pub dst: Operand,
|
||||
pub count: Operand,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum NonDivergingIntrinsic {
|
||||
Assume(Operand),
|
||||
CopyNonOverlapping(CopyNonOverlapping),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Statement {
|
||||
Assign(Place, Rvalue),
|
||||
FakeRead(FakeReadCause, Place),
|
||||
SetDiscriminant { place: Place, variant_index: VariantIdx },
|
||||
Deinit(Place),
|
||||
StorageLive(Local),
|
||||
StorageDead(Local),
|
||||
Retag(RetagKind, Place),
|
||||
PlaceMention(Place),
|
||||
AscribeUserType { place: Place, projections: UserTypeProjection, variance: Variance },
|
||||
Coverage(Coverage),
|
||||
Intrinsic(NonDivergingIntrinsic),
|
||||
ConstEvalCounter,
|
||||
Nop,
|
||||
}
|
||||
|
||||
@ -271,14 +364,22 @@ pub enum Operand {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Place {
|
||||
pub local: usize,
|
||||
pub local: Local,
|
||||
pub projection: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct UserTypeProjection {
|
||||
pub base: UserTypeAnnotationIndex,
|
||||
pub projection: String,
|
||||
}
|
||||
|
||||
pub type Local = usize;
|
||||
|
||||
type FieldIdx = usize;
|
||||
|
||||
/// The source-order index of a variant in a type.
|
||||
type VariantIdx = usize;
|
||||
pub type VariantIdx = usize;
|
||||
|
||||
type UserTypeAnnotationIndex = usize;
|
||||
|
||||
|
@ -92,6 +92,7 @@ use crate::backtrace_rs::{self, BytesOrWideString};
|
||||
use crate::env;
|
||||
use crate::ffi::c_void;
|
||||
use crate::fmt;
|
||||
use crate::panic::UnwindSafe;
|
||||
use crate::sync::atomic::{AtomicUsize, Ordering::Relaxed};
|
||||
use crate::sync::LazyLock;
|
||||
use crate::sys_common::backtrace::{lock, output_filename};
|
||||
@ -427,7 +428,7 @@ impl fmt::Display for Backtrace {
|
||||
}
|
||||
}
|
||||
|
||||
type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync;
|
||||
type LazyResolve = impl (FnOnce() -> Capture) + Send + Sync + UnwindSafe;
|
||||
|
||||
fn lazy_resolve(mut capture: Capture) -> LazyResolve {
|
||||
move || {
|
||||
|
@ -1,4 +1,5 @@
|
||||
use super::*;
|
||||
use crate::panic::{RefUnwindSafe, UnwindSafe};
|
||||
|
||||
fn generate_fake_frames() -> Vec<BacktraceFrame> {
|
||||
vec![
|
||||
@ -91,3 +92,9 @@ fn test_frames() {
|
||||
|
||||
assert!(iter.all(|(f, e)| format!("{f:#?}") == *e));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn backtrace_unwind_safe() {
|
||||
fn assert_unwind_safe<T: UnwindSafe + RefUnwindSafe>() {}
|
||||
assert_unwind_safe::<Backtrace>();
|
||||
}
|
||||
|
22
src/tools/miri/tests/pass/align_repeat_into_packed_field.rs
Normal file
22
src/tools/miri/tests/pass/align_repeat_into_packed_field.rs
Normal file
@ -0,0 +1,22 @@
|
||||
#![feature(custom_mir, core_intrinsics)]
|
||||
use std::intrinsics::mir::*;
|
||||
|
||||
#[repr(packed)]
|
||||
struct S { field: [u32; 2] }
|
||||
|
||||
#[custom_mir(dialect = "runtime", phase = "optimized")]
|
||||
fn test() { mir! {
|
||||
let s: S;
|
||||
{
|
||||
// Store a repeat expression directly into a field of a packed struct.
|
||||
s.field = [0; 2];
|
||||
Return()
|
||||
}
|
||||
} }
|
||||
|
||||
fn main() {
|
||||
// Run this a bunch of time to make sure it doesn't pass by chance.
|
||||
for _ in 0..20 {
|
||||
test();
|
||||
}
|
||||
}
|
@ -4,6 +4,8 @@ use std::mem::size_of;
|
||||
|
||||
fn main() {
|
||||
let mut a = Params::new();
|
||||
// The array itself here happens to be quite well-aligned, but not all its elements have that
|
||||
// large alignment and we better make sure that is still accepted by Miri.
|
||||
a.key_block = [0; BLOCKBYTES];
|
||||
}
|
||||
|
@ -2,5 +2,5 @@ digraph Cov_0_4 {
|
||||
graph [fontname="Courier, monospace"];
|
||||
node [fontname="Courier, monospace"];
|
||||
edge [fontname="Courier, monospace"];
|
||||
bcb0__Cov_0_4 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb0</td></tr><tr><td align="left" balign="left"></td></tr><tr><td align="left" balign="left">Counter(bcb0) at 18:1-20:2<br align="left"/> 19:5-19:9: @0[0]: Coverage::Counter(1) for $DIR/coverage_graphviz.rs:18:1 - 20:2<br align="left"/> 20:2-20:2: @0.Return: return</td></tr><tr><td align="left" balign="left">bb0: Return</td></tr></table>>];
|
||||
bcb0__Cov_0_4 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb0</td></tr><tr><td align="left" balign="left"></td></tr><tr><td align="left" balign="left">Counter(bcb0) at 18:1-20:2<br align="left"/> 19:5-19:9: @0[0]: Coverage::Counter(0) for $DIR/coverage_graphviz.rs:18:1 - 20:2<br align="left"/> 20:2-20:2: @0.Return: return</td></tr><tr><td align="left" balign="left">bb0: Return</td></tr></table>>];
|
||||
}
|
||||
|
@ -2,8 +2,8 @@ digraph Cov_0_3 {
|
||||
graph [fontname="Courier, monospace"];
|
||||
node [fontname="Courier, monospace"];
|
||||
edge [fontname="Courier, monospace"];
|
||||
bcb3__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb3</td></tr><tr><td align="left" balign="left">Counter(bcb3) at 13:10-13:10<br align="left"/> 13:10-13:10: @5[0]: Coverage::Counter(2) for $DIR/coverage_graphviz.rs:13:10 - 13:11</td></tr><tr><td align="left" balign="left">bb5: Goto</td></tr></table>>];
|
||||
bcb2__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb2</td></tr><tr><td align="left" balign="left">Expression(bcb1:(bcb0 + bcb3) - bcb3) at 12:13-12:18<br align="left"/> 12:13-12:18: @4[0]: Coverage::Expression(4294967293) = 4294967294 + 0 for $DIR/coverage_graphviz.rs:15:1 - 15:2<br align="left"/>Expression(bcb2:(bcb1:(bcb0 + bcb3) - bcb3) + 0) at 15:2-15:2<br align="left"/> 15:2-15:2: @4.Return: return</td></tr><tr><td align="left" balign="left">bb4: Return</td></tr></table>>];
|
||||
bcb3__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb3</td></tr><tr><td align="left" balign="left">Counter(bcb3) at 13:10-13:10<br align="left"/> 13:10-13:10: @5[0]: Coverage::Counter(1) for $DIR/coverage_graphviz.rs:13:10 - 13:11</td></tr><tr><td align="left" balign="left">bb5: Goto</td></tr></table>>];
|
||||
bcb2__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb2</td></tr><tr><td align="left" balign="left">Expression(bcb1:(bcb0 + bcb3) - bcb3) at 12:13-12:18<br align="left"/> 12:13-12:18: @4[0]: Coverage::Expression(2) = Expression(1) + Zero for $DIR/coverage_graphviz.rs:15:1 - 15:2<br align="left"/>Expression(bcb2:(bcb1:(bcb0 + bcb3) - bcb3) + 0) at 15:2-15:2<br align="left"/> 15:2-15:2: @4.Return: return</td></tr><tr><td align="left" balign="left">bb4: Return</td></tr></table>>];
|
||||
bcb1__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb1</td></tr><tr><td align="left" balign="left">Expression(bcb0 + bcb3) at 10:5-11:17<br align="left"/> 11:12-11:17: @2.Call: _2 = bar() -> [return: bb3, unwind: bb6]</td></tr><tr><td align="left" balign="left">bb1: FalseUnwind<br align="left"/>bb2: Call</td></tr><tr><td align="left" balign="left">bb3: SwitchInt</td></tr></table>>];
|
||||
bcb0__Cov_0_3 [shape="none", label=<<table border="0" cellborder="1" cellspacing="0"><tr><td bgcolor="gray" align="center" colspan="1">bcb0</td></tr><tr><td align="left" balign="left"></td></tr><tr><td align="left" balign="left">Counter(bcb0) at 9:1-9:11<br align="left"/> </td></tr><tr><td align="left" balign="left">bb0: Goto</td></tr></table>>];
|
||||
bcb3__Cov_0_3 -> bcb1__Cov_0_3 [label=<>];
|
||||
|
@ -5,7 +5,7 @@
|
||||
let mut _0: bool;
|
||||
|
||||
bb0: {
|
||||
+ Coverage::Counter(1) for /the/src/instrument_coverage.rs:20:1 - 22:2;
|
||||
+ Coverage::Counter(0) for /the/src/instrument_coverage.rs:20:1 - 22:2;
|
||||
_0 = const true;
|
||||
return;
|
||||
}
|
||||
|
@ -8,12 +8,12 @@
|
||||
let mut _3: !;
|
||||
|
||||
bb0: {
|
||||
+ Coverage::Counter(1) for /the/src/instrument_coverage.rs:11:1 - 11:11;
|
||||
+ Coverage::Counter(0) for /the/src/instrument_coverage.rs:11:1 - 11:11;
|
||||
goto -> bb1;
|
||||
}
|
||||
|
||||
bb1: {
|
||||
+ Coverage::Expression(4294967295) = 1 + 2 for /the/src/instrument_coverage.rs:12:5 - 13:17;
|
||||
+ Coverage::Expression(0) = Counter(0) + Counter(1) for /the/src/instrument_coverage.rs:12:5 - 13:17;
|
||||
falseUnwind -> [real: bb2, unwind: bb6];
|
||||
}
|
||||
|
||||
@ -27,15 +27,15 @@
|
||||
}
|
||||
|
||||
bb4: {
|
||||
+ Coverage::Expression(4294967293) = 4294967294 + 0 for /the/src/instrument_coverage.rs:17:1 - 17:2;
|
||||
+ Coverage::Expression(4294967294) = 4294967295 - 2 for /the/src/instrument_coverage.rs:14:13 - 14:18;
|
||||
+ Coverage::Expression(2) = Expression(1) + Zero for /the/src/instrument_coverage.rs:17:1 - 17:2;
|
||||
+ Coverage::Expression(1) = Expression(0) - Counter(1) for /the/src/instrument_coverage.rs:14:13 - 14:18;
|
||||
_0 = const ();
|
||||
StorageDead(_2);
|
||||
return;
|
||||
}
|
||||
|
||||
bb5: {
|
||||
+ Coverage::Counter(2) for /the/src/instrument_coverage.rs:15:10 - 15:11;
|
||||
+ Coverage::Counter(1) for /the/src/instrument_coverage.rs:15:10 - 15:11;
|
||||
_1 = const ();
|
||||
StorageDead(_2);
|
||||
goto -> bb1;
|
||||
|
27
tests/ui/binop/borrow-suggestion-109352-2.rs
Normal file
27
tests/ui/binop/borrow-suggestion-109352-2.rs
Normal file
@ -0,0 +1,27 @@
|
||||
struct Bar;
|
||||
|
||||
impl std::ops::Mul for &mut Bar {
|
||||
type Output = Bar;
|
||||
|
||||
fn mul(self, _rhs: Self) -> Self::Output {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let ref_mut_bar: &mut Bar = &mut Bar;
|
||||
let ref_bar: &Bar = &Bar;
|
||||
let owned_bar: Bar = Bar;
|
||||
|
||||
let _ = ref_mut_bar * ref_mut_bar;
|
||||
|
||||
// FIXME: we should be able to suggest borrowing both side
|
||||
let _ = owned_bar * owned_bar;
|
||||
//~^ ERROR cannot multiply
|
||||
let _ = ref_bar * ref_bar;
|
||||
//~^ ERROR cannot multiply
|
||||
let _ = ref_bar * ref_mut_bar;
|
||||
//~^ ERROR cannot multiply
|
||||
let _ = ref_mut_bar * ref_bar;
|
||||
//~^ ERROR mismatched types
|
||||
}
|
64
tests/ui/binop/borrow-suggestion-109352-2.stderr
Normal file
64
tests/ui/binop/borrow-suggestion-109352-2.stderr
Normal file
@ -0,0 +1,64 @@
|
||||
error[E0369]: cannot multiply `Bar` by `Bar`
|
||||
--> $DIR/borrow-suggestion-109352-2.rs:19:23
|
||||
|
|
||||
LL | let _ = owned_bar * owned_bar;
|
||||
| --------- ^ --------- Bar
|
||||
| |
|
||||
| Bar
|
||||
|
|
||||
note: an implementation of `Mul` might be missing for `Bar`
|
||||
--> $DIR/borrow-suggestion-109352-2.rs:1:1
|
||||
|
|
||||
LL | struct Bar;
|
||||
| ^^^^^^^^^^ must implement `Mul`
|
||||
note: the trait `Mul` must be implemented
|
||||
--> $SRC_DIR/core/src/ops/arith.rs:LL:COL
|
||||
|
||||
error[E0369]: cannot multiply `&Bar` by `&Bar`
|
||||
--> $DIR/borrow-suggestion-109352-2.rs:21:21
|
||||
|
|
||||
LL | let _ = ref_bar * ref_bar;
|
||||
| ------- ^ ------- &Bar
|
||||
| |
|
||||
| &Bar
|
||||
|
|
||||
= note: an implementation for `&mut Bar * &mut Bar` exists
|
||||
help: consider making this expression a mutable borrow
|
||||
--> $DIR/borrow-suggestion-109352-2.rs:21:13
|
||||
|
|
||||
LL | let _ = ref_bar * ref_bar;
|
||||
| ^^^^^^^
|
||||
help: consider making this expression a mutable borrow
|
||||
--> $DIR/borrow-suggestion-109352-2.rs:21:23
|
||||
|
|
||||
LL | let _ = ref_bar * ref_bar;
|
||||
| ^^^^^^^
|
||||
|
||||
error[E0369]: cannot multiply `&Bar` by `&mut Bar`
|
||||
--> $DIR/borrow-suggestion-109352-2.rs:23:21
|
||||
|
|
||||
LL | let _ = ref_bar * ref_mut_bar;
|
||||
| ------- ^ ----------- &mut Bar
|
||||
| |
|
||||
| &Bar
|
||||
|
|
||||
= note: an implementation for `&mut Bar * &mut Bar` exists
|
||||
help: consider making this expression a mutable borrow
|
||||
--> $DIR/borrow-suggestion-109352-2.rs:23:13
|
||||
|
|
||||
LL | let _ = ref_bar * ref_mut_bar;
|
||||
| ^^^^^^^
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/borrow-suggestion-109352-2.rs:25:27
|
||||
|
|
||||
LL | let _ = ref_mut_bar * ref_bar;
|
||||
| ^^^^^^^ types differ in mutability
|
||||
|
|
||||
= note: expected mutable reference `&mut Bar`
|
||||
found reference `&Bar`
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
Some errors have detailed explanations: E0308, E0369.
|
||||
For more information about an error, try `rustc --explain E0308`.
|
27
tests/ui/binop/borrow-suggestion-109352.fixed
Normal file
27
tests/ui/binop/borrow-suggestion-109352.fixed
Normal file
@ -0,0 +1,27 @@
|
||||
// run-rustfix
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl std::ops::Mul for &Foo {
|
||||
type Output = Foo;
|
||||
|
||||
fn mul(self, _rhs: Self) -> Self::Output {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let ref_mut_foo: &mut Foo = &mut Foo;
|
||||
let ref_foo: &Foo = &Foo;
|
||||
let owned_foo: Foo = Foo;
|
||||
|
||||
let _ = ref_foo * ref_foo;
|
||||
let _ = ref_foo * ref_mut_foo;
|
||||
|
||||
let _ = &*ref_mut_foo * ref_foo;
|
||||
//~^ ERROR cannot multiply
|
||||
let _ = &*ref_mut_foo * &*ref_mut_foo;
|
||||
//~^ ERROR cannot multiply
|
||||
let _ = &*ref_mut_foo * &owned_foo;
|
||||
//~^ ERROR cannot multiply
|
||||
}
|
27
tests/ui/binop/borrow-suggestion-109352.rs
Normal file
27
tests/ui/binop/borrow-suggestion-109352.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// run-rustfix
|
||||
|
||||
struct Foo;
|
||||
|
||||
impl std::ops::Mul for &Foo {
|
||||
type Output = Foo;
|
||||
|
||||
fn mul(self, _rhs: Self) -> Self::Output {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let ref_mut_foo: &mut Foo = &mut Foo;
|
||||
let ref_foo: &Foo = &Foo;
|
||||
let owned_foo: Foo = Foo;
|
||||
|
||||
let _ = ref_foo * ref_foo;
|
||||
let _ = ref_foo * ref_mut_foo;
|
||||
|
||||
let _ = ref_mut_foo * ref_foo;
|
||||
//~^ ERROR cannot multiply
|
||||
let _ = ref_mut_foo * ref_mut_foo;
|
||||
//~^ ERROR cannot multiply
|
||||
let _ = ref_mut_foo * &owned_foo;
|
||||
//~^ ERROR cannot multiply
|
||||
}
|
45
tests/ui/binop/borrow-suggestion-109352.stderr
Normal file
45
tests/ui/binop/borrow-suggestion-109352.stderr
Normal file
@ -0,0 +1,45 @@
|
||||
error[E0369]: cannot multiply `&mut Foo` by `&Foo`
|
||||
--> $DIR/borrow-suggestion-109352.rs:21:25
|
||||
|
|
||||
LL | let _ = ref_mut_foo * ref_foo;
|
||||
| ----------- ^ ------- &Foo
|
||||
| |
|
||||
| &mut Foo
|
||||
|
|
||||
= note: an implementation for `&Foo * &Foo` exists
|
||||
help: consider reborrowing this side
|
||||
|
|
||||
LL | let _ = &*ref_mut_foo * ref_foo;
|
||||
| ++
|
||||
|
||||
error[E0369]: cannot multiply `&mut Foo` by `&mut Foo`
|
||||
--> $DIR/borrow-suggestion-109352.rs:23:25
|
||||
|
|
||||
LL | let _ = ref_mut_foo * ref_mut_foo;
|
||||
| ----------- ^ ----------- &mut Foo
|
||||
| |
|
||||
| &mut Foo
|
||||
|
|
||||
= note: an implementation for `&Foo * &Foo` exists
|
||||
help: consider reborrowing both sides
|
||||
|
|
||||
LL | let _ = &*ref_mut_foo * &*ref_mut_foo;
|
||||
| ++ ++
|
||||
|
||||
error[E0369]: cannot multiply `&mut Foo` by `&Foo`
|
||||
--> $DIR/borrow-suggestion-109352.rs:25:25
|
||||
|
|
||||
LL | let _ = ref_mut_foo * &owned_foo;
|
||||
| ----------- ^ ---------- &Foo
|
||||
| |
|
||||
| &mut Foo
|
||||
|
|
||||
= note: an implementation for `&Foo * &Foo` exists
|
||||
help: consider reborrowing this side
|
||||
|
|
||||
LL | let _ = &*ref_mut_foo * &owned_foo;
|
||||
| ++
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0369`.
|
Loading…
Reference in New Issue
Block a user