commit
5d62040fb6
540 changed files with 7176 additions and 3374 deletions
1
.mailmap
1
.mailmap
|
@ -74,6 +74,7 @@ Benoît Cortier <benoit.cortier@fried-world.eu>
|
|||
Bheesham Persaud <bheesham123@hotmail.com> Bheesham Persaud <bheesham.persaud@live.ca>
|
||||
Björn Steinbrink <bsteinbr@gmail.com> <B.Steinbrink@gmx.de>
|
||||
blake2-ppc <ulrik.sverdrup@gmail.com> <blake2-ppc>
|
||||
blyxyas <blyxyas@gmail.com> Alejandra González <blyxyas@gmail.com>
|
||||
boolean_coercion <booleancoercion@gmail.com>
|
||||
Boris Egorov <jightuse@gmail.com> <egorov@linux.com>
|
||||
bors <bors@rust-lang.org> bors[bot] <26634292+bors[bot]@users.noreply.github.com>
|
||||
|
|
12
Cargo.lock
12
Cargo.lock
|
@ -697,6 +697,7 @@ dependencies = [
|
|||
"getopts",
|
||||
"glob",
|
||||
"home",
|
||||
"indexmap 2.0.0",
|
||||
"lazycell",
|
||||
"libc",
|
||||
"miow",
|
||||
|
@ -3418,6 +3419,7 @@ dependencies = [
|
|||
"rustc_macros",
|
||||
"rustc_serialize",
|
||||
"rustc_span",
|
||||
"rustc_type_ir",
|
||||
"smallvec",
|
||||
"thin-vec",
|
||||
"tracing",
|
||||
|
@ -3838,7 +3840,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustc_fluent_macro"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"annotate-snippets",
|
||||
"fluent-bundle",
|
||||
|
@ -3914,7 +3916,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustc_hir_typeck"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"rustc_ast",
|
||||
"rustc_attr",
|
||||
|
@ -4042,7 +4044,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustc_lexer"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"expect-test",
|
||||
"unicode-properties",
|
||||
|
@ -4111,7 +4113,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustc_macros"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -4595,7 +4597,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustc_transmute"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"itertools",
|
||||
"rustc_data_structures",
|
||||
|
|
|
@ -14,6 +14,8 @@ rustc_lexer = { path = "../rustc_lexer" }
|
|||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_serialize = { path = "../rustc_serialize" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
# depends on Mutability and Movability, which could be uplifted into a common crate.
|
||||
rustc_type_ir = { path = "../rustc_type_ir" }
|
||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
||||
thin-vec = "0.2.12"
|
||||
tracing = "0.1"
|
||||
|
|
|
@ -34,6 +34,7 @@ use rustc_serialize::{Decodable, Decoder, Encodable, Encoder};
|
|||
use rustc_span::source_map::{respan, Spanned};
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{ErrorGuaranteed, Span, DUMMY_SP};
|
||||
pub use rustc_type_ir::{Movability, Mutability};
|
||||
use std::fmt;
|
||||
use std::mem;
|
||||
use thin_vec::{thin_vec, ThinVec};
|
||||
|
@ -800,57 +801,6 @@ pub enum PatKind {
|
|||
MacCall(P<MacCall>),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Copy)]
|
||||
#[derive(HashStable_Generic, Encodable, Decodable)]
|
||||
pub enum Mutability {
|
||||
// N.B. Order is deliberate, so that Not < Mut
|
||||
Not,
|
||||
Mut,
|
||||
}
|
||||
|
||||
impl Mutability {
|
||||
pub fn invert(self) -> Self {
|
||||
match self {
|
||||
Mutability::Mut => Mutability::Not,
|
||||
Mutability::Not => Mutability::Mut,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `""` (empty string) or `"mut "` depending on the mutability.
|
||||
pub fn prefix_str(self) -> &'static str {
|
||||
match self {
|
||||
Mutability::Mut => "mut ",
|
||||
Mutability::Not => "",
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `"&"` or `"&mut "` depending on the mutability.
|
||||
pub fn ref_prefix_str(self) -> &'static str {
|
||||
match self {
|
||||
Mutability::Not => "&",
|
||||
Mutability::Mut => "&mut ",
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `""` (empty string) or `"mutably "` depending on the mutability.
|
||||
pub fn mutably_str(self) -> &'static str {
|
||||
match self {
|
||||
Mutability::Not => "",
|
||||
Mutability::Mut => "mutably ",
|
||||
}
|
||||
}
|
||||
|
||||
/// Return `true` if self is mutable
|
||||
pub fn is_mut(self) -> bool {
|
||||
matches!(self, Self::Mut)
|
||||
}
|
||||
|
||||
/// Return `true` if self is **not** mutable
|
||||
pub fn is_not(self) -> bool {
|
||||
matches!(self, Self::Not)
|
||||
}
|
||||
}
|
||||
|
||||
/// The kind of borrow in an `AddrOf` expression,
|
||||
/// e.g., `&place` or `&raw const place`.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
|
@ -1579,17 +1529,6 @@ pub enum CaptureBy {
|
|||
Ref,
|
||||
}
|
||||
|
||||
/// The movability of a generator / closure literal:
|
||||
/// whether a generator contains self-references, causing it to be `!Unpin`.
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable, Debug, Copy)]
|
||||
#[derive(HashStable_Generic)]
|
||||
pub enum Movability {
|
||||
/// May contain self-references, `!Unpin`.
|
||||
Static,
|
||||
/// Must not contain self-references, `Unpin`.
|
||||
Movable,
|
||||
}
|
||||
|
||||
/// Closure lifetime binder, `for<'a, 'b>` in `for<'a, 'b> |_: &'a (), _: &'b ()|`.
|
||||
#[derive(Clone, Encodable, Decodable, Debug)]
|
||||
pub enum ClosureBinder {
|
||||
|
|
|
@ -60,7 +60,9 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
|||
/// Requirements for a `StableHashingContext` to be used in this crate.
|
||||
/// This is a hack to allow using the `HashStable_Generic` derive macro
|
||||
/// instead of implementing everything in `rustc_middle`.
|
||||
pub trait HashStableContext: rustc_span::HashStableContext {
|
||||
pub trait HashStableContext:
|
||||
rustc_type_ir::HashStableContext + rustc_span::HashStableContext
|
||||
{
|
||||
fn hash_attr(&mut self, _: &ast::Attribute, hasher: &mut StableHasher);
|
||||
}
|
||||
|
||||
|
|
|
@ -328,26 +328,19 @@ fn check_opaque_type_well_formed<'tcx>(
|
|||
|
||||
// Require that the hidden type actually fulfills all the bounds of the opaque type, even without
|
||||
// the bounds that the function supplies.
|
||||
let mut obligations = vec![];
|
||||
infcx
|
||||
.insert_hidden_type(
|
||||
OpaqueTypeKey { def_id, args: identity_args },
|
||||
&ObligationCause::misc(definition_span, def_id),
|
||||
param_env,
|
||||
definition_ty,
|
||||
true,
|
||||
&mut obligations,
|
||||
)
|
||||
.unwrap();
|
||||
infcx.add_item_bounds_for_hidden_type(
|
||||
def_id.to_def_id(),
|
||||
identity_args,
|
||||
ObligationCause::misc(definition_span, def_id),
|
||||
param_env,
|
||||
definition_ty,
|
||||
&mut obligations,
|
||||
);
|
||||
ocx.register_obligations(obligations);
|
||||
let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), identity_args);
|
||||
ocx.eq(&ObligationCause::misc(definition_span, def_id), param_env, opaque_ty, definition_ty)
|
||||
.map_err(|err| {
|
||||
infcx
|
||||
.err_ctxt()
|
||||
.report_mismatched_types(
|
||||
&ObligationCause::misc(definition_span, def_id),
|
||||
opaque_ty,
|
||||
definition_ty,
|
||||
err,
|
||||
)
|
||||
.emit()
|
||||
})?;
|
||||
|
||||
// Require the hidden type to be well-formed with only the generics of the opaque type.
|
||||
// Defining use functions may have more bounds than the opaque type, which is ok, as long as the
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use rustc_middle::mir::coverage::{CounterId, ExpressionId, Operand};
|
||||
use rustc_middle::mir::coverage::{CounterId, CovTerm, ExpressionId};
|
||||
|
||||
/// Must match the layout of `LLVMRustCounterKind`.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -43,11 +43,11 @@ impl Counter {
|
|||
Self { kind: CounterKind::Expression, id: expression_id.as_u32() }
|
||||
}
|
||||
|
||||
pub(crate) fn from_operand(operand: Operand) -> Self {
|
||||
match operand {
|
||||
Operand::Zero => Self::ZERO,
|
||||
Operand::Counter(id) => Self::counter_value_reference(id),
|
||||
Operand::Expression(id) => Self::expression(id),
|
||||
pub(crate) fn from_term(term: CovTerm) -> Self {
|
||||
match term {
|
||||
CovTerm::Zero => Self::ZERO,
|
||||
CovTerm::Counter(id) => Self::counter_value_reference(id),
|
||||
CovTerm::Expression(id) => Self::expression(id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,17 +73,6 @@ pub struct CounterExpression {
|
|||
pub rhs: Counter,
|
||||
}
|
||||
|
||||
impl CounterExpression {
|
||||
/// The dummy expression `(0 - 0)` has a representation of all zeroes,
|
||||
/// making it marginally more efficient to initialize than `(0 + 0)`.
|
||||
pub(crate) const DUMMY: Self =
|
||||
Self { lhs: Counter::ZERO, kind: ExprKind::Subtract, rhs: Counter::ZERO };
|
||||
|
||||
pub fn new(lhs: Counter, kind: ExprKind, rhs: Counter) -> Self {
|
||||
Self { kind, lhs, rhs }
|
||||
}
|
||||
}
|
||||
|
||||
/// Corresponds to enum `llvm::coverage::CounterMappingRegion::RegionKind`.
|
||||
///
|
||||
/// Must match the layout of `LLVMRustCounterMappingRegionKind`.
|
||||
|
|
|
@ -1,64 +1,78 @@
|
|||
use crate::coverageinfo::ffi::{Counter, CounterExpression, ExprKind};
|
||||
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::coverage::{CodeRegion, CounterId, ExpressionId, Op, Operand};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::mir::coverage::{
|
||||
CodeRegion, CounterId, CovTerm, Expression, ExpressionId, FunctionCoverageInfo, Mapping, Op,
|
||||
};
|
||||
use rustc_middle::ty::Instance;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct Expression {
|
||||
lhs: Operand,
|
||||
op: Op,
|
||||
rhs: Operand,
|
||||
code_regions: Vec<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. 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
|
||||
/// regions" (or "gap areas"). A gap region is a code region within a counted region (either counter
|
||||
/// or expression), but the line or lines in the gap region are not executable (such as lines with
|
||||
/// only whitespace or comments). According to LLVM Code Coverage Mapping documentation, "A count
|
||||
/// for a gap area is only used as the line execution count if there are no other regions on a
|
||||
/// line."
|
||||
/// Holds all of the coverage mapping data associated with a function instance,
|
||||
/// collected during traversal of `Coverage` statements in the function's MIR.
|
||||
#[derive(Debug)]
|
||||
pub struct FunctionCoverage<'tcx> {
|
||||
instance: Instance<'tcx>,
|
||||
source_hash: u64,
|
||||
/// Coverage info that was attached to this function by the instrumentor.
|
||||
function_coverage_info: &'tcx FunctionCoverageInfo,
|
||||
is_used: bool,
|
||||
counters: IndexVec<CounterId, Option<Vec<CodeRegion>>>,
|
||||
expressions: IndexVec<ExpressionId, Option<Expression>>,
|
||||
unreachable_regions: Vec<CodeRegion>,
|
||||
|
||||
/// Tracks which counters have been seen, so that we can identify mappings
|
||||
/// to counters that were optimized out, and set them to zero.
|
||||
counters_seen: BitSet<CounterId>,
|
||||
/// Contains all expression IDs that have been seen in an `ExpressionUsed`
|
||||
/// coverage statement, plus all expression IDs that aren't directly used
|
||||
/// by any mappings (and therefore do not have expression-used statements).
|
||||
/// After MIR traversal is finished, we can conclude that any IDs missing
|
||||
/// from this set must have had their statements deleted by MIR opts.
|
||||
expressions_seen: BitSet<ExpressionId>,
|
||||
}
|
||||
|
||||
impl<'tcx> FunctionCoverage<'tcx> {
|
||||
/// Creates a new set of coverage data for a used (called) function.
|
||||
pub fn new(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
|
||||
Self::create(tcx, instance, true)
|
||||
pub fn new(
|
||||
instance: Instance<'tcx>,
|
||||
function_coverage_info: &'tcx FunctionCoverageInfo,
|
||||
) -> Self {
|
||||
Self::create(instance, function_coverage_info, true)
|
||||
}
|
||||
|
||||
/// Creates a new set of coverage data for an unused (never called) function.
|
||||
pub fn unused(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> Self {
|
||||
Self::create(tcx, instance, false)
|
||||
pub fn unused(
|
||||
instance: Instance<'tcx>,
|
||||
function_coverage_info: &'tcx FunctionCoverageInfo,
|
||||
) -> Self {
|
||||
Self::create(instance, function_coverage_info, false)
|
||||
}
|
||||
|
||||
fn create(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, is_used: bool) -> Self {
|
||||
let coverageinfo = tcx.coverageinfo(instance.def);
|
||||
fn create(
|
||||
instance: Instance<'tcx>,
|
||||
function_coverage_info: &'tcx FunctionCoverageInfo,
|
||||
is_used: bool,
|
||||
) -> Self {
|
||||
let num_counters = function_coverage_info.num_counters;
|
||||
let num_expressions = function_coverage_info.expressions.len();
|
||||
debug!(
|
||||
"FunctionCoverage::create(instance={:?}) has coverageinfo={:?}. is_used={}",
|
||||
instance, coverageinfo, is_used
|
||||
"FunctionCoverage::create(instance={instance:?}) has \
|
||||
num_counters={num_counters}, num_expressions={num_expressions}, is_used={is_used}"
|
||||
);
|
||||
|
||||
// Create a filled set of expression IDs, so that expressions not
|
||||
// directly used by mappings will be treated as "seen".
|
||||
// (If they end up being unused, LLVM will delete them for us.)
|
||||
let mut expressions_seen = BitSet::new_filled(num_expressions);
|
||||
// For each expression ID that is directly used by one or more mappings,
|
||||
// mark it as not-yet-seen. This indicates that we expect to see a
|
||||
// corresponding `ExpressionUsed` statement during MIR traversal.
|
||||
for Mapping { term, .. } in &function_coverage_info.mappings {
|
||||
if let &CovTerm::Expression(id) = term {
|
||||
expressions_seen.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
instance,
|
||||
source_hash: 0, // will be set with the first `add_counter()`
|
||||
function_coverage_info,
|
||||
is_used,
|
||||
counters: IndexVec::from_elem_n(None, coverageinfo.num_counters as usize),
|
||||
expressions: IndexVec::from_elem_n(None, coverageinfo.num_expressions as usize),
|
||||
unreachable_regions: Vec::new(),
|
||||
counters_seen: BitSet::new_empty(num_counters),
|
||||
expressions_seen,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,135 +81,94 @@ impl<'tcx> FunctionCoverage<'tcx> {
|
|||
self.is_used
|
||||
}
|
||||
|
||||
/// Sets the function source hash value. If called multiple times for the same function, all
|
||||
/// calls should have the same hash value.
|
||||
pub fn set_function_source_hash(&mut self, source_hash: u64) {
|
||||
if self.source_hash == 0 {
|
||||
self.source_hash = source_hash;
|
||||
} else {
|
||||
debug_assert_eq!(source_hash, self.source_hash);
|
||||
}
|
||||
}
|
||||
|
||||
/// Adds code regions to be counted by an injected counter intrinsic.
|
||||
/// Marks a counter ID as having been seen in a counter-increment statement.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn add_counter(&mut self, id: CounterId, code_regions: &[CodeRegion]) {
|
||||
if code_regions.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let slot = &mut self.counters[id];
|
||||
match slot {
|
||||
None => *slot = Some(code_regions.to_owned()),
|
||||
// If this counter ID slot has already been filled, it should
|
||||
// contain identical information.
|
||||
Some(ref previous_regions) => assert_eq!(
|
||||
previous_regions, code_regions,
|
||||
"add_counter: code regions for id changed"
|
||||
),
|
||||
}
|
||||
pub(crate) fn mark_counter_id_seen(&mut self, id: CounterId) {
|
||||
self.counters_seen.insert(id);
|
||||
}
|
||||
|
||||
/// Adds information about a coverage expression, along with zero or more
|
||||
/// code regions mapped to that expression.
|
||||
///
|
||||
/// Both counters and "counter expressions" (or simply, "expressions") can be operands in other
|
||||
/// 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.
|
||||
/// Marks an expression ID as having been seen in an expression-used statement.
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn add_counter_expression(
|
||||
&mut self,
|
||||
expression_id: ExpressionId,
|
||||
lhs: Operand,
|
||||
op: Op,
|
||||
rhs: Operand,
|
||||
code_regions: &[CodeRegion],
|
||||
) {
|
||||
debug_assert!(
|
||||
expression_id.as_usize() < self.expressions.len(),
|
||||
"expression_id {} is out of range for expressions.len() = {}
|
||||
for {:?}",
|
||||
expression_id.as_usize(),
|
||||
self.expressions.len(),
|
||||
self,
|
||||
);
|
||||
|
||||
let expression = Expression { lhs, op, rhs, code_regions: code_regions.to_owned() };
|
||||
let slot = &mut self.expressions[expression_id];
|
||||
match slot {
|
||||
None => *slot = Some(expression),
|
||||
// If this expression ID slot has already been filled, it should
|
||||
// contain identical information.
|
||||
Some(ref previous_expression) => assert_eq!(
|
||||
previous_expression, &expression,
|
||||
"add_counter_expression: expression for id changed"
|
||||
),
|
||||
}
|
||||
pub(crate) fn mark_expression_id_seen(&mut self, id: ExpressionId) {
|
||||
self.expressions_seen.insert(id);
|
||||
}
|
||||
|
||||
/// Adds regions that will be marked as "unreachable", with a constant "zero counter".
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
pub(crate) fn add_unreachable_regions(&mut self, code_regions: &[CodeRegion]) {
|
||||
assert!(!code_regions.is_empty(), "unreachable regions always have code regions");
|
||||
self.unreachable_regions.extend_from_slice(code_regions);
|
||||
}
|
||||
|
||||
/// Perform some simplifications to make the final coverage mappings
|
||||
/// slightly smaller.
|
||||
/// Identify expressions that will always have a value of zero, and note
|
||||
/// their IDs in [`ZeroExpressions`]. Mappings that refer to a zero expression
|
||||
/// can instead become mappings to a constant zero value.
|
||||
///
|
||||
/// This method mainly exists to preserve the simplifications that were
|
||||
/// already being performed by the Rust-side expression renumbering, so that
|
||||
/// the resulting coverage mappings don't get worse.
|
||||
pub(crate) fn simplify_expressions(&mut self) {
|
||||
fn identify_zero_expressions(&self) -> ZeroExpressions {
|
||||
// The set of expressions that either were optimized out entirely, or
|
||||
// have zero as both of their operands, and will therefore always have
|
||||
// a value of zero. Other expressions that refer to these as operands
|
||||
// can have those operands replaced with `Operand::Zero`.
|
||||
// can have those operands replaced with `CovTerm::Zero`.
|
||||
let mut zero_expressions = FxIndexSet::default();
|
||||
|
||||
// For each expression, perform simplifications based on lower-numbered
|
||||
// expressions, and then update the set of always-zero expressions if
|
||||
// necessary.
|
||||
// Simplify a copy of each expression based on lower-numbered expressions,
|
||||
// and then update the set of always-zero expressions if necessary.
|
||||
// (By construction, expressions can only refer to other expressions
|
||||
// that have lower IDs, so one simplification pass is sufficient.)
|
||||
for (id, maybe_expression) in self.expressions.iter_enumerated_mut() {
|
||||
let Some(expression) = maybe_expression else {
|
||||
// If an expression is missing, it must have been optimized away,
|
||||
// that have lower IDs, so one pass is sufficient.)
|
||||
for (id, expression) in self.function_coverage_info.expressions.iter_enumerated() {
|
||||
if !self.expressions_seen.contains(id) {
|
||||
// If an expression was not seen, it must have been optimized away,
|
||||
// so any operand that refers to it can be replaced with zero.
|
||||
zero_expressions.insert(id);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We don't need to simplify the actual expression data in the
|
||||
// expressions list; we can just simplify a temporary copy and then
|
||||
// use that to update the set of always-zero expressions.
|
||||
let Expression { mut lhs, op, mut rhs } = *expression;
|
||||
|
||||
// If an expression has an operand that is also an expression, the
|
||||
// operand's ID must be strictly lower. This is what lets us find
|
||||
// all zero expressions in one pass.
|
||||
let assert_operand_expression_is_lower = |operand_id: ExpressionId| {
|
||||
assert!(
|
||||
operand_id < id,
|
||||
"Operand {operand_id:?} should be less than {id:?} in {expression:?}",
|
||||
)
|
||||
};
|
||||
|
||||
// If an operand refers to an expression that is always zero, then
|
||||
// that operand can be replaced with `Operand::Zero`.
|
||||
let maybe_set_operand_to_zero = |operand: &mut Operand| match &*operand {
|
||||
Operand::Expression(id) if zero_expressions.contains(id) => {
|
||||
*operand = Operand::Zero;
|
||||
// that operand can be replaced with `CovTerm::Zero`.
|
||||
let maybe_set_operand_to_zero = |operand: &mut CovTerm| match *operand {
|
||||
CovTerm::Expression(id) => {
|
||||
assert_operand_expression_is_lower(id);
|
||||
if zero_expressions.contains(&id) {
|
||||
*operand = CovTerm::Zero;
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
maybe_set_operand_to_zero(&mut expression.lhs);
|
||||
maybe_set_operand_to_zero(&mut expression.rhs);
|
||||
maybe_set_operand_to_zero(&mut lhs);
|
||||
maybe_set_operand_to_zero(&mut rhs);
|
||||
|
||||
// Coverage counter values cannot be negative, so if an expression
|
||||
// involves subtraction from zero, assume that its RHS must also be zero.
|
||||
// (Do this after simplifications that could set the LHS to zero.)
|
||||
if let Expression { lhs: Operand::Zero, op: Op::Subtract, .. } = expression {
|
||||
expression.rhs = Operand::Zero;
|
||||
if lhs == CovTerm::Zero && op == Op::Subtract {
|
||||
rhs = CovTerm::Zero;
|
||||
}
|
||||
|
||||
// After the above simplifications, if both operands are zero, then
|
||||
// we know that this expression is always zero too.
|
||||
if let Expression { lhs: Operand::Zero, rhs: Operand::Zero, .. } = expression {
|
||||
if lhs == CovTerm::Zero && rhs == CovTerm::Zero {
|
||||
zero_expressions.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
ZeroExpressions(zero_expressions)
|
||||
}
|
||||
|
||||
/// Return the source hash, generated from the HIR node structure, and used to indicate whether
|
||||
/// or not the source code structure changed between different compilations.
|
||||
pub fn source_hash(&self) -> u64 {
|
||||
self.source_hash
|
||||
if self.is_used { self.function_coverage_info.function_source_hash } else { 0 }
|
||||
}
|
||||
|
||||
/// Generate an array of CounterExpressions, and an iterator over all `Counter`s and their
|
||||
|
@ -204,91 +177,80 @@ impl<'tcx> FunctionCoverage<'tcx> {
|
|||
pub fn get_expressions_and_counter_regions(
|
||||
&self,
|
||||
) -> (Vec<CounterExpression>, impl Iterator<Item = (Counter, &CodeRegion)>) {
|
||||
assert!(
|
||||
self.source_hash != 0 || !self.is_used,
|
||||
"No counters provided the source_hash for used function: {:?}",
|
||||
self.instance
|
||||
);
|
||||
let zero_expressions = self.identify_zero_expressions();
|
||||
|
||||
let counter_expressions = self.counter_expressions();
|
||||
let counter_expressions = self.counter_expressions(&zero_expressions);
|
||||
// Expression IDs are indices into `self.expressions`, and on the LLVM
|
||||
// side they will be treated as indices into `counter_expressions`, so
|
||||
// the two vectors should correspond 1:1.
|
||||
assert_eq!(self.expressions.len(), counter_expressions.len());
|
||||
assert_eq!(self.function_coverage_info.expressions.len(), counter_expressions.len());
|
||||
|
||||
let counter_regions = self.counter_regions();
|
||||
let expression_regions = self.expression_regions();
|
||||
let unreachable_regions = self.unreachable_regions();
|
||||
let counter_regions = self.counter_regions(zero_expressions);
|
||||
|
||||
let counter_regions =
|
||||
counter_regions.chain(expression_regions.into_iter().chain(unreachable_regions));
|
||||
(counter_expressions, counter_regions)
|
||||
}
|
||||
|
||||
fn counter_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> {
|
||||
self.counters
|
||||
.iter_enumerated()
|
||||
// Filter out counter IDs that we never saw during MIR traversal.
|
||||
// This can happen if a counter was optimized out by MIR transforms
|
||||
// (and replaced with `CoverageKind::Unreachable` instead).
|
||||
.filter_map(|(id, maybe_code_regions)| Some((id, maybe_code_regions.as_ref()?)))
|
||||
.flat_map(|(id, code_regions)| {
|
||||
let counter = Counter::counter_value_reference(id);
|
||||
code_regions.iter().map(move |region| (counter, region))
|
||||
})
|
||||
}
|
||||
|
||||
/// Convert this function's coverage expression data into a form that can be
|
||||
/// passed through FFI to LLVM.
|
||||
fn counter_expressions(&self) -> Vec<CounterExpression> {
|
||||
fn counter_expressions(&self, zero_expressions: &ZeroExpressions) -> Vec<CounterExpression> {
|
||||
// We know that LLVM will optimize out any unused expressions before
|
||||
// producing the final coverage map, so there's no need to do the same
|
||||
// thing on the Rust side unless we're confident we can do much better.
|
||||
// (See `CounterExpressionsMinimizer` in `CoverageMappingWriter.cpp`.)
|
||||
|
||||
self.expressions
|
||||
let counter_from_operand = |operand: CovTerm| match operand {
|
||||
CovTerm::Expression(id) if zero_expressions.contains(id) => Counter::ZERO,
|
||||
_ => Counter::from_term(operand),
|
||||
};
|
||||
|
||||
self.function_coverage_info
|
||||
.expressions
|
||||
.iter()
|
||||
.map(|expression| match expression {
|
||||
None => {
|
||||
// This expression ID was allocated, but we never saw the
|
||||
// actual expression, so it must have been optimized out.
|
||||
// Replace it with a dummy expression, and let LLVM take
|
||||
// care of omitting it from the expression list.
|
||||
CounterExpression::DUMMY
|
||||
}
|
||||
&Some(Expression { lhs, op, rhs, .. }) => {
|
||||
// Convert the operands and operator as normal.
|
||||
CounterExpression::new(
|
||||
Counter::from_operand(lhs),
|
||||
match op {
|
||||
Op::Add => ExprKind::Add,
|
||||
Op::Subtract => ExprKind::Subtract,
|
||||
},
|
||||
Counter::from_operand(rhs),
|
||||
)
|
||||
}
|
||||
.map(|&Expression { lhs, op, rhs }| CounterExpression {
|
||||
lhs: counter_from_operand(lhs),
|
||||
kind: match op {
|
||||
Op::Add => ExprKind::Add,
|
||||
Op::Subtract => ExprKind::Subtract,
|
||||
},
|
||||
rhs: counter_from_operand(rhs),
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
|
||||
fn expression_regions(&self) -> Vec<(Counter, &CodeRegion)> {
|
||||
// Find all of the expression IDs that weren't optimized out AND have
|
||||
// one or more attached code regions, and return the corresponding
|
||||
// mappings as counter/region pairs.
|
||||
self.expressions
|
||||
.iter_enumerated()
|
||||
.filter_map(|(id, maybe_expression)| {
|
||||
let code_regions = &maybe_expression.as_ref()?.code_regions;
|
||||
Some((id, code_regions))
|
||||
})
|
||||
.flat_map(|(id, code_regions)| {
|
||||
let counter = Counter::expression(id);
|
||||
code_regions.iter().map(move |code_region| (counter, code_region))
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
/// Converts this function's coverage mappings into an intermediate form
|
||||
/// that will be used by `mapgen` when preparing for FFI.
|
||||
fn counter_regions(
|
||||
&self,
|
||||
zero_expressions: ZeroExpressions,
|
||||
) -> impl Iterator<Item = (Counter, &CodeRegion)> {
|
||||
// Historically, mappings were stored directly in counter/expression
|
||||
// statements in MIR, and MIR optimizations would sometimes remove them.
|
||||
// That's mostly no longer true, so now we detect cases where that would
|
||||
// have happened, and zero out the corresponding mappings here instead.
|
||||
let counter_for_term = move |term: CovTerm| {
|
||||
let force_to_zero = match term {
|
||||
CovTerm::Counter(id) => !self.counters_seen.contains(id),
|
||||
CovTerm::Expression(id) => zero_expressions.contains(id),
|
||||
CovTerm::Zero => false,
|
||||
};
|
||||
if force_to_zero { Counter::ZERO } else { Counter::from_term(term) }
|
||||
};
|
||||
|
||||
fn unreachable_regions(&self) -> impl Iterator<Item = (Counter, &CodeRegion)> {
|
||||
self.unreachable_regions.iter().map(|region| (Counter::ZERO, region))
|
||||
self.function_coverage_info.mappings.iter().map(move |mapping| {
|
||||
let &Mapping { term, ref code_region } = mapping;
|
||||
let counter = counter_for_term(term);
|
||||
(counter, code_region)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Set of expression IDs that are known to always evaluate to zero.
|
||||
/// Any mapping or expression operand that refers to these expressions can have
|
||||
/// that reference replaced with a constant zero value.
|
||||
struct ZeroExpressions(FxIndexSet<ExpressionId>);
|
||||
|
||||
impl ZeroExpressions {
|
||||
fn contains(&self, id: ExpressionId) -> bool {
|
||||
self.0.contains(&id)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,9 +10,8 @@ use rustc_hir::def::DefKind;
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::bug;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::coverage::CodeRegion;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_span::Symbol;
|
||||
|
||||
/// Generates and exports the Coverage Map.
|
||||
|
@ -60,10 +59,8 @@ pub fn finalize(cx: &CodegenCx<'_, '_>) {
|
|||
|
||||
// Encode coverage mappings and generate function records
|
||||
let mut function_data = Vec::new();
|
||||
for (instance, mut function_coverage) in function_coverage_map {
|
||||
for (instance, function_coverage) in function_coverage_map {
|
||||
debug!("Generate function coverage for {}, {:?}", cx.codegen_unit.name(), instance);
|
||||
function_coverage.simplify_expressions();
|
||||
let function_coverage = function_coverage;
|
||||
|
||||
let mangled_function_name = tcx.symbol_name(instance).name;
|
||||
let source_hash = function_coverage.source_hash();
|
||||
|
@ -170,10 +167,11 @@ fn encode_mappings_for_function(
|
|||
let mut virtual_file_mapping = IndexVec::<u32, u32>::new();
|
||||
let mut mapping_regions = Vec::with_capacity(counter_regions.len());
|
||||
|
||||
// Sort the list of (counter, region) mapping pairs by region, so that they
|
||||
// can be grouped by filename. Prepare file IDs for each filename, and
|
||||
// prepare the mapping data so that we can pass it through FFI to LLVM.
|
||||
counter_regions.sort_by_key(|(_counter, region)| *region);
|
||||
// Sort and group the list of (counter, region) mapping pairs by filename.
|
||||
// (Preserve any further ordering imposed by `FunctionCoverage`.)
|
||||
// Prepare file IDs for each filename, and prepare the mapping data so that
|
||||
// we can pass it through FFI to LLVM.
|
||||
counter_regions.sort_by_key(|(_counter, region)| region.file_name);
|
||||
for counter_regions_for_file in
|
||||
counter_regions.group_by(|(_, a), (_, b)| a.file_name == b.file_name)
|
||||
{
|
||||
|
@ -331,16 +329,14 @@ fn add_unused_functions(cx: &CodegenCx<'_, '_>) {
|
|||
for non_codegenned_def_id in
|
||||
eligible_def_ids.into_iter().filter(|id| !codegenned_def_ids.contains(id))
|
||||
{
|
||||
let codegen_fn_attrs = tcx.codegen_fn_attrs(non_codegenned_def_id);
|
||||
|
||||
// If a function is marked `#[coverage(off)]`, then skip generating a
|
||||
// dead code stub for it.
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
|
||||
debug!("skipping unused fn marked #[coverage(off)]: {:?}", non_codegenned_def_id);
|
||||
// Skip any function that didn't have coverage data added to it by the
|
||||
// coverage instrumentor.
|
||||
let body = tcx.instance_mir(ty::InstanceDef::Item(non_codegenned_def_id));
|
||||
let Some(function_coverage_info) = body.function_coverage_info.as_deref() else {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
debug!("generating unused fn: {:?}", non_codegenned_def_id);
|
||||
cx.define_unused_fn(non_codegenned_def_id);
|
||||
cx.define_unused_fn(non_codegenned_def_id, function_coverage_info);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +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::{CounterId, CoverageKind};
|
||||
use rustc_middle::mir::coverage::{CounterId, CoverageKind, FunctionCoverageInfo};
|
||||
use rustc_middle::mir::Coverage;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt};
|
||||
|
@ -88,44 +88,63 @@ impl<'ll, 'tcx> CodegenCx<'ll, 'tcx> {
|
|||
/// For used/called functions, the coverageinfo was already added to the
|
||||
/// `function_coverage_map` (keyed by function `Instance`) during codegen.
|
||||
/// But in this case, since the unused function was _not_ previously
|
||||
/// codegenned, collect the coverage `CodeRegion`s from the MIR and add
|
||||
/// them. Since the function is never called, all of its `CodeRegion`s can be
|
||||
/// added as `unreachable_region`s.
|
||||
fn define_unused_fn(&self, def_id: DefId) {
|
||||
/// codegenned, collect the function coverage info from MIR and add an
|
||||
/// "unused" entry to the function coverage map.
|
||||
fn define_unused_fn(&self, def_id: DefId, function_coverage_info: &'tcx FunctionCoverageInfo) {
|
||||
let instance = declare_unused_fn(self, def_id);
|
||||
codegen_unused_fn_and_counter(self, instance);
|
||||
add_unused_function_coverage(self, instance, def_id);
|
||||
add_unused_function_coverage(self, instance, function_coverage_info);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
fn add_coverage(&mut self, instance: Instance<'tcx>, coverage: &Coverage) {
|
||||
// Our caller should have already taken care of inlining subtleties,
|
||||
// so we can assume that counter/expression IDs in this coverage
|
||||
// statement are meaningful for the given instance.
|
||||
//
|
||||
// (Either the statement was not inlined and directly belongs to this
|
||||
// instance, or it was inlined *from* this instance.)
|
||||
|
||||
let bx = self;
|
||||
|
||||
let Some(function_coverage_info) =
|
||||
bx.tcx.instance_mir(instance.def).function_coverage_info.as_deref()
|
||||
else {
|
||||
debug!("function has a coverage statement but no coverage info");
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(coverage_context) = bx.coverage_context() else { return };
|
||||
let mut coverage_map = coverage_context.function_coverage_map.borrow_mut();
|
||||
let func_coverage = coverage_map
|
||||
.entry(instance)
|
||||
.or_insert_with(|| FunctionCoverage::new(bx.tcx(), instance));
|
||||
.or_insert_with(|| FunctionCoverage::new(instance, function_coverage_info));
|
||||
|
||||
let Coverage { kind, code_regions } = coverage;
|
||||
let Coverage { kind } = coverage;
|
||||
match *kind {
|
||||
CoverageKind::Counter { function_source_hash, id } => {
|
||||
debug!(
|
||||
"ensuring function source hash is set for instance={:?}; function_source_hash={}",
|
||||
instance, function_source_hash,
|
||||
);
|
||||
func_coverage.set_function_source_hash(function_source_hash);
|
||||
func_coverage.add_counter(id, code_regions);
|
||||
CoverageKind::CounterIncrement { id } => {
|
||||
func_coverage.mark_counter_id_seen(id);
|
||||
// We need to explicitly drop the `RefMut` before calling into `instrprof_increment`,
|
||||
// as that needs an exclusive borrow.
|
||||
drop(coverage_map);
|
||||
|
||||
let coverageinfo = bx.tcx().coverageinfo(instance.def);
|
||||
// The number of counters passed to `llvm.instrprof.increment` might
|
||||
// be smaller than the number originally inserted by the instrumentor,
|
||||
// if some high-numbered counters were removed by MIR optimizations.
|
||||
// If so, LLVM's profiler runtime will use fewer physical counters.
|
||||
let num_counters =
|
||||
bx.tcx().coverage_ids_info(instance.def).max_counter_id.as_u32() + 1;
|
||||
assert!(
|
||||
num_counters as usize <= function_coverage_info.num_counters,
|
||||
"num_counters disagreement: query says {num_counters} but function info only has {}",
|
||||
function_coverage_info.num_counters
|
||||
);
|
||||
|
||||
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 hash = bx.const_u64(function_coverage_info.function_source_hash);
|
||||
let num_counters = bx.const_u32(num_counters);
|
||||
let index = bx.const_u32(id.as_u32());
|
||||
debug!(
|
||||
"codegen intrinsic instrprof.increment(fn_name={:?}, hash={:?}, num_counters={:?}, index={:?})",
|
||||
|
@ -133,11 +152,8 @@ impl<'tcx> CoverageInfoBuilderMethods<'tcx> for Builder<'_, '_, 'tcx> {
|
|||
);
|
||||
bx.instrprof_increment(fn_name, hash, num_counters, index);
|
||||
}
|
||||
CoverageKind::Expression { id, lhs, op, rhs } => {
|
||||
func_coverage.add_counter_expression(id, lhs, op, rhs, code_regions);
|
||||
}
|
||||
CoverageKind::Unreachable => {
|
||||
func_coverage.add_unreachable_regions(code_regions);
|
||||
CoverageKind::ExpressionUsed { id } => {
|
||||
func_coverage.mark_expression_id_seen(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,15 +216,11 @@ fn codegen_unused_fn_and_counter<'tcx>(cx: &CodegenCx<'_, 'tcx>, instance: Insta
|
|||
fn add_unused_function_coverage<'tcx>(
|
||||
cx: &CodegenCx<'_, 'tcx>,
|
||||
instance: Instance<'tcx>,
|
||||
def_id: DefId,
|
||||
function_coverage_info: &'tcx FunctionCoverageInfo,
|
||||
) {
|
||||
let tcx = cx.tcx;
|
||||
|
||||
let mut function_coverage = FunctionCoverage::unused(tcx, instance);
|
||||
for &code_region in tcx.covered_code_regions(def_id) {
|
||||
let code_region = std::slice::from_ref(code_region);
|
||||
function_coverage.add_unreachable_regions(code_region);
|
||||
}
|
||||
// An unused function's mappings will automatically be rewritten to map to
|
||||
// zero, because none of its counters/expressions are marked as seen.
|
||||
let function_coverage = FunctionCoverage::unused(instance, function_coverage_info);
|
||||
|
||||
if let Some(coverage_context) = cx.coverage_context() {
|
||||
coverage_context.function_coverage_map.borrow_mut().insert(instance, function_coverage);
|
||||
|
|
|
@ -1235,6 +1235,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
|
||||
/// Turning a "maybe pointer" into a proper pointer (and some information
|
||||
/// about where it points), or an absolute address.
|
||||
///
|
||||
/// The result must be used immediately; it is not allowed to convert
|
||||
/// the returned data back into a `Pointer` and store that in machine state.
|
||||
/// (In fact that's not even possible since `M::ProvenanceExtra` is generic and
|
||||
/// we don't have an operation to turn it back into `M::Provenance`.)
|
||||
pub fn ptr_try_get_alloc_id(
|
||||
&self,
|
||||
ptr: Pointer<Option<M::Provenance>>,
|
||||
|
@ -1253,6 +1258,11 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
|
|||
}
|
||||
|
||||
/// Turning a "maybe pointer" into a proper pointer (and some information about where it points).
|
||||
///
|
||||
/// The result must be used immediately; it is not allowed to convert
|
||||
/// the returned data back into a `Pointer` and store that in machine state.
|
||||
/// (In fact that's not even possible since `M::ProvenanceExtra` is generic and
|
||||
/// we don't have an operation to turn it back into `M::Provenance`.)
|
||||
#[inline(always)]
|
||||
pub fn ptr_get_alloc_id(
|
||||
&self,
|
||||
|
|
|
@ -3,7 +3,7 @@ use rustc_hir::def_id::CrateNum;
|
|||
use rustc_hir::definitions::DisambiguatedDefPathData;
|
||||
use rustc_middle::ty::{
|
||||
self,
|
||||
print::{PrettyPrinter, Print, Printer},
|
||||
print::{PrettyPrinter, Print, PrintError, Printer},
|
||||
GenericArg, GenericArgKind, Ty, TyCtxt,
|
||||
};
|
||||
use std::fmt::Write;
|
||||
|
@ -14,23 +14,15 @@ struct AbsolutePathPrinter<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
||||
type Error = std::fmt::Error;
|
||||
|
||||
type Path = Self;
|
||||
type Region = Self;
|
||||
type Type = Self;
|
||||
type DynExistential = Self;
|
||||
type Const = Self;
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> {
|
||||
fn print_region(self, _region: ty::Region<'_>) -> Result<Self, PrintError> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
|
||||
fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self, PrintError> {
|
||||
match *ty.kind() {
|
||||
// Types without identity.
|
||||
ty::Bool
|
||||
|
@ -68,18 +60,18 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
|
||||
fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self, PrintError> {
|
||||
self.pretty_print_const(ct, false)
|
||||
}
|
||||
|
||||
fn print_dyn_existential(
|
||||
self,
|
||||
predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self::DynExistential, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self.pretty_print_dyn_existential(predicates)
|
||||
}
|
||||
|
||||
fn path_crate(mut self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
|
||||
fn path_crate(mut self, cnum: CrateNum) -> Result<Self, PrintError> {
|
||||
self.path.push_str(self.tcx.crate_name(cnum).as_str());
|
||||
Ok(self)
|
||||
}
|
||||
|
@ -88,17 +80,17 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
|||
self,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self.pretty_path_qualified(self_ty, trait_ref)
|
||||
}
|
||||
|
||||
fn path_append_impl(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
_disambiguated_data: &DisambiguatedDefPathData,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self.pretty_path_append_impl(
|
||||
|mut cx| {
|
||||
cx = print_prefix(cx)?;
|
||||
|
@ -114,9 +106,9 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
|||
|
||||
fn path_append(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self = print_prefix(self)?;
|
||||
|
||||
write!(self.path, "::{}", disambiguated_data.data).unwrap();
|
||||
|
@ -126,9 +118,9 @@ impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
|||
|
||||
fn path_generic_args(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
args: &[GenericArg<'tcx>],
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self = print_prefix(self)?;
|
||||
let args =
|
||||
args.iter().cloned().filter(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_)));
|
||||
|
@ -144,9 +136,9 @@ impl<'tcx> PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> {
|
|||
fn should_print_region(&self, _region: ty::Region<'_>) -> bool {
|
||||
false
|
||||
}
|
||||
fn comma_sep<T>(mut self, mut elems: impl Iterator<Item = T>) -> Result<Self, Self::Error>
|
||||
fn comma_sep<T>(mut self, mut elems: impl Iterator<Item = T>) -> Result<Self, PrintError>
|
||||
where
|
||||
T: Print<'tcx, Self, Output = Self, Error = Self::Error>,
|
||||
T: Print<'tcx, Self>,
|
||||
{
|
||||
if let Some(first) = elems.next() {
|
||||
self = first.print(self)?;
|
||||
|
@ -160,8 +152,8 @@ impl<'tcx> PrettyPrinter<'tcx> for AbsolutePathPrinter<'tcx> {
|
|||
|
||||
fn generic_delimiters(
|
||||
mut self,
|
||||
f: impl FnOnce(Self) -> Result<Self, Self::Error>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
f: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
) -> Result<Self, PrintError> {
|
||||
write!(self, "<")?;
|
||||
|
||||
self = f(self)?;
|
||||
|
|
|
@ -1,116 +0,0 @@
|
|||
use rustc_index::{Idx, IndexVec};
|
||||
use std::{mem, rc::Rc, sync::Arc};
|
||||
|
||||
pub trait IdFunctor: Sized {
|
||||
type Inner;
|
||||
|
||||
fn try_map_id<F, E>(self, f: F) -> Result<Self, E>
|
||||
where
|
||||
F: FnMut(Self::Inner) -> Result<Self::Inner, E>;
|
||||
}
|
||||
|
||||
impl<T> IdFunctor for Box<T> {
|
||||
type Inner = T;
|
||||
|
||||
#[inline]
|
||||
fn try_map_id<F, E>(self, mut f: F) -> Result<Self, E>
|
||||
where
|
||||
F: FnMut(Self::Inner) -> Result<Self::Inner, E>,
|
||||
{
|
||||
let raw = Box::into_raw(self);
|
||||
Ok(unsafe {
|
||||
// SAFETY: The raw pointer points to a valid value of type `T`.
|
||||
let value = raw.read();
|
||||
// SAFETY: Converts `Box<T>` to `Box<MaybeUninit<T>>` which is the
|
||||
// inverse of `Box::assume_init()` and should be safe.
|
||||
let raw: Box<mem::MaybeUninit<T>> = Box::from_raw(raw.cast());
|
||||
// SAFETY: Write the mapped value back into the `Box`.
|
||||
Box::write(raw, f(value)?)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IdFunctor for Vec<T> {
|
||||
type Inner = T;
|
||||
|
||||
#[inline]
|
||||
fn try_map_id<F, E>(self, f: F) -> Result<Self, E>
|
||||
where
|
||||
F: FnMut(Self::Inner) -> Result<Self::Inner, E>,
|
||||
{
|
||||
self.into_iter().map(f).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IdFunctor for Box<[T]> {
|
||||
type Inner = T;
|
||||
|
||||
#[inline]
|
||||
fn try_map_id<F, E>(self, f: F) -> Result<Self, E>
|
||||
where
|
||||
F: FnMut(Self::Inner) -> Result<Self::Inner, E>,
|
||||
{
|
||||
Vec::from(self).try_map_id(f).map(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Idx, T> IdFunctor for IndexVec<I, T> {
|
||||
type Inner = T;
|
||||
|
||||
#[inline]
|
||||
fn try_map_id<F, E>(self, f: F) -> Result<Self, E>
|
||||
where
|
||||
F: FnMut(Self::Inner) -> Result<Self::Inner, E>,
|
||||
{
|
||||
self.raw.try_map_id(f).map(IndexVec::from_raw)
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! rc {
|
||||
($($rc:ident),+) => {$(
|
||||
impl<T: Clone> IdFunctor for $rc<T> {
|
||||
type Inner = T;
|
||||
|
||||
#[inline]
|
||||
fn try_map_id<F, E>(mut self, mut f: F) -> Result<Self, E>
|
||||
where
|
||||
F: FnMut(Self::Inner) -> Result<Self::Inner, E>,
|
||||
{
|
||||
// We merely want to replace the contained `T`, if at all possible,
|
||||
// so that we don't needlessly allocate a new `$rc` or indeed clone
|
||||
// the contained type.
|
||||
unsafe {
|
||||
// First step is to ensure that we have a unique reference to
|
||||
// the contained type, which `$rc::make_mut` will accomplish (by
|
||||
// allocating a new `$rc` and cloning the `T` only if required).
|
||||
// This is done *before* casting to `$rc<ManuallyDrop<T>>` so that
|
||||
// panicking during `make_mut` does not leak the `T`.
|
||||
$rc::make_mut(&mut self);
|
||||
|
||||
// Casting to `$rc<ManuallyDrop<T>>` is safe because `ManuallyDrop`
|
||||
// is `repr(transparent)`.
|
||||
let ptr = $rc::into_raw(self).cast::<mem::ManuallyDrop<T>>();
|
||||
let mut unique = $rc::from_raw(ptr);
|
||||
|
||||
// Call to `$rc::make_mut` above guarantees that `unique` is the
|
||||
// sole reference to the contained value, so we can avoid doing
|
||||
// a checked `get_mut` here.
|
||||
let slot = $rc::get_mut_unchecked(&mut unique);
|
||||
|
||||
// Semantically move the contained type out from `unique`, fold
|
||||
// it, then move the folded value back into `unique`. Should
|
||||
// folding fail, `ManuallyDrop` ensures that the "moved-out"
|
||||
// value is not re-dropped.
|
||||
let owned = mem::ManuallyDrop::take(slot);
|
||||
let folded = f(owned)?;
|
||||
*slot = mem::ManuallyDrop::new(folded);
|
||||
|
||||
// Cast back to `$rc<T>`.
|
||||
Ok($rc::from_raw($rc::into_raw(unique).cast()))
|
||||
}
|
||||
}
|
||||
}
|
||||
)+};
|
||||
}
|
||||
|
||||
rc! { Rc, Arc }
|
|
@ -10,7 +10,6 @@
|
|||
#![cfg_attr(not(bootstrap), feature(rustdoc_internals))]
|
||||
#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
|
||||
#![feature(array_windows)]
|
||||
#![feature(associated_type_bounds)]
|
||||
#![feature(auto_traits)]
|
||||
#![feature(cell_leak)]
|
||||
#![feature(core_intrinsics)]
|
||||
|
@ -21,15 +20,12 @@
|
|||
#![feature(min_specialization)]
|
||||
#![feature(never_type)]
|
||||
#![feature(type_alias_impl_trait)]
|
||||
#![feature(new_uninit)]
|
||||
#![feature(lazy_cell)]
|
||||
#![feature(rustc_attrs)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(test)]
|
||||
#![feature(thread_id_value)]
|
||||
#![feature(vec_into_raw_parts)]
|
||||
#![feature(allocator_api)]
|
||||
#![feature(get_mut_unchecked)]
|
||||
#![feature(lint_reasons)]
|
||||
#![feature(unwrap_infallible)]
|
||||
#![feature(strict_provenance)]
|
||||
|
@ -65,7 +61,6 @@ pub mod binary_search_util;
|
|||
pub mod captures;
|
||||
pub mod flat_map_in_place;
|
||||
pub mod flock;
|
||||
pub mod functor;
|
||||
pub mod fx;
|
||||
pub mod graph;
|
||||
pub mod intern;
|
||||
|
|
|
@ -52,7 +52,7 @@ rustc_target = { path = "../rustc_target" }
|
|||
rustc_trait_selection = { path = "../rustc_trait_selection" }
|
||||
rustc_ty_utils = { path = "../rustc_ty_utils" }
|
||||
serde_json = "1.0.59"
|
||||
time = { version = "0.3", default-features = false, features = ["formatting", ] }
|
||||
time = { version = "0.3", default-features = false, features = ["alloc", "formatting"] }
|
||||
tracing = { version = "0.1.35" }
|
||||
# tidy-alphabetical-end
|
||||
|
||||
|
|
|
@ -62,7 +62,6 @@ use std::str;
|
|||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::OnceLock;
|
||||
use std::time::{Instant, SystemTime};
|
||||
use time::format_description::well_known::Rfc3339;
|
||||
use time::OffsetDateTime;
|
||||
|
||||
#[allow(unused_macros)]
|
||||
|
@ -1185,7 +1184,11 @@ fn print_flag_list<T>(
|
|||
///
|
||||
/// So with all that in mind, the comments below have some more detail about the
|
||||
/// contortions done here to get things to work out correctly.
|
||||
fn handle_options(handler: &EarlyErrorHandler, args: &[String]) -> Option<getopts::Matches> {
|
||||
///
|
||||
/// This does not need to be `pub` for rustc itself, but @chaosite needs it to
|
||||
/// be public when using rustc as a library, see
|
||||
/// <https://github.com/rust-lang/rust/commit/2b4c33817a5aaecabf4c6598d41e190080ec119e>
|
||||
pub fn handle_options(handler: &EarlyErrorHandler, args: &[String]) -> Option<getopts::Matches> {
|
||||
if args.is_empty() {
|
||||
// user did not write `-v` nor `-Z unstable-options`, so do not
|
||||
// include that extra information.
|
||||
|
@ -1307,7 +1310,13 @@ fn ice_path() -> &'static Option<PathBuf> {
|
|||
None => std::env::current_dir().unwrap_or_default(),
|
||||
};
|
||||
let now: OffsetDateTime = SystemTime::now().into();
|
||||
let file_now = now.format(&Rfc3339).unwrap_or_default();
|
||||
let file_now = now
|
||||
.format(
|
||||
// Don't use a standard datetime format because Windows doesn't support `:` in paths
|
||||
&time::format_description::parse("[year]-[month]-[day]T[hour]_[minute]_[second]")
|
||||
.unwrap(),
|
||||
)
|
||||
.unwrap_or_default();
|
||||
let pid = std::process::id();
|
||||
path.push(format!("rustc-ice-{file_now}-{pid}.txt"));
|
||||
Some(path)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "rustc_fluent_macro"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -448,7 +448,7 @@ impl<'tcx> dyn AstConv<'tcx> + '_ {
|
|||
|
||||
debug!(?args_trait_ref_and_assoc_item);
|
||||
|
||||
tcx.mk_alias_ty(assoc_item.def_id, args_trait_ref_and_assoc_item)
|
||||
ty::AliasTy::new(tcx, assoc_item.def_id, args_trait_ref_and_assoc_item)
|
||||
})
|
||||
};
|
||||
|
||||
|
|
|
@ -437,7 +437,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
);
|
||||
|
||||
let quiet_projection_ty =
|
||||
tcx.mk_alias_ty(projection_ty.def_id, args_with_infer_self);
|
||||
ty::AliasTy::new(tcx, projection_ty.def_id, args_with_infer_self);
|
||||
|
||||
let term = pred.skip_binder().term;
|
||||
|
||||
|
|
|
@ -916,7 +916,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
// Type aliases defined in crates that have the
|
||||
// feature `lazy_type_alias` enabled get encoded as a type alias that normalization will
|
||||
// then actually instantiate the where bounds of.
|
||||
let alias_ty = tcx.mk_alias_ty(did, args);
|
||||
let alias_ty = ty::AliasTy::new(tcx, did, args);
|
||||
Ty::new_alias(tcx, ty::Weak, alias_ty)
|
||||
} else {
|
||||
tcx.at(span).type_of(did).instantiate(tcx, args)
|
||||
|
@ -1018,7 +1018,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
}
|
||||
err.span_suggestions(
|
||||
span,
|
||||
"use the fully-qualified path",
|
||||
"use fully-qualified syntax",
|
||||
suggestions,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
|
@ -1091,7 +1091,8 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
self.trait_defines_associated_item_named(r.def_id(), ty::AssocKind::Const, assoc_name)
|
||||
});
|
||||
|
||||
let (bound, next_cand) = match (matching_candidates.next(), const_candidates.next()) {
|
||||
let (mut bound, mut next_cand) = match (matching_candidates.next(), const_candidates.next())
|
||||
{
|
||||
(Some(bound), _) => (bound, matching_candidates.next()),
|
||||
(None, Some(bound)) => (bound, const_candidates.next()),
|
||||
(None, None) => {
|
||||
|
@ -1107,6 +1108,37 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
};
|
||||
debug!(?bound);
|
||||
|
||||
// look for a candidate that is not the same as our first bound, disregarding
|
||||
// whether the bound is const.
|
||||
while let Some(mut bound2) = next_cand {
|
||||
debug!(?bound2);
|
||||
let tcx = self.tcx();
|
||||
if bound2.bound_vars() != bound.bound_vars() {
|
||||
break;
|
||||
}
|
||||
|
||||
let generics = tcx.generics_of(bound.def_id());
|
||||
let Some(host_index) = generics.host_effect_index else { break };
|
||||
|
||||
// always return the bound that contains the host param.
|
||||
if let ty::ConstKind::Param(_) = bound2.skip_binder().args.const_at(host_index).kind() {
|
||||
(bound, bound2) = (bound2, bound);
|
||||
}
|
||||
|
||||
let unconsted_args = bound
|
||||
.skip_binder()
|
||||
.args
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(n, arg)| if host_index == n { tcx.consts.true_.into() } else { arg });
|
||||
|
||||
if unconsted_args.eq(bound2.skip_binder().args.iter()) {
|
||||
next_cand = matching_candidates.next().or_else(|| const_candidates.next());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(bound2) = next_cand {
|
||||
debug!(?bound2);
|
||||
|
||||
|
@ -1158,7 +1190,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
} else {
|
||||
err.span_suggestion_verbose(
|
||||
span.with_hi(assoc_name.span.lo()),
|
||||
"use fully qualified syntax to disambiguate",
|
||||
"use fully-qualified syntax to disambiguate",
|
||||
format!("<{ty_param_name} as {}>::", bound.print_only_trait_path()),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
|
@ -1686,7 +1718,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
.chain(args.into_iter().skip(parent_args.len())),
|
||||
);
|
||||
|
||||
let ty = Ty::new_alias(tcx, ty::Inherent, tcx.mk_alias_ty(assoc_item, args));
|
||||
let ty = Ty::new_alias(tcx, ty::Inherent, ty::AliasTy::new(tcx, assoc_item, args));
|
||||
|
||||
return Ok(Some((ty, assoc_item)));
|
||||
}
|
||||
|
|
|
@ -43,6 +43,34 @@ impl<'tcx> Bounds<'tcx> {
|
|||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
span: Span,
|
||||
polarity: ty::ImplPolarity,
|
||||
) {
|
||||
self.push_trait_bound_inner(tcx, trait_ref, span, polarity);
|
||||
|
||||
// push a non-const (`host = true`) version of the bound if it is `~const`.
|
||||
if tcx.features().effects
|
||||
&& let Some(host_effect_idx) = tcx.generics_of(trait_ref.def_id()).host_effect_index
|
||||
&& trait_ref.skip_binder().args.const_at(host_effect_idx) != tcx.consts.true_
|
||||
{
|
||||
let generics = tcx.generics_of(trait_ref.def_id());
|
||||
let Some(host_index) = generics.host_effect_index else { return };
|
||||
let trait_ref = trait_ref.map_bound(|mut trait_ref| {
|
||||
trait_ref.args =
|
||||
tcx.mk_args_from_iter(trait_ref.args.iter().enumerate().map(|(n, arg)| {
|
||||
if host_index == n { tcx.consts.true_.into() } else { arg }
|
||||
}));
|
||||
trait_ref
|
||||
});
|
||||
|
||||
self.push_trait_bound_inner(tcx, trait_ref, span, polarity);
|
||||
}
|
||||
}
|
||||
|
||||
fn push_trait_bound_inner(
|
||||
&mut self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
trait_ref: ty::PolyTraitRef<'tcx>,
|
||||
span: Span,
|
||||
polarity: ty::ImplPolarity,
|
||||
) {
|
||||
self.clauses.push((
|
||||
trait_ref
|
||||
|
|
|
@ -2286,7 +2286,7 @@ pub(super) fn check_type_bounds<'tcx>(
|
|||
_ => predicates.push(
|
||||
ty::Binder::bind_with_vars(
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: tcx.mk_alias_ty(trait_ty.def_id, rebased_args),
|
||||
projection_ty: ty::AliasTy::new(tcx, trait_ty.def_id, rebased_args),
|
||||
term: normalize_impl_ty.into(),
|
||||
},
|
||||
bound_vars,
|
||||
|
|
|
@ -11,7 +11,7 @@ use rustc_hir::intravisit::{self, Visitor};
|
|||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{GenericPredicates, ImplTraitInTraitData, ToPredicate};
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use rustc_span::{sym, Span, DUMMY_SP};
|
||||
|
||||
/// Returns a list of all type predicates (explicit and implicit) for the definition with
|
||||
/// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus
|
||||
|
@ -38,11 +38,38 @@ pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredic
|
|||
// an obligation and instead be skipped. Otherwise we'd use
|
||||
// `tcx.def_span(def_id);`
|
||||
let span = rustc_span::DUMMY_SP;
|
||||
result.predicates =
|
||||
tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once((
|
||||
ty::TraitRef::identity(tcx, def_id).to_predicate(tcx),
|
||||
let non_const_bound = if tcx.features().effects && tcx.has_attr(def_id, sym::const_trait) {
|
||||
// when `Self` is a const trait, also add `Self: Trait<.., true>` as implied bound,
|
||||
// because only implementing `Self: Trait<.., false>` is currently not possible.
|
||||
Some((
|
||||
ty::TraitRef::new(
|
||||
tcx,
|
||||
def_id,
|
||||
ty::GenericArgs::for_item(tcx, def_id, |param, _| {
|
||||
if param.is_host_effect() {
|
||||
tcx.consts.true_.into()
|
||||
} else {
|
||||
tcx.mk_param_from_def(param)
|
||||
}
|
||||
}),
|
||||
)
|
||||
.to_predicate(tcx),
|
||||
span,
|
||||
))));
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
result.predicates = tcx.arena.alloc_from_iter(
|
||||
result
|
||||
.predicates
|
||||
.iter()
|
||||
.copied()
|
||||
.chain(std::iter::once((
|
||||
ty::TraitRef::identity(tcx, def_id).to_predicate(tcx),
|
||||
span,
|
||||
)))
|
||||
.chain(non_const_bound),
|
||||
);
|
||||
}
|
||||
debug!("predicates_of(def_id={:?}) = {:?}", def_id, result);
|
||||
result
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "rustc_hir_typeck"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
|
|
@ -650,7 +650,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.sess
|
||||
.source_map()
|
||||
.is_multiline(call_expr.span.with_lo(callee_expr.span.hi()))
|
||||
&& call_expr.span.ctxt() == callee_expr.span.ctxt();
|
||||
&& call_expr.span.eq_ctxt(callee_expr.span);
|
||||
if call_is_multiline {
|
||||
err.span_suggestion(
|
||||
callee_expr.span.shrink_to_hi(),
|
||||
|
@ -786,8 +786,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
tcx.consts.false_
|
||||
}
|
||||
Some(hir::ConstContext::ConstFn) => {
|
||||
let args = ty::GenericArgs::identity_for_item(tcx, context);
|
||||
args.host_effect_param().expect("ConstContext::Maybe must have host effect param")
|
||||
let host_idx = tcx
|
||||
.generics_of(context)
|
||||
.host_effect_index
|
||||
.expect("ConstContext::Maybe must have host effect param");
|
||||
ty::GenericArgs::identity_for_item(tcx, context).const_at(host_idx)
|
||||
}
|
||||
None => tcx.consts.true_,
|
||||
};
|
||||
|
|
|
@ -221,14 +221,14 @@ impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> {
|
|||
let item_def_id = tcx.hir().ty_param_owner(def_id);
|
||||
let generics = tcx.generics_of(item_def_id);
|
||||
let index = generics.param_def_id_to_index[&def_id.to_def_id()];
|
||||
// HACK(eddyb) should get the original `Span`.
|
||||
let span = tcx.def_span(def_id);
|
||||
ty::GenericPredicates {
|
||||
parent: None,
|
||||
predicates: tcx.arena.alloc_from_iter(
|
||||
self.param_env.caller_bounds().iter().filter_map(|predicate| {
|
||||
match predicate.kind().skip_binder() {
|
||||
ty::ClauseKind::Trait(data) if data.self_ty().is_param(index) => {
|
||||
// HACK(eddyb) should get the original `Span`.
|
||||
let span = tcx.def_span(def_id);
|
||||
Some((predicate, span))
|
||||
}
|
||||
_ => None,
|
||||
|
|
|
@ -674,7 +674,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
);
|
||||
|
||||
let quiet_projection_ty =
|
||||
tcx.mk_alias_ty(projection_ty.def_id, args_with_infer_self);
|
||||
ty::AliasTy::new(tcx, projection_ty.def_id, args_with_infer_self);
|
||||
|
||||
let term = pred.skip_binder().term;
|
||||
|
||||
|
@ -1260,6 +1260,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// Dynamic limit to avoid hiding just one candidate, which is silly.
|
||||
let limit = if sources.len() == 5 { 5 } else { 4 };
|
||||
|
||||
let mut suggs = vec![];
|
||||
for (idx, source) in sources.iter().take(limit).enumerate() {
|
||||
match *source {
|
||||
CandidateSource::Impl(impl_did) => {
|
||||
|
@ -1322,7 +1323,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let path = self.tcx.def_path_str(trait_ref.skip_binder().def_id);
|
||||
|
||||
let ty = match item.kind {
|
||||
ty::AssocKind::Const | ty::AssocKind::Type => rcvr_ty,
|
||||
ty::AssocKind::Const | ty::AssocKind::Type => impl_ty,
|
||||
ty::AssocKind::Fn => self
|
||||
.tcx
|
||||
.fn_sig(item.def_id)
|
||||
|
@ -1334,19 +1335,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
.copied()
|
||||
.unwrap_or(rcvr_ty),
|
||||
};
|
||||
print_disambiguation_help(
|
||||
if let Some(sugg) = print_disambiguation_help(
|
||||
item_name,
|
||||
args,
|
||||
err,
|
||||
path,
|
||||
ty,
|
||||
Some(impl_ty),
|
||||
item.kind,
|
||||
self.tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id),
|
||||
sugg_span,
|
||||
idx,
|
||||
self.tcx.sess.source_map(),
|
||||
item.fn_has_self_parameter,
|
||||
);
|
||||
) {
|
||||
suggs.push(sugg);
|
||||
}
|
||||
}
|
||||
}
|
||||
CandidateSource::Trait(trait_did) => {
|
||||
|
@ -1370,23 +1374,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
};
|
||||
if let Some(sugg_span) = sugg_span {
|
||||
let path = self.tcx.def_path_str(trait_did);
|
||||
print_disambiguation_help(
|
||||
if let Some(sugg) = print_disambiguation_help(
|
||||
item_name,
|
||||
args,
|
||||
err,
|
||||
path,
|
||||
rcvr_ty,
|
||||
None,
|
||||
item.kind,
|
||||
self.tcx.def_kind_descr(item.kind.as_def_kind(), item.def_id),
|
||||
sugg_span,
|
||||
idx,
|
||||
self.tcx.sess.source_map(),
|
||||
item.fn_has_self_parameter,
|
||||
);
|
||||
) {
|
||||
suggs.push(sugg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if !suggs.is_empty() && let Some(span) = sugg_span {
|
||||
err.span_suggestions(
|
||||
span.with_hi(item_name.span.lo()),
|
||||
"use fully-qualified syntax to disambiguate",
|
||||
suggs,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
if sources.len() > limit {
|
||||
err.note(format!("and {} others", sources.len() - limit));
|
||||
}
|
||||
|
@ -3146,52 +3161,51 @@ fn print_disambiguation_help<'tcx>(
|
|||
err: &mut Diagnostic,
|
||||
trait_name: String,
|
||||
rcvr_ty: Ty<'_>,
|
||||
impl_self_ty: Option<Ty<'_>>,
|
||||
kind: ty::AssocKind,
|
||||
def_kind_descr: &'static str,
|
||||
span: Span,
|
||||
candidate: Option<usize>,
|
||||
source_map: &source_map::SourceMap,
|
||||
fn_has_self_parameter: bool,
|
||||
) {
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let (span, sugg) = if let (
|
||||
ty::AssocKind::Fn,
|
||||
Some(MethodCallComponents { receiver, args, .. }),
|
||||
) = (kind, args)
|
||||
{
|
||||
let args = format!(
|
||||
"({}{})",
|
||||
rcvr_ty.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()),
|
||||
std::iter::once(receiver)
|
||||
.chain(args.iter())
|
||||
.map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
"_".to_owned()
|
||||
}))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
);
|
||||
let trait_name = if !fn_has_self_parameter {
|
||||
format!("<{rcvr_ty} as {trait_name}>")
|
||||
) -> Option<String> {
|
||||
Some(
|
||||
if let (ty::AssocKind::Fn, Some(MethodCallComponents { receiver, args, .. })) = (kind, args)
|
||||
{
|
||||
let args = format!(
|
||||
"({}{})",
|
||||
rcvr_ty.ref_mutability().map_or("", |mutbl| mutbl.ref_prefix_str()),
|
||||
std::iter::once(receiver)
|
||||
.chain(args.iter())
|
||||
.map(|arg| source_map
|
||||
.span_to_snippet(arg.span)
|
||||
.unwrap_or_else(|_| { "_".to_owned() }))
|
||||
.collect::<Vec<_>>()
|
||||
.join(", "),
|
||||
);
|
||||
let trait_name = if !fn_has_self_parameter && let Some(impl_self_ty) = impl_self_ty {
|
||||
format!("<{impl_self_ty} as {trait_name}>")
|
||||
} else {
|
||||
trait_name
|
||||
};
|
||||
(span, format!("{trait_name}::{item_name}{args}"))
|
||||
} else {
|
||||
(span.with_hi(item_name.span.lo()), format!("<{rcvr_ty} as {trait_name}>::"))
|
||||
};
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
format!(
|
||||
"disambiguate the {} for {}",
|
||||
def_kind_descr,
|
||||
if let Some(candidate) = candidate {
|
||||
format!("candidate #{candidate}")
|
||||
} else {
|
||||
"the candidate".to_string()
|
||||
},
|
||||
),
|
||||
sugg,
|
||||
applicability,
|
||||
);
|
||||
err.span_suggestion_verbose(
|
||||
span,
|
||||
format!(
|
||||
"disambiguate the {def_kind_descr} for {}",
|
||||
if let Some(candidate) = candidate {
|
||||
format!("candidate #{candidate}")
|
||||
} else {
|
||||
"the candidate".to_string()
|
||||
},
|
||||
),
|
||||
format!("{trait_name}::{item_name}{args}"),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
return None;
|
||||
} else if let Some(impl_self_ty) = impl_self_ty {
|
||||
format!("<{impl_self_ty} as {trait_name}>::")
|
||||
} else {
|
||||
format!("{trait_name}::")
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ use rustc_hir::def_id::{DefId, LocalDefId};
|
|||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_middle::dep_graph::DepContext;
|
||||
use rustc_middle::ty::print::with_forced_trimmed_paths;
|
||||
use rustc_middle::ty::print::{with_forced_trimmed_paths, PrintError};
|
||||
use rustc_middle::ty::relate::{self, RelateResult, TypeRelation};
|
||||
use rustc_middle::ty::{
|
||||
self, error::TypeError, IsSuggestable, List, Region, Ty, TyCtxt, TypeFoldable,
|
||||
|
@ -580,76 +580,68 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
|
||||
struct AbsolutePathPrinter<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
segments: Vec<String>,
|
||||
}
|
||||
|
||||
struct NonTrivialPath;
|
||||
|
||||
impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
||||
type Error = NonTrivialPath;
|
||||
|
||||
type Path = Vec<String>;
|
||||
type Region = !;
|
||||
type Type = !;
|
||||
type DynExistential = !;
|
||||
type Const = !;
|
||||
|
||||
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> {
|
||||
Err(NonTrivialPath)
|
||||
fn print_region(self, _region: ty::Region<'_>) -> Result<Self, PrintError> {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
|
||||
fn print_type(self, _ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
|
||||
Err(NonTrivialPath)
|
||||
fn print_type(self, _ty: Ty<'tcx>) -> Result<Self, PrintError> {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
|
||||
fn print_dyn_existential(
|
||||
self,
|
||||
_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self::DynExistential, Self::Error> {
|
||||
Err(NonTrivialPath)
|
||||
) -> Result<Self, PrintError> {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
|
||||
fn print_const(self, _ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
|
||||
Err(NonTrivialPath)
|
||||
fn print_const(self, _ct: ty::Const<'tcx>) -> Result<Self, PrintError> {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
|
||||
fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
|
||||
Ok(vec![self.tcx.crate_name(cnum).to_string()])
|
||||
fn path_crate(mut self, cnum: CrateNum) -> Result<Self, PrintError> {
|
||||
self.segments = vec![self.tcx.crate_name(cnum).to_string()];
|
||||
Ok(self)
|
||||
}
|
||||
fn path_qualified(
|
||||
self,
|
||||
_self_ty: Ty<'tcx>,
|
||||
_trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
Err(NonTrivialPath)
|
||||
) -> Result<Self, PrintError> {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
|
||||
fn path_append_impl(
|
||||
self,
|
||||
_print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
_print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
_disambiguated_data: &DisambiguatedDefPathData,
|
||||
_self_ty: Ty<'tcx>,
|
||||
_trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
Err(NonTrivialPath)
|
||||
) -> Result<Self, PrintError> {
|
||||
Err(fmt::Error)
|
||||
}
|
||||
fn path_append(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
let mut path = print_prefix(self)?;
|
||||
path.push(disambiguated_data.to_string());
|
||||
Ok(path)
|
||||
) -> Result<Self, PrintError> {
|
||||
self = print_prefix(self)?;
|
||||
self.segments.push(disambiguated_data.to_string());
|
||||
Ok(self)
|
||||
}
|
||||
fn path_generic_args(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
_args: &[GenericArg<'tcx>],
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
print_prefix(self)
|
||||
}
|
||||
}
|
||||
|
@ -659,12 +651,15 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
// are from a local module we could have false positives, e.g.
|
||||
// let _ = [{struct Foo; Foo}, {struct Foo; Foo}];
|
||||
if did1.krate != did2.krate {
|
||||
let abs_path =
|
||||
|def_id| AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]);
|
||||
let abs_path = |def_id| {
|
||||
AbsolutePathPrinter { tcx: self.tcx, segments: vec![] }
|
||||
.print_def_path(def_id, &[])
|
||||
.map(|p| p.segments)
|
||||
};
|
||||
|
||||
// We compare strings because DefPath can be different
|
||||
// for imported and non-imported crates
|
||||
let same_path = || -> Result<_, NonTrivialPath> {
|
||||
let same_path = || -> Result<_, PrintError> {
|
||||
Ok(self.tcx.def_path_str(did1) == self.tcx.def_path_str(did2)
|
||||
|| abs_path(did1)? == abs_path(did2)?)
|
||||
};
|
||||
|
|
|
@ -28,7 +28,7 @@ pub struct Highlighted<'tcx, T> {
|
|||
|
||||
impl<'tcx, T> IntoDiagnosticArg for Highlighted<'tcx, T>
|
||||
where
|
||||
T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>, Error = fmt::Error, Output = FmtPrinter<'a, 'tcx>>,
|
||||
T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>,
|
||||
{
|
||||
fn into_diagnostic_arg(self) -> rustc_errors::DiagnosticArgValue<'static> {
|
||||
rustc_errors::DiagnosticArgValue::Str(self.to_string().into())
|
||||
|
@ -43,7 +43,7 @@ impl<'tcx, T> Highlighted<'tcx, T> {
|
|||
|
||||
impl<'tcx, T> fmt::Display for Highlighted<'tcx, T>
|
||||
where
|
||||
T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>, Error = fmt::Error, Output = FmtPrinter<'a, 'tcx>>,
|
||||
T: for<'a> Print<'tcx, FmtPrinter<'a, 'tcx>>,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let mut printer = ty::print::FmtPrinter::new(self.tcx, Namespace::TypeNS);
|
||||
|
|
|
@ -85,8 +85,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
{
|
||||
let p_def_id = tcx.generics_of(body_owner_def_id).type_param(p, tcx).def_id;
|
||||
let p_span = tcx.def_span(p_def_id);
|
||||
let expected = match (values.expected.kind(), values.found.kind()) {
|
||||
(ty::Param(_), _) => "expected ",
|
||||
(_, ty::Param(_)) => "found ",
|
||||
_ => "",
|
||||
};
|
||||
if !sp.contains(p_span) {
|
||||
diag.span_label(p_span, "this type parameter");
|
||||
diag.span_label(p_span, format!("{expected}this type parameter"));
|
||||
}
|
||||
let hir = tcx.hir();
|
||||
let mut note = true;
|
||||
|
@ -168,8 +173,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
|||
| (ty::Dynamic(..) | ty::Alias(ty::Opaque, ..), ty::Param(p)) => {
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
|
||||
let expected = match (values.expected.kind(), values.found.kind()) {
|
||||
(ty::Param(_), _) => "expected ",
|
||||
(_, ty::Param(_)) => "found ",
|
||||
_ => "",
|
||||
};
|
||||
if !sp.contains(p_span) {
|
||||
diag.span_label(p_span, "this type parameter");
|
||||
diag.span_label(p_span, format!("{expected}this type parameter"));
|
||||
}
|
||||
diag.help("type parameters must be constrained to match other types");
|
||||
if tcx.sess.teach(&diag.get_code().unwrap()) {
|
||||
|
@ -209,7 +219,7 @@ impl<T> Trait<T> for X {
|
|||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
|
||||
if !sp.contains(p_span) {
|
||||
diag.span_label(p_span, "this type parameter");
|
||||
diag.span_label(p_span, "expected this type parameter");
|
||||
}
|
||||
diag.help(format!(
|
||||
"every closure has a distinct type and so could not always match the \
|
||||
|
@ -219,8 +229,13 @@ impl<T> Trait<T> for X {
|
|||
(ty::Param(p), _) | (_, ty::Param(p)) => {
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
|
||||
let expected = match (values.expected.kind(), values.found.kind()) {
|
||||
(ty::Param(_), _) => "expected ",
|
||||
(_, ty::Param(_)) => "found ",
|
||||
_ => "",
|
||||
};
|
||||
if !sp.contains(p_span) {
|
||||
diag.span_label(p_span, "this type parameter");
|
||||
diag.span_label(p_span, format!("{expected}this type parameter"));
|
||||
}
|
||||
}
|
||||
(ty::Alias(ty::Projection | ty::Inherent, proj_ty), _)
|
||||
|
@ -341,39 +356,48 @@ impl<T> Trait<T> for X {
|
|||
let tcx = self.tcx;
|
||||
let assoc = tcx.associated_item(proj_ty.def_id);
|
||||
let (trait_ref, assoc_args) = proj_ty.trait_ref_and_own_args(tcx);
|
||||
if let Some(item) = tcx.hir().get_if_local(body_owner_def_id) {
|
||||
if let Some(hir_generics) = item.generics() {
|
||||
// Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
|
||||
// This will also work for `impl Trait`.
|
||||
let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() {
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
generics.type_param(param_ty, tcx).def_id
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
let Some(def_id) = def_id.as_local() else {
|
||||
return false;
|
||||
};
|
||||
let Some(item) = tcx.hir().get_if_local(body_owner_def_id) else {
|
||||
return false;
|
||||
};
|
||||
let Some(hir_generics) = item.generics() else {
|
||||
return false;
|
||||
};
|
||||
// Get the `DefId` for the type parameter corresponding to `A` in `<A as T>::Foo`.
|
||||
// This will also work for `impl Trait`.
|
||||
let def_id = if let ty::Param(param_ty) = proj_ty.self_ty().kind() {
|
||||
let generics = tcx.generics_of(body_owner_def_id);
|
||||
generics.type_param(param_ty, tcx).def_id
|
||||
} else {
|
||||
return false;
|
||||
};
|
||||
let Some(def_id) = def_id.as_local() else {
|
||||
return false;
|
||||
};
|
||||
|
||||
// First look in the `where` clause, as this might be
|
||||
// `fn foo<T>(x: T) where T: Trait`.
|
||||
for pred in hir_generics.bounds_for_param(def_id) {
|
||||
if self.constrain_generic_bound_associated_type_structured_suggestion(
|
||||
diag,
|
||||
&trait_ref,
|
||||
pred.bounds,
|
||||
assoc,
|
||||
assoc_args,
|
||||
ty,
|
||||
&msg,
|
||||
false,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// First look in the `where` clause, as this might be
|
||||
// `fn foo<T>(x: T) where T: Trait`.
|
||||
for pred in hir_generics.bounds_for_param(def_id) {
|
||||
if self.constrain_generic_bound_associated_type_structured_suggestion(
|
||||
diag,
|
||||
&trait_ref,
|
||||
pred.bounds,
|
||||
assoc,
|
||||
assoc_args,
|
||||
ty,
|
||||
&msg,
|
||||
false,
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
// If associated item, look to constrain the params of the trait/impl.
|
||||
let hir_id = match item {
|
||||
hir::Node::ImplItem(item) => item.hir_id(),
|
||||
hir::Node::TraitItem(item) => item.hir_id(),
|
||||
_ => return false,
|
||||
};
|
||||
let parent = tcx.hir().get_parent_item(hir_id).def_id;
|
||||
self.suggest_constraint(diag, msg, parent.into(), proj_ty, ty)
|
||||
}
|
||||
|
||||
/// An associated type was expected and a different type was found.
|
||||
|
@ -426,21 +450,26 @@ impl<T> Trait<T> for X {
|
|||
let impl_comparison =
|
||||
matches!(cause_code, ObligationCauseCode::CompareImplItemObligation { .. });
|
||||
let assoc = tcx.associated_item(proj_ty.def_id);
|
||||
if !callable_scope || impl_comparison {
|
||||
if impl_comparison {
|
||||
// We do not want to suggest calling functions when the reason of the
|
||||
// type error is a comparison of an `impl` with its `trait` or when the
|
||||
// scope is outside of a `Body`.
|
||||
// type error is a comparison of an `impl` with its `trait`.
|
||||
} else {
|
||||
// If we find a suitable associated function that returns the expected type, we don't
|
||||
// want the more general suggestion later in this method about "consider constraining
|
||||
// the associated type or calling a method that returns the associated type".
|
||||
let point_at_assoc_fn = self.point_at_methods_that_satisfy_associated_type(
|
||||
diag,
|
||||
assoc.container_id(tcx),
|
||||
current_method_ident,
|
||||
proj_ty.def_id,
|
||||
values.expected,
|
||||
);
|
||||
let point_at_assoc_fn = if callable_scope
|
||||
&& self.point_at_methods_that_satisfy_associated_type(
|
||||
diag,
|
||||
assoc.container_id(tcx),
|
||||
current_method_ident,
|
||||
proj_ty.def_id,
|
||||
values.expected,
|
||||
) {
|
||||
// If we find a suitable associated function that returns the expected type, we
|
||||
// don't want the more general suggestion later in this method about "consider
|
||||
// constraining the associated type or calling a method that returns the associated
|
||||
// type".
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
// Possibly suggest constraining the associated type to conform to the
|
||||
// found type.
|
||||
if self.suggest_constraint(diag, &msg, body_owner_def_id, proj_ty, values.found)
|
||||
|
|
|
@ -36,7 +36,7 @@ use rustc_middle::ty::{self, GenericParamDefKind, InferConst, InferTy, Ty, TyCtx
|
|||
use rustc_middle::ty::{ConstVid, EffectVid, FloatVid, IntVid, TyVid};
|
||||
use rustc_middle::ty::{GenericArg, GenericArgKind, GenericArgs, GenericArgsRef};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::Span;
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::fmt;
|
||||
|
@ -1422,12 +1422,25 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
/// This method is idempotent, but it not typically not invoked
|
||||
/// except during the writeback phase.
|
||||
pub fn fully_resolve<T: TypeFoldable<TyCtxt<'tcx>>>(&self, value: T) -> FixupResult<'tcx, T> {
|
||||
let value = resolve::fully_resolve(self, value);
|
||||
assert!(
|
||||
value.as_ref().map_or(true, |value| !value.has_infer()),
|
||||
"`{value:?}` is not fully resolved"
|
||||
);
|
||||
value
|
||||
match resolve::fully_resolve(self, value) {
|
||||
Ok(value) => {
|
||||
if value.has_non_region_infer() {
|
||||
bug!("`{value:?}` is not fully resolved");
|
||||
}
|
||||
if value.has_infer_regions() {
|
||||
let guar = self
|
||||
.tcx
|
||||
.sess
|
||||
.delay_span_bug(DUMMY_SP, format!("`{value:?}` is not fully resolved"));
|
||||
Ok(self.tcx.fold_regions(value, |re, _| {
|
||||
if re.is_var() { ty::Region::new_error(self.tcx, guar) } else { re }
|
||||
}))
|
||||
} else {
|
||||
Ok(value)
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
// Instantiates the bound variables in a given binder with fresh inference
|
||||
|
|
|
@ -145,25 +145,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
|||
return None;
|
||||
}
|
||||
}
|
||||
DefiningAnchor::Bubble => {
|
||||
if let ty::Alias(ty::Opaque, _) = b.kind() {
|
||||
// In bubble mode we don't know which of the two opaque types is supposed to have the other
|
||||
// as a hidden type (both, none or either one of them could be in its defining scope).
|
||||
let predicate = ty::PredicateKind::AliasRelate(
|
||||
a.into(),
|
||||
b.into(),
|
||||
ty::AliasRelationDirection::Equate,
|
||||
);
|
||||
let obligation = traits::Obligation::new(
|
||||
self.tcx,
|
||||
cause.clone(),
|
||||
param_env,
|
||||
predicate,
|
||||
);
|
||||
let obligations = vec![obligation];
|
||||
return Some(Ok(InferOk { value: (), obligations }));
|
||||
}
|
||||
}
|
||||
DefiningAnchor::Bubble => {}
|
||||
DefiningAnchor::Error => return None,
|
||||
};
|
||||
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: b_def_id, .. }) = *b.kind() {
|
||||
|
|
|
@ -125,8 +125,13 @@ pub fn parse_cfgspecs(
|
|||
/// Converts strings provided as `--check-cfg [specs]` into a `CheckCfg`.
|
||||
pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> CheckCfg {
|
||||
rustc_span::create_default_session_if_not_set_then(move |_| {
|
||||
let mut check_cfg = CheckCfg::default();
|
||||
// If any --check-cfg is passed then exhaustive_values and exhaustive_names
|
||||
// are enabled by default.
|
||||
let exhaustive_names = !specs.is_empty();
|
||||
let exhaustive_values = !specs.is_empty();
|
||||
let mut check_cfg = CheckCfg { exhaustive_names, exhaustive_values, ..CheckCfg::default() };
|
||||
|
||||
let mut old_syntax = None;
|
||||
for s in specs {
|
||||
let sess = ParseSess::with_silent_emitter(Some(format!(
|
||||
"this error occurred on the command line: `--check-cfg={s}`"
|
||||
|
@ -142,18 +147,21 @@ pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> Check
|
|||
};
|
||||
}
|
||||
|
||||
let expected_error = || {
|
||||
error!(
|
||||
"expected `names(name1, name2, ... nameN)` or \
|
||||
`values(name, \"value1\", \"value2\", ... \"valueN\")`"
|
||||
)
|
||||
};
|
||||
let expected_error =
|
||||
|| error!("expected `cfg(name, values(\"value1\", \"value2\", ... \"valueN\"))`");
|
||||
|
||||
match maybe_new_parser_from_source_str(&sess, filename, s.to_string()) {
|
||||
Ok(mut parser) => match parser.parse_meta_item() {
|
||||
Ok(meta_item) if parser.token == token::Eof => {
|
||||
if let Some(args) = meta_item.meta_item_list() {
|
||||
if meta_item.has_name(sym::names) {
|
||||
// defaults are flipped for the old syntax
|
||||
if old_syntax == None {
|
||||
check_cfg.exhaustive_names = false;
|
||||
check_cfg.exhaustive_values = false;
|
||||
}
|
||||
old_syntax = Some(true);
|
||||
|
||||
check_cfg.exhaustive_names = true;
|
||||
for arg in args {
|
||||
if arg.is_word() && arg.ident().is_some() {
|
||||
|
@ -167,6 +175,13 @@ pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> Check
|
|||
}
|
||||
}
|
||||
} else if meta_item.has_name(sym::values) {
|
||||
// defaults are flipped for the old syntax
|
||||
if old_syntax == None {
|
||||
check_cfg.exhaustive_names = false;
|
||||
check_cfg.exhaustive_values = false;
|
||||
}
|
||||
old_syntax = Some(true);
|
||||
|
||||
if let Some((name, values)) = args.split_first() {
|
||||
if name.is_word() && name.ident().is_some() {
|
||||
let ident = name.ident().expect("multi-segment cfg key");
|
||||
|
@ -216,6 +231,116 @@ pub fn parse_check_cfg(handler: &EarlyErrorHandler, specs: Vec<String>) -> Check
|
|||
} else {
|
||||
expected_error();
|
||||
}
|
||||
} else if meta_item.has_name(sym::cfg) {
|
||||
old_syntax = Some(false);
|
||||
|
||||
let mut names = Vec::new();
|
||||
let mut values: FxHashSet<_> = Default::default();
|
||||
|
||||
let mut any_specified = false;
|
||||
let mut values_specified = false;
|
||||
let mut values_any_specified = false;
|
||||
|
||||
for arg in args {
|
||||
if arg.is_word() && let Some(ident) = arg.ident() {
|
||||
if values_specified {
|
||||
error!("`cfg()` names cannot be after values");
|
||||
}
|
||||
names.push(ident);
|
||||
} else if arg.has_name(sym::any)
|
||||
&& let Some(args) = arg.meta_item_list()
|
||||
{
|
||||
if any_specified {
|
||||
error!("`any()` cannot be specified multiple times");
|
||||
}
|
||||
any_specified = true;
|
||||
if !args.is_empty() {
|
||||
error!("`any()` must be empty");
|
||||
}
|
||||
} else if arg.has_name(sym::values)
|
||||
&& let Some(args) = arg.meta_item_list()
|
||||
{
|
||||
if names.is_empty() {
|
||||
error!(
|
||||
"`values()` cannot be specified before the names"
|
||||
);
|
||||
} else if values_specified {
|
||||
error!(
|
||||
"`values()` cannot be specified multiple times"
|
||||
);
|
||||
}
|
||||
values_specified = true;
|
||||
|
||||
for arg in args {
|
||||
if let Some(LitKind::Str(s, _)) =
|
||||
arg.lit().map(|lit| &lit.kind)
|
||||
{
|
||||
values.insert(Some(s.to_string()));
|
||||
} else if arg.has_name(sym::any)
|
||||
&& let Some(args) = arg.meta_item_list()
|
||||
{
|
||||
if values_any_specified {
|
||||
error!(
|
||||
"`any()` in `values()` cannot be specified multiple times"
|
||||
);
|
||||
}
|
||||
values_any_specified = true;
|
||||
if !args.is_empty() {
|
||||
error!("`any()` must be empty");
|
||||
}
|
||||
} else {
|
||||
error!(
|
||||
"`values()` arguments must be string literals or `any()`"
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error!(
|
||||
"`cfg()` arguments must be simple identifiers, `any()` or `values(...)`"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if values.is_empty() && !values_any_specified && !any_specified {
|
||||
values.insert(None);
|
||||
} else if !values.is_empty() && values_any_specified {
|
||||
error!(
|
||||
"`values()` arguments cannot specify string literals and `any()` at the same time"
|
||||
);
|
||||
}
|
||||
|
||||
if any_specified {
|
||||
if !names.is_empty()
|
||||
|| !values.is_empty()
|
||||
|| values_any_specified
|
||||
{
|
||||
error!("`cfg(any())` can only be provided in isolation");
|
||||
}
|
||||
|
||||
check_cfg.exhaustive_names = false;
|
||||
} else {
|
||||
for name in names {
|
||||
check_cfg
|
||||
.expecteds
|
||||
.entry(name.to_string())
|
||||
.and_modify(|v| match v {
|
||||
ExpectedValues::Some(v)
|
||||
if !values_any_specified =>
|
||||
{
|
||||
v.extend(values.clone())
|
||||
}
|
||||
ExpectedValues::Some(_) => *v = ExpectedValues::Any,
|
||||
ExpectedValues::Any => {}
|
||||
})
|
||||
.or_insert_with(|| {
|
||||
if values_any_specified {
|
||||
ExpectedValues::Any
|
||||
} else {
|
||||
ExpectedValues::Some(values.clone())
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
expected_error();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#![feature(internal_output_capture)]
|
||||
#![feature(thread_spawn_unchecked)]
|
||||
#![feature(lazy_cell)]
|
||||
#![feature(let_chains)]
|
||||
#![feature(try_blocks)]
|
||||
#![recursion_limit = "256"]
|
||||
#![allow(rustc::potential_query_instability)]
|
||||
|
|
|
@ -770,6 +770,7 @@ fn test_unstable_options_tracking_hash() {
|
|||
);
|
||||
tracked!(codegen_backend, Some("abc".to_string()));
|
||||
tracked!(crate_attr, vec!["abc".to_string()]);
|
||||
tracked!(cross_crate_inline_threshold, Some(200));
|
||||
tracked!(debug_info_for_profiling, true);
|
||||
tracked!(debug_macros, true);
|
||||
tracked!(dep_info_omit_d_target, true);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "rustc_lexer"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
license = "MIT OR Apache-2.0"
|
||||
edition = "2021"
|
||||
|
||||
|
|
|
@ -494,6 +494,8 @@ lint_renamed_lint = lint `{$name}` has been renamed to `{$replace}`
|
|||
|
||||
lint_requested_level = requested on the command line with `{$level} {$lint_name}`
|
||||
|
||||
lint_span_use_eq_ctxt = use `.eq_ctxt()` instead of `.ctxt() == .ctxt()`
|
||||
|
||||
lint_supertrait_as_deref_target = `{$t}` implements `Deref` with supertrait `{$target_principal}` as target
|
||||
.label = target type is set here
|
||||
|
||||
|
|
|
@ -58,7 +58,6 @@ declare_lint! {
|
|||
///
|
||||
///
|
||||
/// ```rust
|
||||
/// # #![feature(return_position_impl_trait_in_trait)]
|
||||
/// use core::future::Future;
|
||||
/// pub trait Trait {
|
||||
/// fn method(&self) -> impl Future<Output = ()> + Send { async {} }
|
||||
|
|
|
@ -677,6 +677,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingCopyImplementations {
|
|||
if type_implements_negative_copy_modulo_regions(cx.tcx, ty, param_env) {
|
||||
return;
|
||||
}
|
||||
if def.is_variant_list_non_exhaustive()
|
||||
|| def.variants().iter().any(|variant| variant.is_field_list_non_exhaustive())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// We shouldn't recommend implementing `Copy` on stateful things,
|
||||
// such as iterators.
|
||||
|
|
|
@ -31,7 +31,7 @@ use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
|
|||
use rustc_middle::middle::privacy::EffectiveVisibilities;
|
||||
use rustc_middle::middle::stability;
|
||||
use rustc_middle::ty::layout::{LayoutError, LayoutOfHelpers, TyAndLayout};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::print::{with_no_trimmed_paths, PrintError};
|
||||
use rustc_middle::ty::{self, print::Printer, GenericArg, RegisteredTools, Ty, TyCtxt};
|
||||
use rustc_session::config::ExpectedValues;
|
||||
use rustc_session::lint::{BuiltinLintDiagnostics, LintExpectationId};
|
||||
|
@ -1200,51 +1200,45 @@ impl<'tcx> LateContext<'tcx> {
|
|||
/// }
|
||||
/// ```
|
||||
pub fn get_def_path(&self, def_id: DefId) -> Vec<Symbol> {
|
||||
pub struct AbsolutePathPrinter<'tcx> {
|
||||
pub tcx: TyCtxt<'tcx>,
|
||||
struct AbsolutePathPrinter<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
path: Vec<Symbol>,
|
||||
}
|
||||
|
||||
impl<'tcx> Printer<'tcx> for AbsolutePathPrinter<'tcx> {
|
||||
type Error = !;
|
||||
|
||||
type Path = Vec<Symbol>;
|
||||
type Region = ();
|
||||
type Type = ();
|
||||
type DynExistential = ();
|
||||
type Const = ();
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> {
|
||||
Ok(())
|
||||
fn print_region(self, _region: ty::Region<'_>) -> Result<Self, PrintError> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn print_type(self, _ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
|
||||
Ok(())
|
||||
fn print_type(self, _ty: Ty<'tcx>) -> Result<Self, PrintError> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn print_dyn_existential(
|
||||
self,
|
||||
_predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self::DynExistential, Self::Error> {
|
||||
Ok(())
|
||||
) -> Result<Self, PrintError> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn print_const(self, _ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
|
||||
Ok(())
|
||||
fn print_const(self, _ct: ty::Const<'tcx>) -> Result<Self, PrintError> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
|
||||
Ok(vec![self.tcx.crate_name(cnum)])
|
||||
fn path_crate(mut self, cnum: CrateNum) -> Result<Self, PrintError> {
|
||||
self.path = vec![self.tcx.crate_name(cnum)];
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn path_qualified(
|
||||
self,
|
||||
mut self,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
if trait_ref.is_none() {
|
||||
if let ty::Adt(def, args) = self_ty.kind() {
|
||||
return self.print_def_path(def.did(), args);
|
||||
|
@ -1253,24 +1247,25 @@ impl<'tcx> LateContext<'tcx> {
|
|||
|
||||
// This shouldn't ever be needed, but just in case:
|
||||
with_no_trimmed_paths!({
|
||||
Ok(vec![match trait_ref {
|
||||
self.path = vec![match trait_ref {
|
||||
Some(trait_ref) => Symbol::intern(&format!("{trait_ref:?}")),
|
||||
None => Symbol::intern(&format!("<{self_ty}>")),
|
||||
}])
|
||||
}];
|
||||
Ok(self)
|
||||
})
|
||||
}
|
||||
|
||||
fn path_append_impl(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
_disambiguated_data: &DisambiguatedDefPathData,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
let mut path = print_prefix(self)?;
|
||||
|
||||
// This shouldn't ever be needed, but just in case:
|
||||
path.push(match trait_ref {
|
||||
path.path.push(match trait_ref {
|
||||
Some(trait_ref) => {
|
||||
with_no_trimmed_paths!(Symbol::intern(&format!(
|
||||
"<impl {} for {}>",
|
||||
|
@ -1288,9 +1283,9 @@ impl<'tcx> LateContext<'tcx> {
|
|||
|
||||
fn path_append(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
let mut path = print_prefix(self)?;
|
||||
|
||||
// Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs.
|
||||
|
@ -1298,20 +1293,23 @@ impl<'tcx> LateContext<'tcx> {
|
|||
return Ok(path);
|
||||
}
|
||||
|
||||
path.push(Symbol::intern(&disambiguated_data.data.to_string()));
|
||||
path.path.push(Symbol::intern(&disambiguated_data.data.to_string()));
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
fn path_generic_args(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
_args: &[GenericArg<'tcx>],
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
print_prefix(self)
|
||||
}
|
||||
}
|
||||
|
||||
AbsolutePathPrinter { tcx: self.tcx }.print_def_path(def_id, &[]).unwrap()
|
||||
AbsolutePathPrinter { tcx: self.tcx, path: vec![] }
|
||||
.print_def_path(def_id, &[])
|
||||
.unwrap()
|
||||
.path
|
||||
}
|
||||
|
||||
/// Returns the associated type `name` for `self_ty` as an implementation of `trait_id`.
|
||||
|
|
|
@ -3,14 +3,14 @@
|
|||
|
||||
use crate::lints::{
|
||||
BadOptAccessDiag, DefaultHashTypesDiag, DiagOutOfImpl, LintPassByHand, NonExistentDocKeyword,
|
||||
QueryInstability, TyQualified, TykindDiag, TykindKind, UntranslatableDiag,
|
||||
QueryInstability, SpanUseEqCtxtDiag, TyQualified, TykindDiag, TykindKind, UntranslatableDiag,
|
||||
UntranslatableDiagnosticTrivial,
|
||||
};
|
||||
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
|
||||
use rustc_ast as ast;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::{def_id::DefId, Expr, ExprKind, GenericArg, PatKind, Path, PathSegment, QPath};
|
||||
use rustc_hir::{HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind};
|
||||
use rustc_hir::{BinOp, BinOpKind, HirId, Impl, Item, ItemKind, Node, Pat, Ty, TyKind};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::hygiene::{ExpnKind, MacroKind};
|
||||
|
@ -537,3 +537,33 @@ impl LateLintPass<'_> for BadOptAccess {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare_tool_lint! {
|
||||
pub rustc::SPAN_USE_EQ_CTXT,
|
||||
Allow,
|
||||
"forbid uses of `==` with `Span::ctxt`, suggest `Span::eq_ctxt` instead",
|
||||
report_in_external_macro: true
|
||||
}
|
||||
|
||||
declare_lint_pass!(SpanUseEqCtxt => [SPAN_USE_EQ_CTXT]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for SpanUseEqCtxt {
|
||||
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
|
||||
if let ExprKind::Binary(BinOp { node: BinOpKind::Eq, .. }, lhs, rhs) = expr.kind {
|
||||
if is_span_ctxt_call(cx, lhs) && is_span_ctxt_call(cx, rhs) {
|
||||
cx.emit_spanned_lint(SPAN_USE_EQ_CTXT, expr.span, SpanUseEqCtxtDiag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_span_ctxt_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
match &expr.kind {
|
||||
ExprKind::MethodCall(..) => cx
|
||||
.typeck_results()
|
||||
.type_dependent_def_id(expr.hir_id)
|
||||
.is_some_and(|call_did| cx.tcx.is_diagnostic_item(sym::SpanCtxt, call_did)),
|
||||
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -531,6 +531,8 @@ fn register_internals(store: &mut LintStore) {
|
|||
store.register_late_mod_pass(|_| Box::new(BadOptAccess));
|
||||
store.register_lints(&PassByValue::get_lints());
|
||||
store.register_late_mod_pass(|_| Box::new(PassByValue));
|
||||
store.register_lints(&SpanUseEqCtxt::get_lints());
|
||||
store.register_late_mod_pass(|_| Box::new(SpanUseEqCtxt));
|
||||
// FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and
|
||||
// `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and
|
||||
// these lints will trigger all of the time - change this once migration to diagnostic structs
|
||||
|
@ -548,6 +550,7 @@ fn register_internals(store: &mut LintStore) {
|
|||
LintId::of(USAGE_OF_QUALIFIED_TY),
|
||||
LintId::of(EXISTING_DOC_KEYWORD),
|
||||
LintId::of(BAD_OPT_ACCESS),
|
||||
LintId::of(SPAN_USE_EQ_CTXT),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
|
|
@ -900,6 +900,10 @@ pub struct QueryInstability {
|
|||
pub query: Symbol,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_span_use_eq_ctxt)]
|
||||
pub struct SpanUseEqCtxtDiag;
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(lint_tykind_kind)]
|
||||
pub struct TykindKind {
|
||||
|
|
|
@ -4449,11 +4449,11 @@ declare_lint! {
|
|||
/// on itself), the blanket impl is not considered to hold for `u8`. This will
|
||||
/// change in a future release.
|
||||
pub COINDUCTIVE_OVERLAP_IN_COHERENCE,
|
||||
Warn,
|
||||
Deny,
|
||||
"impls that are not considered to overlap may be considered to \
|
||||
overlap in the future",
|
||||
@future_incompatible = FutureIncompatibleInfo {
|
||||
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
|
||||
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
|
||||
reference: "issue #114040 <https://github.com/rust-lang/rust/issues/114040>",
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "rustc_macros"
|
||||
version = "0.1.0"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
|
|
@ -1273,6 +1273,10 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
|||
self.root.tables.optimized_mir.get(self, id).is_some()
|
||||
}
|
||||
|
||||
fn cross_crate_inlinable(self, id: DefIndex) -> bool {
|
||||
self.root.tables.cross_crate_inlinable.get(self, id).unwrap_or(false)
|
||||
}
|
||||
|
||||
fn get_fn_has_self_parameter(self, id: DefIndex, sess: &'a Session) -> bool {
|
||||
self.root
|
||||
.tables
|
||||
|
|
|
@ -287,6 +287,7 @@ provide! { tcx, def_id, other, cdata,
|
|||
item_attrs => { tcx.arena.alloc_from_iter(cdata.get_item_attrs(def_id.index, tcx.sess)) }
|
||||
is_mir_available => { cdata.is_item_mir_available(def_id.index) }
|
||||
is_ctfe_mir_available => { cdata.is_ctfe_mir_available(def_id.index) }
|
||||
cross_crate_inlinable => { cdata.cross_crate_inlinable(def_id.index) }
|
||||
|
||||
dylib_dependency_formats => { cdata.get_dylib_dependency_formats(tcx) }
|
||||
is_private_dep => {
|
||||
|
|
|
@ -1054,7 +1054,7 @@ fn should_encode_mir(
|
|||
|| (tcx.sess.opts.output_types.should_codegen()
|
||||
&& reachable_set.contains(&def_id)
|
||||
&& (generics.requires_monomorphization(tcx)
|
||||
|| tcx.codegen_fn_attrs(def_id).requests_inline()));
|
||||
|| tcx.cross_crate_inlinable(def_id)));
|
||||
// The function has a `const` modifier or is in a `#[const_trait]`.
|
||||
let is_const_fn = tcx.is_const_fn_raw(def_id.to_def_id())
|
||||
|| tcx.is_const_default_method(def_id.to_def_id());
|
||||
|
@ -1623,6 +1623,9 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> {
|
|||
debug!("EntryBuilder::encode_mir({:?})", def_id);
|
||||
if encode_opt {
|
||||
record!(self.tables.optimized_mir[def_id.to_def_id()] <- tcx.optimized_mir(def_id));
|
||||
self.tables
|
||||
.cross_crate_inlinable
|
||||
.set(def_id.to_def_id().index, Some(self.tcx.cross_crate_inlinable(def_id)));
|
||||
record!(self.tables.closure_saved_names_of_captured_variables[def_id.to_def_id()]
|
||||
<- tcx.closure_saved_names_of_captured_variables(def_id));
|
||||
|
||||
|
|
|
@ -427,6 +427,7 @@ define_tables! {
|
|||
object_lifetime_default: Table<DefIndex, LazyValue<ObjectLifetimeDefault>>,
|
||||
optimized_mir: Table<DefIndex, LazyValue<mir::Body<'static>>>,
|
||||
mir_for_ctfe: Table<DefIndex, LazyValue<mir::Body<'static>>>,
|
||||
cross_crate_inlinable: Table<DefIndex, bool>,
|
||||
closure_saved_names_of_captured_variables: Table<DefIndex, LazyValue<IndexVec<FieldIdx, Symbol>>>,
|
||||
mir_generator_witnesses: Table<DefIndex, LazyValue<mir::GeneratorLayout<'static>>>,
|
||||
promoted_mir: Table<DefIndex, LazyValue<IndexVec<mir::Promoted, mir::Body<'static>>>>,
|
||||
|
|
|
@ -299,6 +299,30 @@ impl FixedSizeEncoding for bool {
|
|||
}
|
||||
}
|
||||
|
||||
impl FixedSizeEncoding for Option<bool> {
|
||||
type ByteArray = [u8; 1];
|
||||
|
||||
#[inline]
|
||||
fn from_bytes(b: &[u8; 1]) -> Self {
|
||||
match b[0] {
|
||||
0 => Some(false),
|
||||
1 => Some(true),
|
||||
2 => None,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_to_bytes(self, b: &mut [u8; 1]) {
|
||||
debug_assert!(!self.is_default());
|
||||
b[0] = match self {
|
||||
Some(false) => 0,
|
||||
Some(true) => 1,
|
||||
None => 2,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl FixedSizeEncoding for UnusedGenericParams {
|
||||
type ByteArray = [u8; 4];
|
||||
|
||||
|
|
|
@ -126,14 +126,6 @@ impl CodegenFnAttrs {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `#[inline]` or `#[inline(always)]` is present.
|
||||
pub fn requests_inline(&self) -> bool {
|
||||
match self.inline {
|
||||
InlineAttr::Hint | InlineAttr::Always => true,
|
||||
InlineAttr::None | InlineAttr::Never => false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if it looks like this symbol needs to be exported, for example:
|
||||
///
|
||||
/// * `#[no_mangle]` is present
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Metadata from source code coverage analysis and instrumentation.
|
||||
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_span::Symbol;
|
||||
|
||||
|
@ -8,6 +9,11 @@ use std::fmt::{self, Debug, Formatter};
|
|||
rustc_index::newtype_index! {
|
||||
/// ID of a coverage counter. Values ascend from 0.
|
||||
///
|
||||
/// Before MIR inlining, counter IDs are local to their enclosing function.
|
||||
/// After MIR inlining, coverage statements may have been inlined into
|
||||
/// another function, so use the statement's source-scope to find which
|
||||
/// function/instance its IDs are meaningful for.
|
||||
///
|
||||
/// 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)]
|
||||
|
@ -23,6 +29,11 @@ impl CounterId {
|
|||
rustc_index::newtype_index! {
|
||||
/// ID of a coverage-counter expression. Values ascend from 0.
|
||||
///
|
||||
/// Before MIR inlining, expression IDs are local to their enclosing function.
|
||||
/// After MIR inlining, coverage statements may have been inlined into
|
||||
/// another function, so use the statement's source-scope to find which
|
||||
/// function/instance its IDs are meaningful for.
|
||||
///
|
||||
/// 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)]
|
||||
|
@ -35,19 +46,21 @@ impl ExpressionId {
|
|||
pub const START: Self = Self::from_u32(0);
|
||||
}
|
||||
|
||||
/// Operand of a coverage-counter expression.
|
||||
/// Enum that can hold a constant zero value, the ID of an physical coverage
|
||||
/// counter, or the ID 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.
|
||||
/// This was originally only used for expression operands (and named `Operand`),
|
||||
/// but the zero/counter/expression distinction is also useful for representing
|
||||
/// the value of code/gap mappings, and the true/false arms of branch mappings.
|
||||
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub enum Operand {
|
||||
pub enum CovTerm {
|
||||
Zero,
|
||||
Counter(CounterId),
|
||||
Expression(ExpressionId),
|
||||
}
|
||||
|
||||
impl Debug for Operand {
|
||||
impl Debug for CovTerm {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Zero => write!(f, "Zero"),
|
||||
|
@ -59,40 +72,31 @@ impl Debug for Operand {
|
|||
|
||||
#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub enum CoverageKind {
|
||||
Counter {
|
||||
function_source_hash: u64,
|
||||
/// ID of this counter within its enclosing function.
|
||||
/// Expressions in the same function can refer to it as an operand.
|
||||
id: CounterId,
|
||||
},
|
||||
Expression {
|
||||
/// 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: Operand,
|
||||
},
|
||||
Unreachable,
|
||||
/// Marks the point in MIR control flow represented by a coverage counter.
|
||||
///
|
||||
/// This is eventually lowered to `llvm.instrprof.increment` in LLVM IR.
|
||||
///
|
||||
/// If this statement does not survive MIR optimizations, any mappings that
|
||||
/// refer to this counter can have those references simplified to zero.
|
||||
CounterIncrement { id: CounterId },
|
||||
|
||||
/// Marks the point in MIR control-flow represented by a coverage expression.
|
||||
///
|
||||
/// If this statement does not survive MIR optimizations, any mappings that
|
||||
/// refer to this expression can have those references simplified to zero.
|
||||
///
|
||||
/// (This is only inserted for expression IDs that are directly used by
|
||||
/// mappings. Intermediate expressions with no direct mappings are
|
||||
/// retained/zeroed based on whether they are transitively used.)
|
||||
ExpressionUsed { id: ExpressionId },
|
||||
}
|
||||
|
||||
impl Debug for CoverageKind {
|
||||
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
|
||||
use CoverageKind::*;
|
||||
match self {
|
||||
Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()),
|
||||
Expression { id, lhs, op, rhs } => write!(
|
||||
fmt,
|
||||
"Expression({:?}) = {:?} {} {:?}",
|
||||
id.index(),
|
||||
lhs,
|
||||
match op {
|
||||
Op::Add => "+",
|
||||
Op::Subtract => "-",
|
||||
},
|
||||
rhs,
|
||||
),
|
||||
Unreachable => write!(fmt, "Unreachable"),
|
||||
CounterIncrement { id } => write!(fmt, "CounterIncrement({:?})", id.index()),
|
||||
ExpressionUsed { id } => write!(fmt, "ExpressionUsed({:?})", id.index()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -133,3 +137,38 @@ impl Op {
|
|||
matches!(self, Self::Subtract)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct Expression {
|
||||
pub lhs: CovTerm,
|
||||
pub op: Op,
|
||||
pub rhs: CovTerm,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct Mapping {
|
||||
pub code_region: CodeRegion,
|
||||
|
||||
/// Indicates whether this mapping uses a counter value, expression value,
|
||||
/// or zero value.
|
||||
///
|
||||
/// FIXME: When we add support for mapping kinds other than `Code`
|
||||
/// (e.g. branch regions, expansion regions), replace this with a dedicated
|
||||
/// mapping-kind enum.
|
||||
pub term: CovTerm,
|
||||
}
|
||||
|
||||
/// Stores per-function coverage information attached to a `mir::Body`,
|
||||
/// to be used in conjunction with the individual coverage statements injected
|
||||
/// into the function's basic blocks.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(TyEncodable, TyDecodable, Hash, HashStable, TypeFoldable, TypeVisitable)]
|
||||
pub struct FunctionCoverageInfo {
|
||||
pub function_source_hash: u64,
|
||||
pub num_counters: usize,
|
||||
|
||||
pub expressions: IndexVec<ExpressionId, Expression>,
|
||||
pub mappings: Vec<Mapping>,
|
||||
}
|
||||
|
|
|
@ -345,6 +345,14 @@ pub struct Body<'tcx> {
|
|||
pub injection_phase: Option<MirPhase>,
|
||||
|
||||
pub tainted_by_errors: Option<ErrorGuaranteed>,
|
||||
|
||||
/// Per-function coverage information added by the `InstrumentCoverage`
|
||||
/// pass, to be used in conjunction with the coverage statements injected
|
||||
/// into this body's blocks.
|
||||
///
|
||||
/// If `-Cinstrument-coverage` is not active, or if an individual function
|
||||
/// is not eligible for coverage, then this should always be `None`.
|
||||
pub function_coverage_info: Option<Box<coverage::FunctionCoverageInfo>>,
|
||||
}
|
||||
|
||||
impl<'tcx> Body<'tcx> {
|
||||
|
@ -392,6 +400,7 @@ impl<'tcx> Body<'tcx> {
|
|||
is_polymorphic: false,
|
||||
injection_phase: None,
|
||||
tainted_by_errors,
|
||||
function_coverage_info: None,
|
||||
};
|
||||
body.is_polymorphic = body.has_non_region_param();
|
||||
body
|
||||
|
@ -420,6 +429,7 @@ impl<'tcx> Body<'tcx> {
|
|||
is_polymorphic: false,
|
||||
injection_phase: None,
|
||||
tainted_by_errors: None,
|
||||
function_coverage_info: None,
|
||||
};
|
||||
body.is_polymorphic = body.has_non_region_param();
|
||||
body
|
||||
|
|
|
@ -493,6 +493,27 @@ pub fn write_mir_intro<'tcx>(
|
|||
// Add an empty line before the first block is printed.
|
||||
writeln!(w)?;
|
||||
|
||||
if let Some(function_coverage_info) = &body.function_coverage_info {
|
||||
write_function_coverage_info(function_coverage_info, w)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_function_coverage_info(
|
||||
function_coverage_info: &coverage::FunctionCoverageInfo,
|
||||
w: &mut dyn io::Write,
|
||||
) -> io::Result<()> {
|
||||
let coverage::FunctionCoverageInfo { expressions, mappings, .. } = function_coverage_info;
|
||||
|
||||
for (id, expression) in expressions.iter_enumerated() {
|
||||
writeln!(w, "{INDENT}coverage {id:?} => {expression:?};")?;
|
||||
}
|
||||
for coverage::Mapping { term, code_region } in mappings {
|
||||
writeln!(w, "{INDENT}coverage {term:?} => {code_region:?};")?;
|
||||
}
|
||||
writeln!(w)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -685,13 +706,7 @@ impl Debug for Statement<'_> {
|
|||
AscribeUserType(box (ref place, ref c_ty), ref variance) => {
|
||||
write!(fmt, "AscribeUserType({place:?}, {variance:?}, {c_ty:?})")
|
||||
}
|
||||
Coverage(box mir::Coverage { ref kind, ref code_regions }) => {
|
||||
if code_regions.is_empty() {
|
||||
write!(fmt, "Coverage::{kind:?}")
|
||||
} else {
|
||||
write!(fmt, "Coverage::{kind:?} for {code_regions:?}")
|
||||
}
|
||||
}
|
||||
Coverage(box mir::Coverage { ref kind }) => write!(fmt, "Coverage::{kind:?}"),
|
||||
Intrinsic(box ref intrinsic) => write!(fmt, "{intrinsic}"),
|
||||
ConstEvalCounter => write!(fmt, "ConstEvalCounter"),
|
||||
Nop => write!(fmt, "nop"),
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Values computed by queries that use MIR.
|
||||
|
||||
use crate::mir;
|
||||
use crate::ty::{self, OpaqueHiddenType, Ty, TyCtxt};
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::unord::UnordSet;
|
||||
|
@ -445,14 +446,19 @@ pub struct DestructuredConstant<'tcx> {
|
|||
pub fields: &'tcx [(ConstValue<'tcx>, Ty<'tcx>)],
|
||||
}
|
||||
|
||||
/// Coverage information summarized from a MIR if instrumented for source code coverage (see
|
||||
/// compiler option `-Cinstrument-coverage`). This information is generated by the
|
||||
/// `InstrumentCoverage` MIR pass and can be retrieved via the `coverageinfo` query.
|
||||
/// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass
|
||||
/// (for compiler option `-Cinstrument-coverage`), after MIR optimizations
|
||||
/// have had a chance to potentially remove some of them.
|
||||
///
|
||||
/// Used by the `coverage_ids_info` query.
|
||||
#[derive(Clone, TyEncodable, TyDecodable, Debug, HashStable)]
|
||||
pub struct CoverageInfo {
|
||||
/// The total number of coverage region counters added to the MIR `Body`.
|
||||
pub num_counters: u32,
|
||||
|
||||
/// The total number of coverage region counter expressions added to the MIR `Body`.
|
||||
pub num_expressions: u32,
|
||||
pub struct CoverageIdsInfo {
|
||||
/// Coverage codegen needs to know the highest counter ID that is ever
|
||||
/// incremented within a function, so that it can set the `num-counters`
|
||||
/// argument of the `llvm.instrprof.increment` intrinsic.
|
||||
///
|
||||
/// This may be less than the highest counter ID emitted by the
|
||||
/// InstrumentCoverage MIR pass, if the highest-numbered counter increments
|
||||
/// were removed by MIR optimizations.
|
||||
pub max_counter_id: mir::coverage::CounterId,
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
use super::{BasicBlock, Const, Local, UserTypeProjection};
|
||||
|
||||
use crate::mir::coverage::{CodeRegion, CoverageKind};
|
||||
use crate::mir::coverage::CoverageKind;
|
||||
use crate::traits::Reveal;
|
||||
use crate::ty::adjustment::PointerCoercion;
|
||||
use crate::ty::GenericArgsRef;
|
||||
|
@ -361,11 +361,16 @@ pub enum StatementKind<'tcx> {
|
|||
/// Disallowed after drop elaboration.
|
||||
AscribeUserType(Box<(Place<'tcx>, UserTypeProjection)>, ty::Variance),
|
||||
|
||||
/// Marks the start of a "coverage region", injected with '-Cinstrument-coverage'. A
|
||||
/// `Coverage` statement carries metadata about the coverage region, used to inject a coverage
|
||||
/// map into the binary. If `Coverage::kind` is a `Counter`, the statement also generates
|
||||
/// executable code, to increment a counter variable at runtime, each time the code region is
|
||||
/// executed.
|
||||
/// Carries control-flow-sensitive information injected by `-Cinstrument-coverage`,
|
||||
/// such as where to generate physical coverage-counter-increments during codegen.
|
||||
///
|
||||
/// Coverage statements are used in conjunction with the coverage mappings and other
|
||||
/// information stored in the function's
|
||||
/// [`mir::Body::function_coverage_info`](crate::mir::Body::function_coverage_info).
|
||||
/// (For inlined MIR, take care to look up the *original function's* coverage info.)
|
||||
///
|
||||
/// Interpreters and codegen backends that don't support coverage instrumentation
|
||||
/// can usually treat this as a no-op.
|
||||
Coverage(Box<Coverage>),
|
||||
|
||||
/// Denotes a call to an intrinsic that does not require an unwind path and always returns.
|
||||
|
@ -514,7 +519,6 @@ pub enum FakeReadCause {
|
|||
#[derive(TypeFoldable, TypeVisitable)]
|
||||
pub struct Coverage {
|
||||
pub kind: CoverageKind,
|
||||
pub code_regions: Vec<CodeRegion>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
|
||||
|
|
|
@ -573,24 +573,14 @@ rustc_queries! {
|
|||
separate_provide_extern
|
||||
}
|
||||
|
||||
/// Returns coverage summary info for a function, after executing the `InstrumentCoverage`
|
||||
/// MIR pass (assuming the -Cinstrument-coverage option is enabled).
|
||||
query coverageinfo(key: ty::InstanceDef<'tcx>) -> &'tcx mir::CoverageInfo {
|
||||
desc { |tcx| "retrieving coverage info from MIR for `{}`", tcx.def_path_str(key.def_id()) }
|
||||
/// Summarizes coverage IDs inserted by the `InstrumentCoverage` MIR pass
|
||||
/// (for compiler option `-Cinstrument-coverage`), after MIR optimizations
|
||||
/// have had a chance to potentially remove some of them.
|
||||
query coverage_ids_info(key: ty::InstanceDef<'tcx>) -> &'tcx mir::CoverageIdsInfo {
|
||||
desc { |tcx| "retrieving coverage IDs info from MIR for `{}`", tcx.def_path_str(key.def_id()) }
|
||||
arena_cache
|
||||
}
|
||||
|
||||
/// Returns the `CodeRegions` for a function that has instrumented coverage, in case the
|
||||
/// function was optimized out before codegen, and before being added to the Coverage Map.
|
||||
query covered_code_regions(key: DefId) -> &'tcx Vec<&'tcx mir::coverage::CodeRegion> {
|
||||
desc {
|
||||
|tcx| "retrieving the covered `CodeRegion`s, if instrumented, for `{}`",
|
||||
tcx.def_path_str(key)
|
||||
}
|
||||
arena_cache
|
||||
cache_on_disk_if { key.is_local() }
|
||||
}
|
||||
|
||||
/// The `DefId` is the `DefId` of the containing MIR body. Promoteds do not have their own
|
||||
/// `DefId`. This function returns all promoteds in the specified body. The body references
|
||||
/// promoteds by the `DefId` and the `mir::Promoted` index. This is necessary, because
|
||||
|
@ -2202,6 +2192,11 @@ rustc_queries! {
|
|||
query generics_require_sized_self(def_id: DefId) -> bool {
|
||||
desc { "check whether the item has a `where Self: Sized` bound" }
|
||||
}
|
||||
|
||||
query cross_crate_inlinable(def_id: DefId) -> bool {
|
||||
desc { "whether the item should be made inlinable across crates" }
|
||||
separate_provide_extern
|
||||
}
|
||||
}
|
||||
|
||||
rustc_query_append! { define_callbacks! }
|
||||
|
|
|
@ -80,54 +80,45 @@ use std::ops::{Bound, Deref};
|
|||
|
||||
#[allow(rustc::usage_of_ty_tykind)]
|
||||
impl<'tcx> Interner for TyCtxt<'tcx> {
|
||||
type AdtDef = ty::AdtDef<'tcx>;
|
||||
type GenericArgsRef = ty::GenericArgsRef<'tcx>;
|
||||
type GenericArg = ty::GenericArg<'tcx>;
|
||||
type DefId = DefId;
|
||||
type AdtDef = ty::AdtDef<'tcx>;
|
||||
type GenericArgs = ty::GenericArgsRef<'tcx>;
|
||||
type GenericArg = ty::GenericArg<'tcx>;
|
||||
type Binder<T> = Binder<'tcx, T>;
|
||||
type Ty = Ty<'tcx>;
|
||||
type Const = ty::Const<'tcx>;
|
||||
type Region = Region<'tcx>;
|
||||
type Predicate = Predicate<'tcx>;
|
||||
type PredicateKind = ty::PredicateKind<'tcx>;
|
||||
type TypeAndMut = TypeAndMut<'tcx>;
|
||||
type Mutability = hir::Mutability;
|
||||
type Movability = hir::Movability;
|
||||
type PolyFnSig = PolyFnSig<'tcx>;
|
||||
type ListBinderExistentialPredicate = &'tcx List<PolyExistentialPredicate<'tcx>>;
|
||||
type BinderListTy = Binder<'tcx, &'tcx List<Ty<'tcx>>>;
|
||||
type ListTy = &'tcx List<Ty<'tcx>>;
|
||||
type Ty = Ty<'tcx>;
|
||||
type Tys = &'tcx List<Ty<'tcx>>;
|
||||
type AliasTy = ty::AliasTy<'tcx>;
|
||||
type ParamTy = ParamTy;
|
||||
type BoundTy = ty::BoundTy;
|
||||
type PlaceholderType = ty::PlaceholderType;
|
||||
type PlaceholderTy = ty::PlaceholderType;
|
||||
type InferTy = InferTy;
|
||||
type ErrorGuaranteed = ErrorGuaranteed;
|
||||
type PredicateKind = ty::PredicateKind<'tcx>;
|
||||
type BoundExistentialPredicates = &'tcx List<PolyExistentialPredicate<'tcx>>;
|
||||
type PolyFnSig = PolyFnSig<'tcx>;
|
||||
type AllocId = crate::mir::interpret::AllocId;
|
||||
|
||||
type Const = ty::Const<'tcx>;
|
||||
type InferConst = ty::InferConst<'tcx>;
|
||||
type AliasConst = ty::UnevaluatedConst<'tcx>;
|
||||
type PlaceholderConst = ty::PlaceholderConst<'tcx>;
|
||||
type ParamConst = ty::ParamConst;
|
||||
type BoundConst = ty::BoundVar;
|
||||
type PlaceholderConst = ty::PlaceholderConst<'tcx>;
|
||||
type ValueConst = ty::ValTree<'tcx>;
|
||||
type ExprConst = ty::Expr<'tcx>;
|
||||
|
||||
type Region = Region<'tcx>;
|
||||
type EarlyBoundRegion = ty::EarlyBoundRegion;
|
||||
type BoundRegion = ty::BoundRegion;
|
||||
type FreeRegion = ty::FreeRegion;
|
||||
type RegionVid = ty::RegionVid;
|
||||
type InferRegion = ty::RegionVid;
|
||||
type PlaceholderRegion = ty::PlaceholderRegion;
|
||||
|
||||
fn ty_and_mut_to_parts(
|
||||
TypeAndMut { ty, mutbl }: TypeAndMut<'tcx>,
|
||||
) -> (Self::Ty, Self::Mutability) {
|
||||
) -> (Self::Ty, ty::Mutability) {
|
||||
(ty, mutbl)
|
||||
}
|
||||
|
||||
fn mutability_is_mut(mutbl: Self::Mutability) -> bool {
|
||||
mutbl.is_mut()
|
||||
}
|
||||
}
|
||||
|
||||
type InternedSet<'tcx, T> = ShardedHashMap<InternedInSet<'tcx, T>, ()>;
|
||||
|
@ -1687,7 +1678,6 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
&& let DefKind::Impl { of_trait: false } = self.def_kind(self.parent(_def_id))
|
||||
{
|
||||
// If this is an inherent projection.
|
||||
|
||||
generics.params.len() + 1
|
||||
} else {
|
||||
generics.count()
|
||||
|
@ -1897,15 +1887,6 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
self.mk_args_from_iter(iter::once(self_ty.into()).chain(rest))
|
||||
}
|
||||
|
||||
pub fn mk_alias_ty(
|
||||
self,
|
||||
def_id: DefId,
|
||||
args: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
|
||||
) -> ty::AliasTy<'tcx> {
|
||||
let args = self.check_and_mk_args(def_id, args);
|
||||
ty::AliasTy { def_id, args, _use_mk_alias_ty_instead: () }
|
||||
}
|
||||
|
||||
pub fn mk_bound_variable_kinds_from_iter<I, T>(self, iter: I) -> T::Output
|
||||
where
|
||||
I: Iterator<Item = T>,
|
||||
|
|
|
@ -11,7 +11,6 @@ use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_macros::HashStable;
|
||||
use rustc_serialize::{self, Decodable, Encodable};
|
||||
use rustc_span::sym;
|
||||
use rustc_type_ir::WithCachedTypeInfo;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
|
@ -452,10 +451,6 @@ impl<'tcx> GenericArgs<'tcx> {
|
|||
tcx.mk_args_from_iter(self.iter().take(generics.count()))
|
||||
}
|
||||
|
||||
pub fn host_effect_param(&'tcx self) -> Option<ty::Const<'tcx>> {
|
||||
self.consts().rfind(|x| matches!(x.kind(), ty::ConstKind::Param(p) if p.name == sym::host))
|
||||
}
|
||||
|
||||
pub fn print_as_list(&self) -> String {
|
||||
let v = self.iter().map(|arg| arg.to_string()).collect::<Vec<_>>();
|
||||
format!("[{}]", v.join(", "))
|
||||
|
|
|
@ -79,6 +79,10 @@ impl GenericParamDef {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_host_effect(&self) -> bool {
|
||||
matches!(self.kind, GenericParamDefKind::Const { is_host_effect: true, .. })
|
||||
}
|
||||
|
||||
pub fn default_value<'tcx>(
|
||||
&self,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
|
|
|
@ -245,16 +245,15 @@ impl<'tcx> InstanceDef<'tcx> {
|
|||
// drops of `Option::None` before LTO. We also respect the intent of
|
||||
// `#[inline]` on `Drop::drop` implementations.
|
||||
return ty.ty_adt_def().map_or(true, |adt_def| {
|
||||
adt_def.destructor(tcx).map_or_else(
|
||||
|| adt_def.is_enum(),
|
||||
|dtor| tcx.codegen_fn_attrs(dtor.did).requests_inline(),
|
||||
)
|
||||
adt_def
|
||||
.destructor(tcx)
|
||||
.map_or_else(|| adt_def.is_enum(), |dtor| tcx.cross_crate_inlinable(dtor.did))
|
||||
});
|
||||
}
|
||||
if let ty::InstanceDef::ThreadLocalShim(..) = *self {
|
||||
return false;
|
||||
}
|
||||
tcx.codegen_fn_attrs(self.def_id()).requests_inline()
|
||||
tcx.cross_crate_inlinable(self.def_id())
|
||||
}
|
||||
|
||||
pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool {
|
||||
|
|
|
@ -1023,7 +1023,7 @@ impl<'tcx> Term<'tcx> {
|
|||
_ => None,
|
||||
},
|
||||
TermKind::Const(ct) => match ct.kind() {
|
||||
ConstKind::Unevaluated(uv) => Some(tcx.mk_alias_ty(uv.def, uv.args)),
|
||||
ConstKind::Unevaluated(uv) => Some(AliasTy::new(tcx, uv.def, uv.args)),
|
||||
_ => None,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -10,13 +10,12 @@ use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
|
|||
mod pretty;
|
||||
pub use self::pretty::*;
|
||||
|
||||
pub type PrintError = std::fmt::Error;
|
||||
|
||||
// FIXME(eddyb) false positive, the lifetime parameters are used with `P: Printer<...>`.
|
||||
#[allow(unused_lifetimes)]
|
||||
pub trait Print<'tcx, P> {
|
||||
type Output;
|
||||
type Error;
|
||||
|
||||
fn print(&self, cx: P) -> Result<Self::Output, Self::Error>;
|
||||
fn print(&self, cx: P) -> Result<P, PrintError>;
|
||||
}
|
||||
|
||||
/// Interface for outputting user-facing "type-system entities"
|
||||
|
@ -29,21 +28,13 @@ pub trait Print<'tcx, P> {
|
|||
//
|
||||
// FIXME(eddyb) find a better name; this is more general than "printing".
|
||||
pub trait Printer<'tcx>: Sized {
|
||||
type Error;
|
||||
|
||||
type Path;
|
||||
type Region;
|
||||
type Type;
|
||||
type DynExistential;
|
||||
type Const;
|
||||
|
||||
fn tcx<'a>(&'a self) -> TyCtxt<'tcx>;
|
||||
|
||||
fn print_def_path(
|
||||
self,
|
||||
def_id: DefId,
|
||||
args: &'tcx [GenericArg<'tcx>],
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self.default_print_def_path(def_id, args)
|
||||
}
|
||||
|
||||
|
@ -53,48 +44,48 @@ pub trait Printer<'tcx>: Sized {
|
|||
args: &'tcx [GenericArg<'tcx>],
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self.default_print_impl_path(impl_def_id, args, self_ty, trait_ref)
|
||||
}
|
||||
|
||||
fn print_region(self, region: ty::Region<'tcx>) -> Result<Self::Region, Self::Error>;
|
||||
fn print_region(self, region: ty::Region<'tcx>) -> Result<Self, PrintError>;
|
||||
|
||||
fn print_type(self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error>;
|
||||
fn print_type(self, ty: Ty<'tcx>) -> Result<Self, PrintError>;
|
||||
|
||||
fn print_dyn_existential(
|
||||
self,
|
||||
predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self::DynExistential, Self::Error>;
|
||||
) -> Result<Self, PrintError>;
|
||||
|
||||
fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error>;
|
||||
fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self, PrintError>;
|
||||
|
||||
fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error>;
|
||||
fn path_crate(self, cnum: CrateNum) -> Result<Self, PrintError>;
|
||||
|
||||
fn path_qualified(
|
||||
self,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error>;
|
||||
) -> Result<Self, PrintError>;
|
||||
|
||||
fn path_append_impl(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error>;
|
||||
) -> Result<Self, PrintError>;
|
||||
|
||||
fn path_append(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
) -> Result<Self::Path, Self::Error>;
|
||||
) -> Result<Self, PrintError>;
|
||||
|
||||
fn path_generic_args(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
args: &[GenericArg<'tcx>],
|
||||
) -> Result<Self::Path, Self::Error>;
|
||||
) -> Result<Self, PrintError>;
|
||||
|
||||
// Defaults (should not be overridden):
|
||||
|
||||
|
@ -103,7 +94,7 @@ pub trait Printer<'tcx>: Sized {
|
|||
self,
|
||||
def_id: DefId,
|
||||
args: &'tcx [GenericArg<'tcx>],
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
let key = self.tcx().def_key(def_id);
|
||||
debug!(?key);
|
||||
|
||||
|
@ -194,7 +185,7 @@ pub trait Printer<'tcx>: Sized {
|
|||
_args: &'tcx [GenericArg<'tcx>],
|
||||
self_ty: Ty<'tcx>,
|
||||
impl_trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
debug!(
|
||||
"default_print_impl_path: impl_def_id={:?}, self_ty={}, impl_trait_ref={:?}",
|
||||
impl_def_id, self_ty, impl_trait_ref
|
||||
|
@ -295,34 +286,25 @@ pub fn characteristic_def_id_of_type(ty: Ty<'_>) -> Option<DefId> {
|
|||
}
|
||||
|
||||
impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Region<'tcx> {
|
||||
type Output = P::Region;
|
||||
type Error = P::Error;
|
||||
fn print(&self, cx: P) -> Result<Self::Output, Self::Error> {
|
||||
fn print(&self, cx: P) -> Result<P, PrintError> {
|
||||
cx.print_region(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for Ty<'tcx> {
|
||||
type Output = P::Type;
|
||||
type Error = P::Error;
|
||||
|
||||
fn print(&self, cx: P) -> Result<Self::Output, Self::Error> {
|
||||
fn print(&self, cx: P) -> Result<P, PrintError> {
|
||||
cx.print_type(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>> {
|
||||
type Output = P::DynExistential;
|
||||
type Error = P::Error;
|
||||
fn print(&self, cx: P) -> Result<Self::Output, Self::Error> {
|
||||
fn print(&self, cx: P) -> Result<P, PrintError> {
|
||||
cx.print_dyn_existential(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, P: Printer<'tcx>> Print<'tcx, P> for ty::Const<'tcx> {
|
||||
type Output = P::Const;
|
||||
type Error = P::Error;
|
||||
fn print(&self, cx: P) -> Result<Self::Output, Self::Error> {
|
||||
fn print(&self, cx: P) -> Result<P, PrintError> {
|
||||
cx.print_const(*self)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -205,29 +205,19 @@ impl<'tcx> RegionHighlightMode<'tcx> {
|
|||
}
|
||||
|
||||
/// Trait for printers that pretty-print using `fmt::Write` to the printer.
|
||||
pub trait PrettyPrinter<'tcx>:
|
||||
Printer<
|
||||
'tcx,
|
||||
Error = fmt::Error,
|
||||
Path = Self,
|
||||
Region = Self,
|
||||
Type = Self,
|
||||
DynExistential = Self,
|
||||
Const = Self,
|
||||
> + fmt::Write
|
||||
{
|
||||
pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
|
||||
/// Like `print_def_path` but for value paths.
|
||||
fn print_value_path(
|
||||
self,
|
||||
def_id: DefId,
|
||||
args: &'tcx [GenericArg<'tcx>],
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self.print_def_path(def_id, args)
|
||||
}
|
||||
|
||||
fn in_binder<T>(self, value: &ty::Binder<'tcx, T>) -> Result<Self, Self::Error>
|
||||
fn in_binder<T>(self, value: &ty::Binder<'tcx, T>) -> Result<Self, PrintError>
|
||||
where
|
||||
T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
value.as_ref().skip_binder().print(self)
|
||||
}
|
||||
|
@ -236,17 +226,17 @@ pub trait PrettyPrinter<'tcx>:
|
|||
self,
|
||||
value: &ty::Binder<'tcx, T>,
|
||||
f: F,
|
||||
) -> Result<Self, Self::Error>
|
||||
) -> Result<Self, PrintError>
|
||||
where
|
||||
T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
f(value.as_ref().skip_binder(), self)
|
||||
}
|
||||
|
||||
/// Prints comma-separated elements.
|
||||
fn comma_sep<T>(mut self, mut elems: impl Iterator<Item = T>) -> Result<Self, Self::Error>
|
||||
fn comma_sep<T>(mut self, mut elems: impl Iterator<Item = T>) -> Result<Self, PrintError>
|
||||
where
|
||||
T: Print<'tcx, Self, Output = Self, Error = Self::Error>,
|
||||
T: Print<'tcx, Self>,
|
||||
{
|
||||
if let Some(first) = elems.next() {
|
||||
self = first.print(self)?;
|
||||
|
@ -261,10 +251,10 @@ pub trait PrettyPrinter<'tcx>:
|
|||
/// Prints `{f: t}` or `{f as t}` depending on the `cast` argument
|
||||
fn typed_value(
|
||||
mut self,
|
||||
f: impl FnOnce(Self) -> Result<Self, Self::Error>,
|
||||
t: impl FnOnce(Self) -> Result<Self, Self::Error>,
|
||||
f: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
t: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
conversion: &str,
|
||||
) -> Result<Self::Const, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self.write_str("{")?;
|
||||
self = f(self)?;
|
||||
self.write_str(conversion)?;
|
||||
|
@ -276,8 +266,8 @@ pub trait PrettyPrinter<'tcx>:
|
|||
/// Prints `<...>` around what `f` prints.
|
||||
fn generic_delimiters(
|
||||
self,
|
||||
f: impl FnOnce(Self) -> Result<Self, Self::Error>,
|
||||
) -> Result<Self, Self::Error>;
|
||||
f: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
) -> Result<Self, PrintError>;
|
||||
|
||||
/// Returns `true` if the region should be printed in
|
||||
/// optional positions, e.g., `&'a T` or `dyn Tr + 'b`.
|
||||
|
@ -291,7 +281,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
/// If possible, this returns a global path resolving to `def_id` that is visible
|
||||
/// from at least one local module, and returns `true`. If the crate defining `def_id` is
|
||||
/// declared with an `extern crate`, the path is guaranteed to use the `extern crate`.
|
||||
fn try_print_visible_def_path(self, def_id: DefId) -> Result<(Self, bool), Self::Error> {
|
||||
fn try_print_visible_def_path(self, def_id: DefId) -> Result<(Self, bool), PrintError> {
|
||||
if NO_VISIBLE_PATH.with(|flag| flag.get()) {
|
||||
return Ok((self, false));
|
||||
}
|
||||
|
@ -305,10 +295,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
// For enum variants, if they have an unique name, then we only print the name, otherwise we
|
||||
// print the enum name and the variant name. Otherwise, we do not print anything and let the
|
||||
// caller use the `print_def_path` fallback.
|
||||
fn force_print_trimmed_def_path(
|
||||
mut self,
|
||||
def_id: DefId,
|
||||
) -> Result<(Self::Path, bool), Self::Error> {
|
||||
fn force_print_trimmed_def_path(mut self, def_id: DefId) -> Result<(Self, bool), PrintError> {
|
||||
let key = self.tcx().def_key(def_id);
|
||||
let visible_parent_map = self.tcx().visible_parent_map(());
|
||||
let kind = self.tcx().def_kind(def_id);
|
||||
|
@ -378,10 +365,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
}
|
||||
|
||||
/// Try to see if this path can be trimmed to a unique symbol name.
|
||||
fn try_print_trimmed_def_path(
|
||||
mut self,
|
||||
def_id: DefId,
|
||||
) -> Result<(Self::Path, bool), Self::Error> {
|
||||
fn try_print_trimmed_def_path(mut self, def_id: DefId) -> Result<(Self, bool), PrintError> {
|
||||
if FORCE_TRIMMED_PATH.with(|flag| flag.get()) {
|
||||
let (s, trimmed) = self.force_print_trimmed_def_path(def_id)?;
|
||||
if trimmed {
|
||||
|
@ -423,7 +407,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
mut self,
|
||||
def_id: DefId,
|
||||
callers: &mut Vec<DefId>,
|
||||
) -> Result<(Self, bool), Self::Error> {
|
||||
) -> Result<(Self, bool), PrintError> {
|
||||
define_scoped_cx!(self);
|
||||
|
||||
debug!("try_print_visible_def_path: def_id={:?}", def_id);
|
||||
|
@ -595,7 +579,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
self,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
if trait_ref.is_none() {
|
||||
// Inherent impls. Try to print `Foo::bar` for an inherent
|
||||
// impl on `Foo`, but fallback to `<Foo>::bar` if self-type is
|
||||
|
@ -629,10 +613,10 @@ pub trait PrettyPrinter<'tcx>:
|
|||
|
||||
fn pretty_path_append_impl(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self = print_prefix(self)?;
|
||||
|
||||
self.generic_delimiters(|mut cx| {
|
||||
|
@ -648,7 +632,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
})
|
||||
}
|
||||
|
||||
fn pretty_print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
|
||||
fn pretty_print_type(mut self, ty: Ty<'tcx>) -> Result<Self, PrintError> {
|
||||
define_scoped_cx!(self);
|
||||
|
||||
match *ty.kind() {
|
||||
|
@ -919,7 +903,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
mut self,
|
||||
def_id: DefId,
|
||||
args: &'tcx ty::List<ty::GenericArg<'tcx>>,
|
||||
) -> Result<Self::Type, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
let tcx = self.tcx();
|
||||
|
||||
// Grab the "TraitA + TraitB" from `impl TraitA + TraitB`,
|
||||
|
@ -1189,7 +1173,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
fn pretty_print_inherent_projection(
|
||||
self,
|
||||
alias_ty: &ty::AliasTy<'tcx>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
let def_key = self.tcx().def_key(alias_ty.def_id);
|
||||
self.path_generic_args(
|
||||
|cx| {
|
||||
|
@ -1213,7 +1197,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
fn pretty_print_dyn_existential(
|
||||
mut self,
|
||||
predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self::DynExistential, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
// Generate the main trait ref, including associated types.
|
||||
let mut first = true;
|
||||
|
||||
|
@ -1306,7 +1290,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
inputs: &[Ty<'tcx>],
|
||||
c_variadic: bool,
|
||||
output: Ty<'tcx>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
define_scoped_cx!(self);
|
||||
|
||||
p!("(", comma_sep(inputs.iter().copied()));
|
||||
|
@ -1328,7 +1312,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
mut self,
|
||||
ct: ty::Const<'tcx>,
|
||||
print_ty: bool,
|
||||
) -> Result<Self::Const, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
define_scoped_cx!(self);
|
||||
|
||||
if self.should_print_verbose() {
|
||||
|
@ -1404,11 +1388,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
Ok(self)
|
||||
}
|
||||
|
||||
fn pretty_print_const_scalar(
|
||||
self,
|
||||
scalar: Scalar,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Result<Self::Const, Self::Error> {
|
||||
fn pretty_print_const_scalar(self, scalar: Scalar, ty: Ty<'tcx>) -> Result<Self, PrintError> {
|
||||
match scalar {
|
||||
Scalar::Ptr(ptr, _size) => self.pretty_print_const_scalar_ptr(ptr, ty),
|
||||
Scalar::Int(int) => {
|
||||
|
@ -1421,7 +1401,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
mut self,
|
||||
ptr: Pointer,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Result<Self::Const, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
define_scoped_cx!(self);
|
||||
|
||||
let (alloc_id, offset) = ptr.into_parts();
|
||||
|
@ -1483,7 +1463,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
int: ScalarInt,
|
||||
ty: Ty<'tcx>,
|
||||
print_ty: bool,
|
||||
) -> Result<Self::Const, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
define_scoped_cx!(self);
|
||||
|
||||
match ty.kind() {
|
||||
|
@ -1545,7 +1525,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
self,
|
||||
_: Pointer<Prov>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Result<Self::Const, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self.typed_value(
|
||||
|mut this| {
|
||||
this.write_str("&_")?;
|
||||
|
@ -1556,7 +1536,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
)
|
||||
}
|
||||
|
||||
fn pretty_print_byte_str(mut self, byte_str: &'tcx [u8]) -> Result<Self::Const, Self::Error> {
|
||||
fn pretty_print_byte_str(mut self, byte_str: &'tcx [u8]) -> Result<Self, PrintError> {
|
||||
write!(self, "b\"{}\"", byte_str.escape_ascii())?;
|
||||
Ok(self)
|
||||
}
|
||||
|
@ -1566,7 +1546,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
valtree: ty::ValTree<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
print_ty: bool,
|
||||
) -> Result<Self::Const, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
define_scoped_cx!(self);
|
||||
|
||||
if self.should_print_verbose() {
|
||||
|
@ -1689,7 +1669,7 @@ pub trait PrettyPrinter<'tcx>:
|
|||
fn pretty_closure_as_impl(
|
||||
mut self,
|
||||
closure: ty::ClosureArgs<'tcx>,
|
||||
) -> Result<Self::Const, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
let sig = closure.sig();
|
||||
let kind = closure.kind_ty().to_opt_closure_kind().unwrap_or(ty::ClosureKind::Fn);
|
||||
|
||||
|
@ -1862,14 +1842,6 @@ impl fmt::Write for FmtPrinter<'_, '_> {
|
|||
}
|
||||
|
||||
impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
|
||||
type Error = fmt::Error;
|
||||
|
||||
type Path = Self;
|
||||
type Region = Self;
|
||||
type Type = Self;
|
||||
type DynExistential = Self;
|
||||
type Const = Self;
|
||||
|
||||
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
@ -1878,7 +1850,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
|
|||
mut self,
|
||||
def_id: DefId,
|
||||
args: &'tcx [GenericArg<'tcx>],
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
define_scoped_cx!(self);
|
||||
|
||||
if args.is_empty() {
|
||||
|
@ -1933,11 +1905,11 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
|
|||
self.default_print_def_path(def_id, args)
|
||||
}
|
||||
|
||||
fn print_region(self, region: ty::Region<'tcx>) -> Result<Self::Region, Self::Error> {
|
||||
fn print_region(self, region: ty::Region<'tcx>) -> Result<Self, PrintError> {
|
||||
self.pretty_print_region(region)
|
||||
}
|
||||
|
||||
fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
|
||||
fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self, PrintError> {
|
||||
if self.type_length_limit.value_within_limit(self.printed_type_count) {
|
||||
self.printed_type_count += 1;
|
||||
self.pretty_print_type(ty)
|
||||
|
@ -1951,15 +1923,15 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
|
|||
fn print_dyn_existential(
|
||||
self,
|
||||
predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self::DynExistential, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self.pretty_print_dyn_existential(predicates)
|
||||
}
|
||||
|
||||
fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
|
||||
fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self, PrintError> {
|
||||
self.pretty_print_const(ct, false)
|
||||
}
|
||||
|
||||
fn path_crate(mut self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
|
||||
fn path_crate(mut self, cnum: CrateNum) -> Result<Self, PrintError> {
|
||||
self.empty_path = true;
|
||||
if cnum == LOCAL_CRATE {
|
||||
if self.tcx.sess.at_least_rust_2018() {
|
||||
|
@ -1980,7 +1952,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
|
|||
mut self,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self = self.pretty_path_qualified(self_ty, trait_ref)?;
|
||||
self.empty_path = false;
|
||||
Ok(self)
|
||||
|
@ -1988,11 +1960,11 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
|
|||
|
||||
fn path_append_impl(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
_disambiguated_data: &DisambiguatedDefPathData,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self = self.pretty_path_append_impl(
|
||||
|mut cx| {
|
||||
cx = print_prefix(cx)?;
|
||||
|
@ -2011,9 +1983,9 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
|
|||
|
||||
fn path_append(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self = print_prefix(self)?;
|
||||
|
||||
// Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs.
|
||||
|
@ -2042,9 +2014,9 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> {
|
|||
|
||||
fn path_generic_args(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
args: &[GenericArg<'tcx>],
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self = print_prefix(self)?;
|
||||
|
||||
let tcx = self.tcx;
|
||||
|
@ -2101,7 +2073,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
|
|||
mut self,
|
||||
def_id: DefId,
|
||||
args: &'tcx [GenericArg<'tcx>],
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
let was_in_value = std::mem::replace(&mut self.in_value, true);
|
||||
self = self.print_def_path(def_id, args)?;
|
||||
self.in_value = was_in_value;
|
||||
|
@ -2109,30 +2081,30 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
|
|||
Ok(self)
|
||||
}
|
||||
|
||||
fn in_binder<T>(self, value: &ty::Binder<'tcx, T>) -> Result<Self, Self::Error>
|
||||
fn in_binder<T>(self, value: &ty::Binder<'tcx, T>) -> Result<Self, PrintError>
|
||||
where
|
||||
T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
self.pretty_in_binder(value)
|
||||
}
|
||||
|
||||
fn wrap_binder<T, C: FnOnce(&T, Self) -> Result<Self, Self::Error>>(
|
||||
fn wrap_binder<T, C: FnOnce(&T, Self) -> Result<Self, PrintError>>(
|
||||
self,
|
||||
value: &ty::Binder<'tcx, T>,
|
||||
f: C,
|
||||
) -> Result<Self, Self::Error>
|
||||
) -> Result<Self, PrintError>
|
||||
where
|
||||
T: Print<'tcx, Self, Output = Self, Error = Self::Error> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
self.pretty_wrap_binder(value, f)
|
||||
}
|
||||
|
||||
fn typed_value(
|
||||
mut self,
|
||||
f: impl FnOnce(Self) -> Result<Self, Self::Error>,
|
||||
t: impl FnOnce(Self) -> Result<Self, Self::Error>,
|
||||
f: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
t: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
conversion: &str,
|
||||
) -> Result<Self::Const, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self.write_str("{")?;
|
||||
self = f(self)?;
|
||||
self.write_str(conversion)?;
|
||||
|
@ -2145,8 +2117,8 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
|
|||
|
||||
fn generic_delimiters(
|
||||
mut self,
|
||||
f: impl FnOnce(Self) -> Result<Self, Self::Error>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
f: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
) -> Result<Self, PrintError> {
|
||||
write!(self, "<")?;
|
||||
|
||||
let was_in_value = std::mem::replace(&mut self.in_value, false);
|
||||
|
@ -2206,7 +2178,7 @@ impl<'tcx> PrettyPrinter<'tcx> for FmtPrinter<'_, 'tcx> {
|
|||
self,
|
||||
p: Pointer<Prov>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> Result<Self::Const, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
let print = |mut this: Self| {
|
||||
define_scoped_cx!(this);
|
||||
if this.print_alloc_ids {
|
||||
|
@ -2371,7 +2343,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
|
|||
value: &ty::Binder<'tcx, T>,
|
||||
) -> Result<(Self, T, BTreeMap<ty::BoundRegion, ty::Region<'tcx>>), fmt::Error>
|
||||
where
|
||||
T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
fn name_by_region_index(
|
||||
index: usize,
|
||||
|
@ -2541,7 +2513,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
|
|||
|
||||
pub fn pretty_in_binder<T>(self, value: &ty::Binder<'tcx, T>) -> Result<Self, fmt::Error>
|
||||
where
|
||||
T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
let old_region_index = self.region_index;
|
||||
let (new, new_value, _) = self.name_all_regions(value)?;
|
||||
|
@ -2557,7 +2529,7 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
|
|||
f: C,
|
||||
) -> Result<Self, fmt::Error>
|
||||
where
|
||||
T: Print<'tcx, Self, Output = Self, Error = fmt::Error> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
T: Print<'tcx, Self> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
let old_region_index = self.region_index;
|
||||
let (new, new_value, _) = self.name_all_regions(value)?;
|
||||
|
@ -2622,24 +2594,19 @@ impl<'tcx> FmtPrinter<'_, 'tcx> {
|
|||
|
||||
impl<'tcx, T, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::Binder<'tcx, T>
|
||||
where
|
||||
T: Print<'tcx, P, Output = P, Error = P::Error> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
T: Print<'tcx, P> + TypeFoldable<TyCtxt<'tcx>>,
|
||||
{
|
||||
type Output = P;
|
||||
type Error = P::Error;
|
||||
|
||||
fn print(&self, cx: P) -> Result<Self::Output, Self::Error> {
|
||||
fn print(&self, cx: P) -> Result<P, PrintError> {
|
||||
cx.in_binder(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T, U, P: PrettyPrinter<'tcx>> Print<'tcx, P> for ty::OutlivesPredicate<T, U>
|
||||
where
|
||||
T: Print<'tcx, P, Output = P, Error = P::Error>,
|
||||
U: Print<'tcx, P, Output = P, Error = P::Error>,
|
||||
T: Print<'tcx, P>,
|
||||
U: Print<'tcx, P>,
|
||||
{
|
||||
type Output = P;
|
||||
type Error = P::Error;
|
||||
fn print(&self, mut cx: P) -> Result<Self::Output, Self::Error> {
|
||||
fn print(&self, mut cx: P) -> Result<P, PrintError> {
|
||||
define_scoped_cx!(cx);
|
||||
p!(print(self.0), ": ", print(self.1));
|
||||
Ok(cx)
|
||||
|
@ -2666,9 +2633,7 @@ macro_rules! forward_display_to_print {
|
|||
macro_rules! define_print_and_forward_display {
|
||||
(($self:ident, $cx:ident): $($ty:ty $print:block)+) => {
|
||||
$(impl<'tcx, P: PrettyPrinter<'tcx>> Print<'tcx, P> for $ty {
|
||||
type Output = P;
|
||||
type Error = fmt::Error;
|
||||
fn print(&$self, $cx: P) -> Result<Self::Output, Self::Error> {
|
||||
fn print(&$self, $cx: P) -> Result<P, PrintError> {
|
||||
#[allow(unused_mut)]
|
||||
let mut $cx = $cx;
|
||||
define_scoped_cx!($cx);
|
||||
|
|
|
@ -288,7 +288,7 @@ impl<'tcx> Relate<'tcx> for ty::AliasTy<'tcx> {
|
|||
}
|
||||
def => bug!("unknown alias DefKind: {def:?}"),
|
||||
};
|
||||
Ok(relation.tcx().mk_alias_ty(a.def_id, args))
|
||||
Ok(ty::AliasTy::new(relation.tcx(), a.def_id, args))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1213,11 +1213,20 @@ pub struct AliasTy<'tcx> {
|
|||
pub def_id: DefId,
|
||||
|
||||
/// This field exists to prevent the creation of `AliasTy` without using
|
||||
/// [TyCtxt::mk_alias_ty].
|
||||
pub(super) _use_mk_alias_ty_instead: (),
|
||||
/// [AliasTy::new].
|
||||
_use_alias_ty_new_instead: (),
|
||||
}
|
||||
|
||||
impl<'tcx> AliasTy<'tcx> {
|
||||
pub fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
args: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
|
||||
) -> ty::AliasTy<'tcx> {
|
||||
let args = tcx.check_and_mk_args(def_id, args);
|
||||
ty::AliasTy { def_id, args, _use_alias_ty_new_instead: () }
|
||||
}
|
||||
|
||||
pub fn kind(self, tcx: TyCtxt<'tcx>) -> ty::AliasKind {
|
||||
match tcx.def_kind(self.def_id) {
|
||||
DefKind::AssocTy
|
||||
|
@ -1245,7 +1254,7 @@ impl<'tcx> AliasTy<'tcx> {
|
|||
}
|
||||
|
||||
pub fn with_self_ty(self, tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>) -> Self {
|
||||
tcx.mk_alias_ty(self.def_id, [self_ty.into()].into_iter().chain(self.args.iter().skip(1)))
|
||||
AliasTy::new(tcx, self.def_id, [self_ty.into()].into_iter().chain(self.args.iter().skip(1)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1667,8 +1676,11 @@ impl<'tcx> ExistentialProjection<'tcx> {
|
|||
debug_assert!(!self_ty.has_escaping_bound_vars());
|
||||
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: tcx
|
||||
.mk_alias_ty(self.def_id, [self_ty.into()].into_iter().chain(self.args)),
|
||||
projection_ty: AliasTy::new(
|
||||
tcx,
|
||||
self.def_id,
|
||||
[self_ty.into()].into_iter().chain(self.args),
|
||||
),
|
||||
term: self.term,
|
||||
}
|
||||
}
|
||||
|
@ -1971,7 +1983,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
|
||||
#[inline]
|
||||
pub fn new_opaque(tcx: TyCtxt<'tcx>, def_id: DefId, args: GenericArgsRef<'tcx>) -> Ty<'tcx> {
|
||||
Ty::new_alias(tcx, ty::Opaque, tcx.mk_alias_ty(def_id, args))
|
||||
Ty::new_alias(tcx, ty::Opaque, AliasTy::new(tcx, def_id, args))
|
||||
}
|
||||
|
||||
/// Constructs a `TyKind::Error` type with current `ErrorGuaranteed`
|
||||
|
@ -2135,7 +2147,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
item_def_id: DefId,
|
||||
args: impl IntoIterator<Item: Into<GenericArg<'tcx>>>,
|
||||
) -> Ty<'tcx> {
|
||||
Ty::new_alias(tcx, ty::Projection, tcx.mk_alias_ty(item_def_id, args))
|
||||
Ty::new_alias(tcx, ty::Projection, AliasTy::new(tcx, item_def_id, args))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
|
@ -60,6 +60,7 @@ pub(super) fn build_custom_mir<'tcx>(
|
|||
tainted_by_errors: None,
|
||||
injection_phase: None,
|
||||
pass_count: 0,
|
||||
function_coverage_info: None,
|
||||
};
|
||||
|
||||
body.local_decls.push(LocalDecl::new(return_ty, return_ty_span));
|
||||
|
|
|
@ -113,6 +113,6 @@ impl<'tcx> MirPass<'tcx> for AbortUnwindingCalls {
|
|||
}
|
||||
|
||||
// We may have invalidated some `cleanup` blocks so clean those up now.
|
||||
super::simplify::remove_dead_blocks(tcx, body);
|
||||
super::simplify::remove_dead_blocks(body);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ const NESTED_INDENT: &str = " ";
|
|||
#[derive(Clone)]
|
||||
pub(super) enum BcbCounter {
|
||||
Counter { id: CounterId },
|
||||
Expression { id: ExpressionId, lhs: Operand, op: Op, rhs: Operand },
|
||||
Expression { id: ExpressionId },
|
||||
}
|
||||
|
||||
impl BcbCounter {
|
||||
|
@ -27,10 +27,10 @@ impl BcbCounter {
|
|||
matches!(self, Self::Expression { .. })
|
||||
}
|
||||
|
||||
pub(super) fn as_operand(&self) -> Operand {
|
||||
pub(super) fn as_term(&self) -> CovTerm {
|
||||
match *self {
|
||||
BcbCounter::Counter { id, .. } => Operand::Counter(id),
|
||||
BcbCounter::Expression { id, .. } => Operand::Expression(id),
|
||||
BcbCounter::Counter { id, .. } => CovTerm::Counter(id),
|
||||
BcbCounter::Expression { id, .. } => CovTerm::Expression(id),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,17 +39,7 @@ impl Debug for BcbCounter {
|
|||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Counter { id, .. } => write!(fmt, "Counter({:?})", id.index()),
|
||||
Self::Expression { id, lhs, op, rhs } => write!(
|
||||
fmt,
|
||||
"Expression({:?}) = {:?} {} {:?}",
|
||||
id.index(),
|
||||
lhs,
|
||||
match op {
|
||||
Op::Add => "+",
|
||||
Op::Subtract => "-",
|
||||
},
|
||||
rhs,
|
||||
),
|
||||
Self::Expression { id } => write!(fmt, "Expression({:?})", id.index()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +48,6 @@ impl Debug for BcbCounter {
|
|||
/// associated with nodes/edges in the BCB graph.
|
||||
pub(super) struct CoverageCounters {
|
||||
next_counter_id: CounterId,
|
||||
next_expression_id: ExpressionId,
|
||||
|
||||
/// Coverage counters/expressions that are associated with individual BCBs.
|
||||
bcb_counters: IndexVec<BasicCoverageBlock, Option<BcbCounter>>,
|
||||
|
@ -69,10 +58,9 @@ pub(super) struct CoverageCounters {
|
|||
/// Only used by debug assertions, to verify that BCBs with incoming edge
|
||||
/// counters do not have their own physical counters (expressions are allowed).
|
||||
bcb_has_incoming_edge_counters: BitSet<BasicCoverageBlock>,
|
||||
/// Expression nodes that are not directly associated with any particular
|
||||
/// BCB/edge, but are needed as operands to more complex expressions.
|
||||
/// These are always [`BcbCounter::Expression`].
|
||||
pub(super) intermediate_expressions: Vec<BcbCounter>,
|
||||
/// Table of expression data, associating each expression ID with its
|
||||
/// corresponding operator (+ or -) and its LHS/RHS operands.
|
||||
expressions: IndexVec<ExpressionId, Expression>,
|
||||
}
|
||||
|
||||
impl CoverageCounters {
|
||||
|
@ -81,12 +69,10 @@ impl CoverageCounters {
|
|||
|
||||
Self {
|
||||
next_counter_id: CounterId::START,
|
||||
next_expression_id: ExpressionId::START,
|
||||
|
||||
bcb_counters: IndexVec::from_elem_n(None, num_bcbs),
|
||||
bcb_edge_counters: FxHashMap::default(),
|
||||
bcb_has_incoming_edge_counters: BitSet::new_empty(num_bcbs),
|
||||
intermediate_expressions: Vec::new(),
|
||||
expressions: IndexVec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -106,9 +92,9 @@ impl CoverageCounters {
|
|||
BcbCounter::Counter { id }
|
||||
}
|
||||
|
||||
fn make_expression(&mut self, lhs: Operand, op: Op, rhs: Operand) -> BcbCounter {
|
||||
let id = self.next_expression();
|
||||
BcbCounter::Expression { id, lhs, op, rhs }
|
||||
fn make_expression(&mut self, lhs: CovTerm, op: Op, rhs: CovTerm) -> BcbCounter {
|
||||
let id = self.expressions.push(Expression { lhs, op, rhs });
|
||||
BcbCounter::Expression { id }
|
||||
}
|
||||
|
||||
/// Counter IDs start from one and go up.
|
||||
|
@ -118,19 +104,20 @@ impl CoverageCounters {
|
|||
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 = self.next_expression_id + 1;
|
||||
next
|
||||
pub(super) fn num_counters(&self) -> usize {
|
||||
self.next_counter_id.as_usize()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub(super) fn num_expressions(&self) -> usize {
|
||||
self.expressions.len()
|
||||
}
|
||||
|
||||
fn set_bcb_counter(
|
||||
&mut self,
|
||||
bcb: BasicCoverageBlock,
|
||||
counter_kind: BcbCounter,
|
||||
) -> Result<Operand, Error> {
|
||||
) -> Result<CovTerm, 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
|
||||
|
@ -138,14 +125,14 @@ impl CoverageCounters {
|
|||
counter_kind.is_expression() || !self.bcb_has_incoming_edge_counters.contains(bcb),
|
||||
"attempt to add a `Counter` to a BCB target with existing incoming edge counters"
|
||||
);
|
||||
let operand = counter_kind.as_operand();
|
||||
let term = counter_kind.as_term();
|
||||
if let Some(replaced) = self.bcb_counters[bcb].replace(counter_kind) {
|
||||
Error::from_string(format!(
|
||||
"attempt to set a BasicCoverageBlock coverage counter more than once; \
|
||||
{bcb:?} already had counter {replaced:?}",
|
||||
))
|
||||
} else {
|
||||
Ok(operand)
|
||||
Ok(term)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -154,7 +141,7 @@ impl CoverageCounters {
|
|||
from_bcb: BasicCoverageBlock,
|
||||
to_bcb: BasicCoverageBlock,
|
||||
counter_kind: BcbCounter,
|
||||
) -> Result<Operand, Error> {
|
||||
) -> Result<CovTerm, 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
|
||||
|
@ -167,14 +154,14 @@ impl CoverageCounters {
|
|||
}
|
||||
}
|
||||
self.bcb_has_incoming_edge_counters.insert(to_bcb);
|
||||
let operand = counter_kind.as_operand();
|
||||
let term = counter_kind.as_term();
|
||||
if let Some(replaced) = self.bcb_edge_counters.insert((from_bcb, to_bcb), counter_kind) {
|
||||
Error::from_string(format!(
|
||||
"attempt to set an edge counter more than once; from_bcb: \
|
||||
{from_bcb:?} already had counter {replaced:?}",
|
||||
))
|
||||
} else {
|
||||
Ok(operand)
|
||||
Ok(term)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -199,6 +186,10 @@ impl CoverageCounters {
|
|||
) -> impl Iterator<Item = ((BasicCoverageBlock, BasicCoverageBlock), BcbCounter)> + '_ {
|
||||
self.bcb_edge_counters.drain()
|
||||
}
|
||||
|
||||
pub(super) fn take_expressions(&mut self) -> IndexVec<ExpressionId, Expression> {
|
||||
std::mem::take(&mut self.expressions)
|
||||
}
|
||||
}
|
||||
|
||||
/// Traverse the `CoverageGraph` and add either a `Counter` or `Expression` to every BCB, to be
|
||||
|
@ -276,7 +267,7 @@ impl<'a> MakeBcbCounters<'a> {
|
|||
&mut self,
|
||||
traversal: &TraverseCoverageGraphWithLoops<'_>,
|
||||
branching_bcb: BasicCoverageBlock,
|
||||
branching_counter_operand: Operand,
|
||||
branching_counter_operand: CovTerm,
|
||||
) -> Result<(), Error> {
|
||||
let branches = self.bcb_branches(branching_bcb);
|
||||
debug!(
|
||||
|
@ -324,8 +315,7 @@ impl<'a> MakeBcbCounters<'a> {
|
|||
sumup_counter_operand,
|
||||
);
|
||||
debug!(" [new intermediate expression: {:?}]", intermediate_expression);
|
||||
let intermediate_expression_operand = intermediate_expression.as_operand();
|
||||
self.coverage_counters.intermediate_expressions.push(intermediate_expression);
|
||||
let intermediate_expression_operand = intermediate_expression.as_term();
|
||||
some_sumup_counter_operand.replace(intermediate_expression_operand);
|
||||
}
|
||||
}
|
||||
|
@ -356,7 +346,7 @@ impl<'a> MakeBcbCounters<'a> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_or_make_counter_operand(&mut self, bcb: BasicCoverageBlock) -> Result<Operand, Error> {
|
||||
fn get_or_make_counter_operand(&mut self, bcb: BasicCoverageBlock) -> Result<CovTerm, Error> {
|
||||
self.recursive_get_or_make_counter_operand(bcb, 1)
|
||||
}
|
||||
|
||||
|
@ -364,7 +354,7 @@ impl<'a> MakeBcbCounters<'a> {
|
|||
&mut self,
|
||||
bcb: BasicCoverageBlock,
|
||||
debug_indent_level: usize,
|
||||
) -> Result<Operand, Error> {
|
||||
) -> Result<CovTerm, Error> {
|
||||
// If the BCB already has a counter, return it.
|
||||
if let Some(counter_kind) = &self.coverage_counters.bcb_counters[bcb] {
|
||||
debug!(
|
||||
|
@ -373,7 +363,7 @@ impl<'a> MakeBcbCounters<'a> {
|
|||
bcb,
|
||||
counter_kind,
|
||||
);
|
||||
return Ok(counter_kind.as_operand());
|
||||
return Ok(counter_kind.as_term());
|
||||
}
|
||||
|
||||
// A BCB with only one incoming edge gets a simple `Counter` (via `make_counter()`).
|
||||
|
@ -437,8 +427,7 @@ impl<'a> MakeBcbCounters<'a> {
|
|||
NESTED_INDENT.repeat(debug_indent_level),
|
||||
intermediate_expression
|
||||
);
|
||||
let intermediate_expression_operand = intermediate_expression.as_operand();
|
||||
self.coverage_counters.intermediate_expressions.push(intermediate_expression);
|
||||
let intermediate_expression_operand = intermediate_expression.as_term();
|
||||
some_sumup_edge_counter_operand.replace(intermediate_expression_operand);
|
||||
}
|
||||
}
|
||||
|
@ -460,7 +449,7 @@ impl<'a> MakeBcbCounters<'a> {
|
|||
&mut self,
|
||||
from_bcb: BasicCoverageBlock,
|
||||
to_bcb: BasicCoverageBlock,
|
||||
) -> Result<Operand, Error> {
|
||||
) -> Result<CovTerm, Error> {
|
||||
self.recursive_get_or_make_edge_counter_operand(from_bcb, to_bcb, 1)
|
||||
}
|
||||
|
||||
|
@ -469,7 +458,7 @@ impl<'a> MakeBcbCounters<'a> {
|
|||
from_bcb: BasicCoverageBlock,
|
||||
to_bcb: BasicCoverageBlock,
|
||||
debug_indent_level: usize,
|
||||
) -> Result<Operand, Error> {
|
||||
) -> Result<CovTerm, 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();
|
||||
|
@ -488,7 +477,7 @@ impl<'a> MakeBcbCounters<'a> {
|
|||
to_bcb,
|
||||
counter_kind
|
||||
);
|
||||
return Ok(counter_kind.as_operand());
|
||||
return Ok(counter_kind.as_term());
|
||||
}
|
||||
|
||||
// Make a new counter to count this edge.
|
||||
|
|
|
@ -104,6 +104,7 @@ struct Instrumentor<'a, 'tcx> {
|
|||
function_source_hash: u64,
|
||||
basic_coverage_blocks: CoverageGraph,
|
||||
coverage_counters: CoverageCounters,
|
||||
mappings: Vec<Mapping>,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
||||
|
@ -144,6 +145,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
|||
function_source_hash,
|
||||
basic_coverage_blocks,
|
||||
coverage_counters,
|
||||
mappings: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,9 +167,6 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
|||
// every coverage span has a `Counter` or `Expression` assigned to its `BasicCoverageBlock`
|
||||
// and all `Expression` dependencies (operands) are also generated, for any other
|
||||
// `BasicCoverageBlock`s not already associated with a coverage span.
|
||||
//
|
||||
// Intermediate expressions (used to compute other `Expression` values), which have no
|
||||
// direct association with any `BasicCoverageBlock`, are accumulated inside `coverage_counters`.
|
||||
let bcb_has_coverage_spans = |bcb| coverage_spans.bcb_has_coverage_spans(bcb);
|
||||
let result = self
|
||||
.coverage_counters
|
||||
|
@ -193,24 +192,18 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
|||
// are in fact counted, even though they don't directly contribute to counting
|
||||
// their own independent code region's coverage.
|
||||
self.inject_indirect_counters();
|
||||
|
||||
// Intermediate expressions will be injected as the final step, after generating
|
||||
// debug output, if any.
|
||||
////////////////////////////////////////////////////
|
||||
};
|
||||
|
||||
if let Err(e) = result {
|
||||
bug!("Error processing: {:?}: {:?}", self.mir_body.source.def_id(), e.message)
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// Finally, inject the intermediate expressions collected along the way.
|
||||
for intermediate_expression in &self.coverage_counters.intermediate_expressions {
|
||||
inject_intermediate_expression(
|
||||
self.mir_body,
|
||||
self.make_mir_coverage_kind(intermediate_expression),
|
||||
);
|
||||
}
|
||||
self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
|
||||
function_source_hash: self.function_source_hash,
|
||||
num_counters: self.coverage_counters.num_counters(),
|
||||
expressions: self.coverage_counters.take_expressions(),
|
||||
mappings: std::mem::take(&mut self.mappings),
|
||||
}));
|
||||
}
|
||||
|
||||
/// Injects a single [`StatementKind::Coverage`] for each BCB that has one
|
||||
|
@ -229,18 +222,16 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
|||
bug!("Every BasicCoverageBlock should have a Counter or Expression");
|
||||
});
|
||||
|
||||
// Convert the coverage spans into a vector of code regions to be
|
||||
// associated with this BCB's coverage statement.
|
||||
let code_regions = spans
|
||||
.iter()
|
||||
.map(|&span| make_code_region(source_map, file_name, span, body_span))
|
||||
.collect::<Vec<_>>();
|
||||
let term = counter_kind.as_term();
|
||||
self.mappings.extend(spans.iter().map(|&span| {
|
||||
let code_region = make_code_region(source_map, file_name, span, body_span);
|
||||
Mapping { code_region, term }
|
||||
}));
|
||||
|
||||
inject_statement(
|
||||
self.mir_body,
|
||||
self.make_mir_coverage_kind(&counter_kind),
|
||||
self.bcb_leader_bb(bcb),
|
||||
code_regions,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -298,13 +289,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
|||
self.mir_body,
|
||||
self.make_mir_coverage_kind(&counter_kind),
|
||||
inject_to_bb,
|
||||
Vec::new(),
|
||||
);
|
||||
}
|
||||
BcbCounter::Expression { .. } => inject_intermediate_expression(
|
||||
self.mir_body,
|
||||
self.make_mir_coverage_kind(&counter_kind),
|
||||
),
|
||||
// Experessions with no associated spans don't need to inject a statement.
|
||||
BcbCounter::Expression { .. } => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -326,12 +314,8 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
|||
|
||||
fn make_mir_coverage_kind(&self, counter_kind: &BcbCounter) -> CoverageKind {
|
||||
match *counter_kind {
|
||||
BcbCounter::Counter { id } => {
|
||||
CoverageKind::Counter { function_source_hash: self.function_source_hash, id }
|
||||
}
|
||||
BcbCounter::Expression { id, lhs, op, rhs } => {
|
||||
CoverageKind::Expression { id, lhs, op, rhs }
|
||||
}
|
||||
BcbCounter::Counter { id } => CoverageKind::CounterIncrement { id },
|
||||
BcbCounter::Expression { id } => CoverageKind::ExpressionUsed { id },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -359,39 +343,17 @@ fn inject_edge_counter_basic_block(
|
|||
new_bb
|
||||
}
|
||||
|
||||
fn inject_statement(
|
||||
mir_body: &mut mir::Body<'_>,
|
||||
counter_kind: CoverageKind,
|
||||
bb: BasicBlock,
|
||||
code_regions: Vec<CodeRegion>,
|
||||
) {
|
||||
debug!(" injecting statement {counter_kind:?} for {bb:?} at code regions: {code_regions:?}");
|
||||
fn inject_statement(mir_body: &mut mir::Body<'_>, counter_kind: CoverageKind, bb: BasicBlock) {
|
||||
debug!(" injecting statement {counter_kind:?} for {bb:?}");
|
||||
let data = &mut mir_body[bb];
|
||||
let source_info = data.terminator().source_info;
|
||||
let statement = Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Coverage(Box::new(Coverage { kind: counter_kind, code_regions })),
|
||||
kind: StatementKind::Coverage(Box::new(Coverage { kind: counter_kind })),
|
||||
};
|
||||
data.statements.insert(0, statement);
|
||||
}
|
||||
|
||||
// Non-code expressions are injected into the coverage map, without generating executable code.
|
||||
fn inject_intermediate_expression(mir_body: &mut mir::Body<'_>, expression: CoverageKind) {
|
||||
debug_assert!(matches!(expression, CoverageKind::Expression { .. }));
|
||||
debug!(" injecting non-code expression {:?}", expression);
|
||||
let inject_in_bb = mir::START_BLOCK;
|
||||
let data = &mut mir_body[inject_in_bb];
|
||||
let source_info = data.terminator().source_info;
|
||||
let statement = Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Coverage(Box::new(Coverage {
|
||||
kind: expression,
|
||||
code_regions: Vec::new(),
|
||||
})),
|
||||
};
|
||||
data.statements.push(statement);
|
||||
}
|
||||
|
||||
/// Convert the Span into its file name, start line and column, and end line and column
|
||||
fn make_code_region(
|
||||
source_map: &SourceMap,
|
||||
|
|
|
@ -2,100 +2,31 @@ use super::*;
|
|||
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_middle::mir::coverage::*;
|
||||
use rustc_middle::mir::{self, Body, Coverage, CoverageInfo};
|
||||
use rustc_middle::mir::{Body, Coverage, CoverageIdsInfo};
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::{self, TyCtxt};
|
||||
use rustc_span::def_id::DefId;
|
||||
|
||||
/// A `query` provider for retrieving coverage information injected into MIR.
|
||||
pub(crate) fn provide(providers: &mut Providers) {
|
||||
providers.coverageinfo = |tcx, def_id| coverageinfo(tcx, def_id);
|
||||
providers.covered_code_regions = |tcx, def_id| covered_code_regions(tcx, def_id);
|
||||
providers.coverage_ids_info = |tcx, def_id| coverage_ids_info(tcx, def_id);
|
||||
}
|
||||
|
||||
/// Coverage codegen needs to know the total number of counter IDs and expression IDs that have
|
||||
/// been used by a function's coverage mappings. These totals are used to create vectors to hold
|
||||
/// the relevant counter and expression data, and the maximum counter ID (+ 1) is also needed by
|
||||
/// the `llvm.instrprof.increment` intrinsic.
|
||||
///
|
||||
/// MIR optimization may split and duplicate some BasicBlock sequences, or optimize out some code
|
||||
/// including injected counters. (It is OK if some counters are optimized out, but those counters
|
||||
/// are still included in the total `num_counters` or `num_expressions`.) Simply counting the
|
||||
/// calls may not work; but computing the number of counters or expressions by adding `1` to the
|
||||
/// highest ID (for a given instrumented function) is valid.
|
||||
///
|
||||
/// It's possible for a coverage expression to remain in MIR while one or both of its operands
|
||||
/// have been optimized away. To avoid problems in codegen, we include those operands' IDs when
|
||||
/// determining the maximum counter/expression ID, even if the underlying counter/expression is
|
||||
/// no longer present.
|
||||
struct CoverageVisitor {
|
||||
max_counter_id: CounterId,
|
||||
max_expression_id: ExpressionId,
|
||||
}
|
||||
|
||||
impl CoverageVisitor {
|
||||
/// Updates `max_counter_id` to the maximum encountered counter ID.
|
||||
#[inline(always)]
|
||||
fn update_max_counter_id(&mut self, counter_id: CounterId) {
|
||||
self.max_counter_id = self.max_counter_id.max(counter_id);
|
||||
}
|
||||
|
||||
/// Updates `max_expression_id` to the maximum encountered expression ID.
|
||||
#[inline(always)]
|
||||
fn update_max_expression_id(&mut self, expression_id: ExpressionId) {
|
||||
self.max_expression_id = self.max_expression_id.max(expression_id);
|
||||
}
|
||||
|
||||
fn update_from_expression_operand(&mut self, operand: Operand) {
|
||||
match operand {
|
||||
Operand::Counter(id) => self.update_max_counter_id(id),
|
||||
Operand::Expression(id) => self.update_max_expression_id(id),
|
||||
Operand::Zero => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_body(&mut self, body: &Body<'_>) {
|
||||
for coverage in all_coverage_in_mir_body(body) {
|
||||
self.visit_coverage(coverage);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_coverage(&mut self, coverage: &Coverage) {
|
||||
match coverage.kind {
|
||||
CoverageKind::Counter { id, .. } => self.update_max_counter_id(id),
|
||||
CoverageKind::Expression { id, lhs, rhs, .. } => {
|
||||
self.update_max_expression_id(id);
|
||||
self.update_from_expression_operand(lhs);
|
||||
self.update_from_expression_operand(rhs);
|
||||
}
|
||||
CoverageKind::Unreachable => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn coverageinfo<'tcx>(tcx: TyCtxt<'tcx>, instance_def: ty::InstanceDef<'tcx>) -> CoverageInfo {
|
||||
/// Query implementation for `coverage_ids_info`.
|
||||
fn coverage_ids_info<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
instance_def: ty::InstanceDef<'tcx>,
|
||||
) -> CoverageIdsInfo {
|
||||
let mir_body = tcx.instance_mir(instance_def);
|
||||
|
||||
let mut coverage_visitor = CoverageVisitor {
|
||||
max_counter_id: CounterId::START,
|
||||
max_expression_id: ExpressionId::START,
|
||||
};
|
||||
let max_counter_id = all_coverage_in_mir_body(mir_body)
|
||||
.filter_map(|coverage| match coverage.kind {
|
||||
CoverageKind::CounterIncrement { id } => Some(id),
|
||||
_ => None,
|
||||
})
|
||||
.max()
|
||||
.unwrap_or(CounterId::START);
|
||||
|
||||
coverage_visitor.visit_body(mir_body);
|
||||
|
||||
// Add 1 to the highest IDs to get the total number of IDs.
|
||||
CoverageInfo {
|
||||
num_counters: (coverage_visitor.max_counter_id + 1).as_u32(),
|
||||
num_expressions: (coverage_visitor.max_expression_id + 1).as_u32(),
|
||||
}
|
||||
}
|
||||
|
||||
fn covered_code_regions(tcx: TyCtxt<'_>, def_id: DefId) -> Vec<&CodeRegion> {
|
||||
let body = mir_body(tcx, def_id);
|
||||
all_coverage_in_mir_body(body)
|
||||
// Coverage statements have a list of code regions (possibly empty).
|
||||
.flat_map(|coverage| coverage.code_regions.as_slice())
|
||||
.collect()
|
||||
CoverageIdsInfo { max_counter_id }
|
||||
}
|
||||
|
||||
fn all_coverage_in_mir_body<'a, 'tcx>(
|
||||
|
@ -115,11 +46,3 @@ fn is_inlined(body: &Body<'_>, statement: &Statement<'_>) -> bool {
|
|||
let scope_data = &body.source_scopes[statement.source_info.scope];
|
||||
scope_data.inlined.is_some() || scope_data.inlined_parent_scope.is_some()
|
||||
}
|
||||
|
||||
/// This function ensures we obtain the correct MIR for the given item irrespective of
|
||||
/// whether that means const mir or runtime mir. For `const fn` this opts for runtime
|
||||
/// mir.
|
||||
fn mir_body(tcx: TyCtxt<'_>, def_id: DefId) -> &mir::Body<'_> {
|
||||
let def = ty::InstanceDef::Item(def_id);
|
||||
tcx.instance_mir(def)
|
||||
}
|
||||
|
|
|
@ -404,7 +404,7 @@ impl<'a> CoverageSpansGenerator<'a> {
|
|||
|
||||
let Some(visible_macro) = curr.visible_macro(self.body_span) else { return };
|
||||
if let Some(prev) = &self.some_prev
|
||||
&& prev.expn_span.ctxt() == curr.expn_span.ctxt()
|
||||
&& prev.expn_span.eq_ctxt(curr.expn_span)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -656,7 +656,7 @@ fn test_make_bcb_counters() {
|
|||
coverage_counters
|
||||
.make_bcb_counters(&mut basic_coverage_blocks, bcb_has_coverage_spans)
|
||||
.expect("should be Ok");
|
||||
assert_eq!(coverage_counters.intermediate_expressions.len(), 0);
|
||||
assert_eq!(coverage_counters.num_expressions(), 0);
|
||||
|
||||
let_bcb!(1);
|
||||
assert_eq!(
|
||||
|
|
119
compiler/rustc_mir_transform/src/cross_crate_inline.rs
Normal file
119
compiler/rustc_mir_transform/src/cross_crate_inline.rs
Normal file
|
@ -0,0 +1,119 @@
|
|||
use rustc_attr::InlineAttr;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_middle::mir::visit::Visitor;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::query::Providers;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_session::config::OptLevel;
|
||||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
providers.cross_crate_inlinable = cross_crate_inlinable;
|
||||
}
|
||||
|
||||
fn cross_crate_inlinable(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id);
|
||||
// If this has an extern indicator, then this function is globally shared and thus will not
|
||||
// generate cgu-internal copies which would make it cross-crate inlinable.
|
||||
if codegen_fn_attrs.contains_extern_indicator() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Obey source annotations first; this is important because it means we can use
|
||||
// #[inline(never)] to force code generation.
|
||||
match codegen_fn_attrs.inline {
|
||||
InlineAttr::Never => return false,
|
||||
InlineAttr::Hint | InlineAttr::Always => return true,
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// This just reproduces the logic from Instance::requires_inline.
|
||||
match tcx.def_kind(def_id) {
|
||||
DefKind::Ctor(..) | DefKind::Closure => return true,
|
||||
DefKind::Fn | DefKind::AssocFn => {}
|
||||
_ => return false,
|
||||
}
|
||||
|
||||
// Don't do any inference when incremental compilation is enabled; the additional inlining that
|
||||
// inference permits also creates more work for small edits.
|
||||
if tcx.sess.opts.incremental.is_some() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't do any inference unless optimizations are enabled.
|
||||
if matches!(tcx.sess.opts.optimize, OptLevel::No) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if !tcx.is_mir_available(def_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mir = tcx.optimized_mir(def_id);
|
||||
let mut checker =
|
||||
CostChecker { tcx, callee_body: mir, calls: 0, statements: 0, landing_pads: 0, resumes: 0 };
|
||||
checker.visit_body(mir);
|
||||
checker.calls == 0
|
||||
&& checker.resumes == 0
|
||||
&& checker.landing_pads == 0
|
||||
&& checker.statements
|
||||
<= tcx.sess.opts.unstable_opts.cross_crate_inline_threshold.unwrap_or(100)
|
||||
}
|
||||
|
||||
struct CostChecker<'b, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
callee_body: &'b Body<'tcx>,
|
||||
calls: usize,
|
||||
statements: usize,
|
||||
landing_pads: usize,
|
||||
resumes: usize,
|
||||
}
|
||||
|
||||
impl<'tcx> Visitor<'tcx> for CostChecker<'_, 'tcx> {
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, _: Location) {
|
||||
// Don't count StorageLive/StorageDead in the inlining cost.
|
||||
match statement.kind {
|
||||
StatementKind::StorageLive(_)
|
||||
| StatementKind::StorageDead(_)
|
||||
| StatementKind::Deinit(_)
|
||||
| StatementKind::Nop => {}
|
||||
_ => self.statements += 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, _: Location) {
|
||||
let tcx = self.tcx;
|
||||
match terminator.kind {
|
||||
TerminatorKind::Drop { ref place, unwind, .. } => {
|
||||
let ty = place.ty(self.callee_body, tcx).ty;
|
||||
if !ty.is_trivially_pure_clone_copy() {
|
||||
self.calls += 1;
|
||||
if let UnwindAction::Cleanup(_) = unwind {
|
||||
self.landing_pads += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
TerminatorKind::Call { unwind, .. } => {
|
||||
self.calls += 1;
|
||||
if let UnwindAction::Cleanup(_) = unwind {
|
||||
self.landing_pads += 1;
|
||||
}
|
||||
}
|
||||
TerminatorKind::Assert { unwind, .. } => {
|
||||
self.calls += 1;
|
||||
if let UnwindAction::Cleanup(_) = unwind {
|
||||
self.landing_pads += 1;
|
||||
}
|
||||
}
|
||||
TerminatorKind::UnwindResume => self.resumes += 1,
|
||||
TerminatorKind::InlineAsm { unwind, .. } => {
|
||||
self.statements += 1;
|
||||
if let UnwindAction::Cleanup(_) = unwind {
|
||||
self.landing_pads += 1;
|
||||
}
|
||||
}
|
||||
TerminatorKind::Return => {}
|
||||
_ => self.statements += 1,
|
||||
}
|
||||
}
|
||||
}
|
|
@ -244,7 +244,7 @@ impl<'tcx> MirPass<'tcx> for DestinationPropagation {
|
|||
if round_count != 0 {
|
||||
// Merging can introduce overlap between moved arguments and/or call destination in an
|
||||
// unreachable code, which validator considers to be ill-formed.
|
||||
remove_dead_blocks(tcx, body);
|
||||
remove_dead_blocks(body);
|
||||
}
|
||||
|
||||
trace!(round_count);
|
||||
|
|
|
@ -1088,7 +1088,7 @@ fn create_generator_drop_shim<'tcx>(
|
|||
|
||||
// Make sure we remove dead blocks to remove
|
||||
// unrelated code from the resume part of the function
|
||||
simplify::remove_dead_blocks(tcx, &mut body);
|
||||
simplify::remove_dead_blocks(&mut body);
|
||||
|
||||
// Update the body's def to become the drop glue.
|
||||
// This needs to be updated before the AbortUnwindingCalls pass.
|
||||
|
@ -1276,7 +1276,7 @@ fn create_generator_resume_function<'tcx>(
|
|||
|
||||
// Make sure we remove dead blocks to remove
|
||||
// unrelated code from the drop part of the function
|
||||
simplify::remove_dead_blocks(tcx, body);
|
||||
simplify::remove_dead_blocks(body);
|
||||
|
||||
pm::run_passes_no_validate(tcx, body, &[&abort_unwinding_calls::AbortUnwindingCalls], None);
|
||||
|
||||
|
|
|
@ -63,7 +63,7 @@ impl<'tcx> MirPass<'tcx> for Inline {
|
|||
if inline(tcx, body) {
|
||||
debug!("running simplify cfg on {:?}", body.source);
|
||||
CfgSimplifier::new(body).simplify();
|
||||
remove_dead_blocks(tcx, body);
|
||||
remove_dead_blocks(body);
|
||||
deref_finder(tcx, body);
|
||||
}
|
||||
}
|
||||
|
@ -169,8 +169,11 @@ impl<'tcx> Inliner<'tcx> {
|
|||
caller_body: &mut Body<'tcx>,
|
||||
callsite: &CallSite<'tcx>,
|
||||
) -> Result<std::ops::Range<BasicBlock>, &'static str> {
|
||||
self.check_mir_is_available(caller_body, &callsite.callee)?;
|
||||
|
||||
let callee_attrs = self.tcx.codegen_fn_attrs(callsite.callee.def_id());
|
||||
self.check_codegen_attributes(callsite, callee_attrs)?;
|
||||
let cross_crate_inlinable = self.tcx.cross_crate_inlinable(callsite.callee.def_id());
|
||||
self.check_codegen_attributes(callsite, callee_attrs, cross_crate_inlinable)?;
|
||||
|
||||
let terminator = caller_body[callsite.block].terminator.as_ref().unwrap();
|
||||
let TerminatorKind::Call { args, destination, .. } = &terminator.kind else { bug!() };
|
||||
|
@ -183,9 +186,8 @@ impl<'tcx> Inliner<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
self.check_mir_is_available(caller_body, &callsite.callee)?;
|
||||
let callee_body = try_instance_mir(self.tcx, callsite.callee.def)?;
|
||||
self.check_mir_body(callsite, callee_body, callee_attrs)?;
|
||||
self.check_mir_body(callsite, callee_body, callee_attrs, cross_crate_inlinable)?;
|
||||
|
||||
if !self.tcx.consider_optimizing(|| {
|
||||
format!("Inline {:?} into {:?}", callsite.callee, caller_body.source)
|
||||
|
@ -401,6 +403,7 @@ impl<'tcx> Inliner<'tcx> {
|
|||
&self,
|
||||
callsite: &CallSite<'tcx>,
|
||||
callee_attrs: &CodegenFnAttrs,
|
||||
cross_crate_inlinable: bool,
|
||||
) -> Result<(), &'static str> {
|
||||
if let InlineAttr::Never = callee_attrs.inline {
|
||||
return Err("never inline hint");
|
||||
|
@ -414,7 +417,7 @@ impl<'tcx> Inliner<'tcx> {
|
|||
.non_erasable_generics(self.tcx, callsite.callee.def_id())
|
||||
.next()
|
||||
.is_some();
|
||||
if !is_generic && !callee_attrs.requests_inline() {
|
||||
if !is_generic && !cross_crate_inlinable {
|
||||
return Err("not exported");
|
||||
}
|
||||
|
||||
|
@ -456,10 +459,11 @@ impl<'tcx> Inliner<'tcx> {
|
|||
callsite: &CallSite<'tcx>,
|
||||
callee_body: &Body<'tcx>,
|
||||
callee_attrs: &CodegenFnAttrs,
|
||||
cross_crate_inlinable: bool,
|
||||
) -> Result<(), &'static str> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
let mut threshold = if callee_attrs.requests_inline() {
|
||||
let mut threshold = if cross_crate_inlinable {
|
||||
self.tcx.sess.opts.unstable_opts.inline_mir_hint_threshold.unwrap_or(100)
|
||||
} else {
|
||||
self.tcx.sess.opts.unstable_opts.inline_mir_threshold.unwrap_or(50)
|
||||
|
|
|
@ -62,6 +62,7 @@ mod const_prop;
|
|||
mod const_prop_lint;
|
||||
mod copy_prop;
|
||||
mod coverage;
|
||||
mod cross_crate_inline;
|
||||
mod ctfe_limit;
|
||||
mod dataflow_const_prop;
|
||||
mod dead_store_elimination;
|
||||
|
@ -123,6 +124,7 @@ pub fn provide(providers: &mut Providers) {
|
|||
coverage::query::provide(providers);
|
||||
ffi_unwind_calls::provide(providers);
|
||||
shim::provide(providers);
|
||||
cross_crate_inline::provide(providers);
|
||||
*providers = Providers {
|
||||
mir_keys,
|
||||
mir_const,
|
||||
|
@ -381,7 +383,7 @@ fn mir_drops_elaborated_and_const_checked(tcx: TyCtxt<'_>, def: LocalDefId) -> &
|
|||
let is_fn_like = tcx.def_kind(def).is_fn_like();
|
||||
if is_fn_like {
|
||||
// Do not compute the mir call graph without said call graph actually being used.
|
||||
if inline::Inline.is_enabled(&tcx.sess) {
|
||||
if pm::should_run_pass(tcx, &inline::Inline) {
|
||||
tcx.ensure_with_value().mir_inliner_callees(ty::InstanceDef::Item(def.to_def_id()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,6 @@ impl<'tcx> MirPass<'tcx> for MultipleReturnTerminators {
|
|||
}
|
||||
}
|
||||
|
||||
simplify::remove_dead_blocks(tcx, body)
|
||||
simplify::remove_dead_blocks(body)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,6 +83,25 @@ pub fn run_passes<'tcx>(
|
|||
run_passes_inner(tcx, body, passes, phase_change, true);
|
||||
}
|
||||
|
||||
pub fn should_run_pass<'tcx, P>(tcx: TyCtxt<'tcx>, pass: &P) -> bool
|
||||
where
|
||||
P: MirPass<'tcx> + ?Sized,
|
||||
{
|
||||
let name = pass.name();
|
||||
|
||||
let overridden_passes = &tcx.sess.opts.unstable_opts.mir_enable_passes;
|
||||
let overridden =
|
||||
overridden_passes.iter().rev().find(|(s, _)| s == &*name).map(|(_name, polarity)| {
|
||||
trace!(
|
||||
pass = %name,
|
||||
"{} as requested by flag",
|
||||
if *polarity { "Running" } else { "Not running" },
|
||||
);
|
||||
*polarity
|
||||
});
|
||||
overridden.unwrap_or_else(|| pass.is_enabled(&tcx.sess))
|
||||
}
|
||||
|
||||
fn run_passes_inner<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &mut Body<'tcx>,
|
||||
|
@ -100,19 +119,9 @@ fn run_passes_inner<'tcx>(
|
|||
for pass in passes {
|
||||
let name = pass.name();
|
||||
|
||||
let overridden = overridden_passes.iter().rev().find(|(s, _)| s == &*name).map(
|
||||
|(_name, polarity)| {
|
||||
trace!(
|
||||
pass = %name,
|
||||
"{} as requested by flag",
|
||||
if *polarity { "Running" } else { "Not running" },
|
||||
);
|
||||
*polarity
|
||||
},
|
||||
);
|
||||
if !overridden.unwrap_or_else(|| pass.is_enabled(&tcx.sess)) {
|
||||
if !should_run_pass(tcx, *pass) {
|
||||
continue;
|
||||
}
|
||||
};
|
||||
|
||||
let dump_enabled = pass.is_mir_dump_enabled();
|
||||
|
||||
|
|
|
@ -28,10 +28,8 @@
|
|||
//! return.
|
||||
|
||||
use crate::MirPass;
|
||||
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
use rustc_middle::mir::coverage::*;
|
||||
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
|
@ -68,7 +66,7 @@ impl SimplifyCfg {
|
|||
pub fn simplify_cfg<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
CfgSimplifier::new(body).simplify();
|
||||
remove_duplicate_unreachable_blocks(tcx, body);
|
||||
remove_dead_blocks(tcx, body);
|
||||
remove_dead_blocks(body);
|
||||
|
||||
// FIXME: Should probably be moved into some kind of pass manager
|
||||
body.basic_blocks_mut().raw.shrink_to_fit();
|
||||
|
@ -337,7 +335,7 @@ pub fn remove_duplicate_unreachable_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut B
|
|||
}
|
||||
}
|
||||
|
||||
pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
pub fn remove_dead_blocks(body: &mut Body<'_>) {
|
||||
let reachable = traversal::reachable_as_bitset(body);
|
||||
let num_blocks = body.basic_blocks.len();
|
||||
if num_blocks == reachable.count() {
|
||||
|
@ -345,10 +343,6 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
|||
}
|
||||
|
||||
let basic_blocks = body.basic_blocks.as_mut();
|
||||
let source_scopes = &body.source_scopes;
|
||||
if tcx.sess.instrument_coverage() {
|
||||
save_unreachable_coverage(basic_blocks, source_scopes, &reachable);
|
||||
}
|
||||
|
||||
let mut replacements: Vec<_> = (0..num_blocks).map(BasicBlock::new).collect();
|
||||
let mut orig_index = 0;
|
||||
|
@ -370,99 +364,6 @@ pub fn remove_dead_blocks<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
|||
}
|
||||
}
|
||||
|
||||
/// Some MIR transforms can determine at compile time that a sequences of
|
||||
/// statements will never be executed, so they can be dropped from the MIR.
|
||||
/// For example, an `if` or `else` block that is guaranteed to never be executed
|
||||
/// because its condition can be evaluated at compile time, such as by const
|
||||
/// evaluation: `if false { ... }`.
|
||||
///
|
||||
/// Those statements are bypassed by redirecting paths in the CFG around the
|
||||
/// `dead blocks`; but with `-C instrument-coverage`, the dead blocks usually
|
||||
/// include `Coverage` statements representing the Rust source code regions to
|
||||
/// be counted at runtime. Without these `Coverage` statements, the regions are
|
||||
/// lost, and the Rust source code will show no coverage information.
|
||||
///
|
||||
/// What we want to show in a coverage report is the dead code with coverage
|
||||
/// counts of `0`. To do this, we need to save the code regions, by injecting
|
||||
/// `Unreachable` coverage statements. These are non-executable statements whose
|
||||
/// code regions are still recorded in the coverage map, representing regions
|
||||
/// with `0` executions.
|
||||
///
|
||||
/// If there are no live `Counter` `Coverage` statements remaining, we remove
|
||||
/// `Coverage` statements along with the dead blocks. Since at least one
|
||||
/// counter per function is required by LLVM (and necessary, to add the
|
||||
/// `function_hash` to the counter's call to the LLVM intrinsic
|
||||
/// `instrprof.increment()`).
|
||||
///
|
||||
/// The `generator::StateTransform` MIR pass and MIR inlining can create
|
||||
/// atypical conditions, where all live `Counter`s are dropped from the MIR.
|
||||
///
|
||||
/// With MIR inlining we can have coverage counters belonging to different
|
||||
/// instances in a single body, so the strategy described above is applied to
|
||||
/// coverage counters from each instance individually.
|
||||
fn save_unreachable_coverage(
|
||||
basic_blocks: &mut IndexSlice<BasicBlock, BasicBlockData<'_>>,
|
||||
source_scopes: &IndexSlice<SourceScope, SourceScopeData<'_>>,
|
||||
reachable: &BitSet<BasicBlock>,
|
||||
) {
|
||||
// Identify instances that still have some live coverage counters left.
|
||||
let mut live = FxHashSet::default();
|
||||
for bb in reachable.iter() {
|
||||
let basic_block = &basic_blocks[bb];
|
||||
for statement in &basic_block.statements {
|
||||
let StatementKind::Coverage(coverage) = &statement.kind else { continue };
|
||||
let CoverageKind::Counter { .. } = coverage.kind else { continue };
|
||||
let instance = statement.source_info.scope.inlined_instance(source_scopes);
|
||||
live.insert(instance);
|
||||
}
|
||||
}
|
||||
|
||||
for bb in reachable.iter() {
|
||||
let block = &mut basic_blocks[bb];
|
||||
for statement in &mut block.statements {
|
||||
let StatementKind::Coverage(_) = &statement.kind else { continue };
|
||||
let instance = statement.source_info.scope.inlined_instance(source_scopes);
|
||||
if !live.contains(&instance) {
|
||||
statement.make_nop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if live.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Retain coverage for instances that still have some live counters left.
|
||||
let mut retained_coverage = Vec::new();
|
||||
for dead_block in basic_blocks.indices() {
|
||||
if reachable.contains(dead_block) {
|
||||
continue;
|
||||
}
|
||||
let dead_block = &basic_blocks[dead_block];
|
||||
for statement in &dead_block.statements {
|
||||
let StatementKind::Coverage(coverage) = &statement.kind else { continue };
|
||||
if coverage.code_regions.is_empty() {
|
||||
continue;
|
||||
};
|
||||
let instance = statement.source_info.scope.inlined_instance(source_scopes);
|
||||
if live.contains(&instance) {
|
||||
retained_coverage.push((statement.source_info, coverage.code_regions.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let start_block = &mut basic_blocks[START_BLOCK];
|
||||
start_block.statements.extend(retained_coverage.into_iter().map(
|
||||
|(source_info, code_regions)| Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Coverage(Box::new(Coverage {
|
||||
kind: CoverageKind::Unreachable,
|
||||
code_regions,
|
||||
})),
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
pub enum SimplifyLocals {
|
||||
BeforeConstProp,
|
||||
Final,
|
||||
|
|
|
@ -65,7 +65,7 @@ impl MirPass<'_> for UnreachablePropagation {
|
|||
}
|
||||
|
||||
if replaced {
|
||||
simplify::remove_dead_blocks(tcx, body);
|
||||
simplify::remove_dead_blocks(body);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1776,6 +1776,7 @@ impl CheckAttrVisitor<'_> {
|
|||
.collect();
|
||||
|
||||
let mut int_reprs = 0;
|
||||
let mut is_explicit_rust = false;
|
||||
let mut is_c = false;
|
||||
let mut is_simd = false;
|
||||
let mut is_transparent = false;
|
||||
|
@ -1787,7 +1788,9 @@ impl CheckAttrVisitor<'_> {
|
|||
}
|
||||
|
||||
match hint.name_or_empty() {
|
||||
sym::Rust => {}
|
||||
sym::Rust => {
|
||||
is_explicit_rust = true;
|
||||
}
|
||||
sym::C => {
|
||||
is_c = true;
|
||||
match target {
|
||||
|
@ -1897,12 +1900,16 @@ impl CheckAttrVisitor<'_> {
|
|||
|
||||
// Error on repr(transparent, <anything else>).
|
||||
if is_transparent && hints.len() > 1 {
|
||||
let hint_spans: Vec<_> = hint_spans.clone().collect();
|
||||
let hint_spans = hint_spans.clone().collect();
|
||||
self.tcx.sess.emit_err(errors::TransparentIncompatible {
|
||||
hint_spans,
|
||||
target: target.to_string(),
|
||||
});
|
||||
}
|
||||
if is_explicit_rust && (int_reprs > 0 || is_c || is_simd) {
|
||||
let hint_spans = hint_spans.clone().collect();
|
||||
self.tcx.sess.emit_err(errors::ReprConflicting { hint_spans });
|
||||
}
|
||||
// Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
|
||||
if (int_reprs > 1)
|
||||
|| (is_simd && is_c)
|
||||
|
@ -1919,7 +1926,7 @@ impl CheckAttrVisitor<'_> {
|
|||
CONFLICTING_REPR_HINTS,
|
||||
hir_id,
|
||||
hint_spans.collect::<Vec<Span>>(),
|
||||
errors::ReprConflicting,
|
||||
errors::ReprConflictingLint,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -558,9 +558,16 @@ pub struct ReprIdent {
|
|||
pub span: Span,
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_repr_conflicting, code = "E0566")]
|
||||
pub struct ReprConflicting {
|
||||
#[primary_span]
|
||||
pub hint_spans: Vec<Span>,
|
||||
}
|
||||
|
||||
#[derive(LintDiagnostic)]
|
||||
#[diag(passes_repr_conflicting, code = "E0566")]
|
||||
pub struct ReprConflicting;
|
||||
pub struct ReprConflictingLint;
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(passes_used_static)]
|
||||
|
|
|
@ -231,7 +231,7 @@ impl<'a, 'hir> CheckLoopVisitor<'a, 'hir> {
|
|||
AsyncClosure(closure_span) => {
|
||||
self.sess.emit_err(BreakInsideAsyncBlock { span, closure_span, name });
|
||||
}
|
||||
UnlabeledBlock(block_span) if is_break && block_span.ctxt() == break_span.ctxt() => {
|
||||
UnlabeledBlock(block_span) if is_break && block_span.eq_ctxt(break_span) => {
|
||||
let suggestion = Some(OutsideLoopSuggestion { block_span, break_span });
|
||||
self.sess.emit_err(OutsideLoop { span, name, is_break, suggestion });
|
||||
}
|
||||
|
|
|
@ -18,43 +18,10 @@ use rustc_middle::ty::{self, TyCtxt};
|
|||
use rustc_session::config::CrateType;
|
||||
use rustc_target::spec::abi::Abi;
|
||||
|
||||
// Returns true if the given item must be inlined because it may be
|
||||
// monomorphized or it was marked with `#[inline]`. This will only return
|
||||
// true for functions.
|
||||
fn item_might_be_inlined(tcx: TyCtxt<'_>, item: &hir::Item<'_>, attrs: &CodegenFnAttrs) -> bool {
|
||||
if attrs.requests_inline() {
|
||||
return true;
|
||||
}
|
||||
|
||||
match item.kind {
|
||||
hir::ItemKind::Fn(ref sig, ..) if sig.header.is_const() => true,
|
||||
hir::ItemKind::Impl { .. } | hir::ItemKind::Fn(..) => {
|
||||
let generics = tcx.generics_of(item.owner_id);
|
||||
generics.requires_monomorphization(tcx)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn method_might_be_inlined(
|
||||
tcx: TyCtxt<'_>,
|
||||
impl_item: &hir::ImplItem<'_>,
|
||||
impl_src: LocalDefId,
|
||||
) -> bool {
|
||||
let codegen_fn_attrs = tcx.codegen_fn_attrs(impl_item.hir_id().owner.to_def_id());
|
||||
let generics = tcx.generics_of(impl_item.owner_id);
|
||||
if codegen_fn_attrs.requests_inline() || generics.requires_monomorphization(tcx) {
|
||||
return true;
|
||||
}
|
||||
if let hir::ImplItemKind::Fn(method_sig, _) = &impl_item.kind {
|
||||
if method_sig.header.is_const() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
match tcx.hir().find_by_def_id(impl_src) {
|
||||
Some(Node::Item(item)) => item_might_be_inlined(tcx, &item, codegen_fn_attrs),
|
||||
Some(..) | None => span_bug!(impl_item.span, "impl did is not an item"),
|
||||
}
|
||||
fn item_might_be_inlined(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
||||
tcx.generics_of(def_id).requires_monomorphization(tcx)
|
||||
|| tcx.cross_crate_inlinable(def_id)
|
||||
|| tcx.is_const_fn(def_id)
|
||||
}
|
||||
|
||||
// Information needed while computing reachability.
|
||||
|
@ -150,9 +117,7 @@ impl<'tcx> ReachableContext<'tcx> {
|
|||
|
||||
match self.tcx.hir().find_by_def_id(def_id) {
|
||||
Some(Node::Item(item)) => match item.kind {
|
||||
hir::ItemKind::Fn(..) => {
|
||||
item_might_be_inlined(self.tcx, &item, self.tcx.codegen_fn_attrs(def_id))
|
||||
}
|
||||
hir::ItemKind::Fn(..) => item_might_be_inlined(self.tcx, def_id.into()),
|
||||
_ => false,
|
||||
},
|
||||
Some(Node::TraitItem(trait_method)) => match trait_method.kind {
|
||||
|
@ -164,9 +129,7 @@ impl<'tcx> ReachableContext<'tcx> {
|
|||
Some(Node::ImplItem(impl_item)) => match impl_item.kind {
|
||||
hir::ImplItemKind::Const(..) => true,
|
||||
hir::ImplItemKind::Fn(..) => {
|
||||
let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
|
||||
let impl_did = self.tcx.hir().get_parent_item(hir_id);
|
||||
method_might_be_inlined(self.tcx, impl_item, impl_did.def_id)
|
||||
item_might_be_inlined(self.tcx, impl_item.hir_id().owner.to_def_id())
|
||||
}
|
||||
hir::ImplItemKind::Type(_) => false,
|
||||
},
|
||||
|
@ -226,11 +189,7 @@ impl<'tcx> ReachableContext<'tcx> {
|
|||
Node::Item(item) => {
|
||||
match item.kind {
|
||||
hir::ItemKind::Fn(.., body) => {
|
||||
if item_might_be_inlined(
|
||||
self.tcx,
|
||||
&item,
|
||||
self.tcx.codegen_fn_attrs(item.owner_id),
|
||||
) {
|
||||
if item_might_be_inlined(self.tcx, item.owner_id.into()) {
|
||||
self.visit_nested_body(body);
|
||||
}
|
||||
}
|
||||
|
@ -279,8 +238,7 @@ impl<'tcx> ReachableContext<'tcx> {
|
|||
self.visit_nested_body(body);
|
||||
}
|
||||
hir::ImplItemKind::Fn(_, body) => {
|
||||
let impl_def_id = self.tcx.local_parent(search_item);
|
||||
if method_might_be_inlined(self.tcx, impl_item, impl_def_id) {
|
||||
if item_might_be_inlined(self.tcx, impl_item.hir_id().owner.to_def_id()) {
|
||||
self.visit_nested_body(body)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1477,6 +1477,8 @@ options! {
|
|||
"combine CGUs into a single one"),
|
||||
crate_attr: Vec<String> = (Vec::new(), parse_string_push, [TRACKED],
|
||||
"inject the given attribute in the crate"),
|
||||
cross_crate_inline_threshold: Option<usize> = (None, parse_opt_number, [TRACKED],
|
||||
"threshold to allow cross crate inlining of functions"),
|
||||
debug_info_for_profiling: bool = (false, parse_bool, [TRACKED],
|
||||
"emit discriminators and other data necessary for AutoFDO"),
|
||||
debug_macros: bool = (false, parse_bool, [TRACKED],
|
||||
|
|
|
@ -6,9 +6,11 @@
|
|||
use crate::rustc_internal;
|
||||
use crate::rustc_smir::Tables;
|
||||
use rustc_data_structures::fx;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_driver::{Callbacks, Compilation, RunCompiler};
|
||||
use rustc_interface::{interface, Queries};
|
||||
use rustc_middle::mir::interpret::AllocId;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::def_id::{CrateNum, DefId};
|
||||
use rustc_span::Span;
|
||||
|
@ -97,7 +99,7 @@ impl<'tcx> Tables<'tcx> {
|
|||
stable_mir::ty::Prov(self.create_alloc_id(aid))
|
||||
}
|
||||
|
||||
fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId {
|
||||
pub(crate) fn create_def_id(&mut self, did: DefId) -> stable_mir::DefId {
|
||||
self.def_ids.create_or_fetch(did)
|
||||
}
|
||||
|
||||
|
@ -108,6 +110,17 @@ impl<'tcx> Tables<'tcx> {
|
|||
pub(crate) fn create_span(&mut self, span: Span) -> stable_mir::ty::Span {
|
||||
self.spans.create_or_fetch(span)
|
||||
}
|
||||
|
||||
pub(crate) fn instance_def(
|
||||
&mut self,
|
||||
instance: ty::Instance<'tcx>,
|
||||
) -> stable_mir::mir::mono::InstanceDef {
|
||||
self.instances.create_or_fetch(instance)
|
||||
}
|
||||
|
||||
pub(crate) fn static_def(&mut self, did: DefId) -> stable_mir::mir::mono::StaticDef {
|
||||
stable_mir::mir::mono::StaticDef(self.create_def_id(did))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn crate_num(item: &stable_mir::Crate) -> CrateNum {
|
||||
|
@ -118,10 +131,11 @@ pub fn run(tcx: TyCtxt<'_>, f: impl FnOnce()) {
|
|||
stable_mir::run(
|
||||
Tables {
|
||||
tcx,
|
||||
def_ids: rustc_internal::IndexMap { index_map: fx::FxIndexMap::default() },
|
||||
alloc_ids: rustc_internal::IndexMap { index_map: fx::FxIndexMap::default() },
|
||||
spans: rustc_internal::IndexMap { index_map: fx::FxIndexMap::default() },
|
||||
def_ids: IndexMap::default(),
|
||||
alloc_ids: IndexMap::default(),
|
||||
spans: IndexMap::default(),
|
||||
types: vec![],
|
||||
instances: IndexMap::default(),
|
||||
},
|
||||
f,
|
||||
);
|
||||
|
@ -192,6 +206,12 @@ pub struct IndexMap<K, V> {
|
|||
index_map: fx::FxIndexMap<K, V>,
|
||||
}
|
||||
|
||||
impl<K, V> Default for IndexMap<K, V> {
|
||||
fn default() -> Self {
|
||||
Self { index_map: FxIndexMap::default() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<K: PartialEq + Hash + Eq, V: Copy + Debug + PartialEq + IndexedVal> IndexMap<K, V> {
|
||||
pub fn create_or_fetch(&mut self, key: K) -> V {
|
||||
let len = self.index_map.len();
|
||||
|
|
|
@ -13,10 +13,12 @@ use crate::rustc_smir::stable_mir::ty::{BoundRegion, EarlyBoundRegion, Region};
|
|||
use rustc_hir as hir;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::{alloc_range, AllocId};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, Variance};
|
||||
use rustc_middle::mir::mono::MonoItem;
|
||||
use rustc_middle::ty::{self, Instance, ParamEnv, Ty, TyCtxt, Variance};
|
||||
use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
|
||||
use rustc_target::abi::FieldIdx;
|
||||
use stable_mir::mir::{CopyNonOverlapping, Statement, UserTypeProjection, VariantIdx};
|
||||
use stable_mir::mir::mono::InstanceDef;
|
||||
use stable_mir::mir::{Body, CopyNonOverlapping, Statement, UserTypeProjection, VariantIdx};
|
||||
use stable_mir::ty::{
|
||||
FloatTy, GenericParamDef, IntTy, LineInfo, Movability, RigidTy, Span, TyKind, UintTy,
|
||||
};
|
||||
|
@ -119,29 +121,7 @@ impl<'tcx> Context for Tables<'tcx> {
|
|||
|
||||
fn mir_body(&mut self, item: stable_mir::DefId) -> stable_mir::mir::Body {
|
||||
let def_id = self[item];
|
||||
let mir = self.tcx.instance_mir(ty::InstanceDef::Item(def_id));
|
||||
stable_mir::mir::Body {
|
||||
blocks: mir
|
||||
.basic_blocks
|
||||
.iter()
|
||||
.map(|block| stable_mir::mir::BasicBlock {
|
||||
terminator: block.terminator().stable(self),
|
||||
statements: block
|
||||
.statements
|
||||
.iter()
|
||||
.map(|statement| statement.stable(self))
|
||||
.collect(),
|
||||
})
|
||||
.collect(),
|
||||
locals: mir
|
||||
.local_decls
|
||||
.iter()
|
||||
.map(|decl| stable_mir::mir::LocalDecl {
|
||||
ty: self.intern_ty(decl.ty),
|
||||
span: decl.source_info.span.stable(self),
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
self.tcx.instance_mir(ty::InstanceDef::Item(def_id)).stable(self)
|
||||
}
|
||||
|
||||
fn ty_kind(&mut self, ty: stable_mir::ty::Ty) -> TyKind {
|
||||
|
@ -190,6 +170,34 @@ impl<'tcx> Context for Tables<'tcx> {
|
|||
.collect(),
|
||||
}
|
||||
}
|
||||
|
||||
fn instance_body(&mut self, _def: InstanceDef) -> Body {
|
||||
todo!("Monomorphize the body")
|
||||
}
|
||||
|
||||
fn instance_ty(&mut self, def: InstanceDef) -> stable_mir::ty::Ty {
|
||||
let instance = self.instances[def];
|
||||
let ty = instance.ty(self.tcx, ParamEnv::empty());
|
||||
self.intern_ty(ty)
|
||||
}
|
||||
|
||||
fn instance_def_id(&mut self, def: InstanceDef) -> stable_mir::DefId {
|
||||
let def_id = self.instances[def].def_id();
|
||||
self.create_def_id(def_id)
|
||||
}
|
||||
|
||||
fn mono_instance(&mut self, item: stable_mir::CrateItem) -> stable_mir::mir::mono::Instance {
|
||||
let def_id = self[item.0];
|
||||
Instance::mono(self.tcx, def_id).stable(self)
|
||||
}
|
||||
|
||||
fn requires_monomorphization(&self, def_id: stable_mir::DefId) -> bool {
|
||||
let def_id = self[def_id];
|
||||
let generics = self.tcx.generics_of(def_id);
|
||||
let result = generics.requires_monomorphization(self.tcx);
|
||||
println!("req {result}: {def_id:?}");
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -224,7 +232,8 @@ pub struct Tables<'tcx> {
|
|||
pub def_ids: IndexMap<DefId, stable_mir::DefId>,
|
||||
pub alloc_ids: IndexMap<AllocId, stable_mir::AllocId>,
|
||||
pub spans: IndexMap<rustc_span::Span, Span>,
|
||||
pub types: Vec<MaybeStable<stable_mir::ty::TyKind, Ty<'tcx>>>,
|
||||
pub types: Vec<MaybeStable<TyKind, Ty<'tcx>>>,
|
||||
pub instances: IndexMap<ty::Instance<'tcx>, InstanceDef>,
|
||||
}
|
||||
|
||||
impl<'tcx> Tables<'tcx> {
|
||||
|
@ -254,6 +263,35 @@ pub(crate) trait Stable<'tcx> {
|
|||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T;
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for mir::Body<'tcx> {
|
||||
type T = stable_mir::mir::Body;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
stable_mir::mir::Body {
|
||||
blocks: self
|
||||
.basic_blocks
|
||||
.iter()
|
||||
.map(|block| stable_mir::mir::BasicBlock {
|
||||
terminator: block.terminator().stable(tables),
|
||||
statements: block
|
||||
.statements
|
||||
.iter()
|
||||
.map(|statement| statement.stable(tables))
|
||||
.collect(),
|
||||
})
|
||||
.collect(),
|
||||
locals: self
|
||||
.local_decls
|
||||
.iter()
|
||||
.map(|decl| stable_mir::mir::LocalDecl {
|
||||
ty: tables.intern_ty(decl.ty),
|
||||
span: decl.source_info.span.stable(tables),
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for mir::Statement<'tcx> {
|
||||
type T = stable_mir::mir::Statement;
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
|
@ -1637,3 +1675,38 @@ impl<'tcx> Stable<'tcx> for DefKind {
|
|||
opaque(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> {
|
||||
type T = stable_mir::mir::mono::Instance;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
let def = tables.instance_def(*self);
|
||||
let kind = match self.def {
|
||||
ty::InstanceDef::Item(..) => stable_mir::mir::mono::InstanceKind::Item,
|
||||
ty::InstanceDef::Intrinsic(..) => stable_mir::mir::mono::InstanceKind::Intrinsic,
|
||||
ty::InstanceDef::Virtual(..) => stable_mir::mir::mono::InstanceKind::Virtual,
|
||||
ty::InstanceDef::VTableShim(..)
|
||||
| ty::InstanceDef::ReifyShim(..)
|
||||
| ty::InstanceDef::FnPtrAddrShim(..)
|
||||
| ty::InstanceDef::ClosureOnceShim { .. }
|
||||
| ty::InstanceDef::ThreadLocalShim(..)
|
||||
| ty::InstanceDef::DropGlue(..)
|
||||
| ty::InstanceDef::CloneShim(..)
|
||||
| ty::InstanceDef::FnPtrShim(..) => stable_mir::mir::mono::InstanceKind::Shim,
|
||||
};
|
||||
stable_mir::mir::mono::Instance { def, kind }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for MonoItem<'tcx> {
|
||||
type T = stable_mir::mir::mono::MonoItem;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
use stable_mir::mir::mono::MonoItem as StableMonoItem;
|
||||
match self {
|
||||
MonoItem::Fn(instance) => StableMonoItem::Fn(instance.stable(tables)),
|
||||
MonoItem::Static(def_id) => StableMonoItem::Static(tables.static_def(*def_id)),
|
||||
MonoItem::GlobalAsm(item_id) => StableMonoItem::GlobalAsm(opaque(item_id)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -212,6 +212,7 @@ impl Span {
|
|||
|
||||
/// This function is used as a fast path when decoding the full `SpanData` is not necessary.
|
||||
/// It's a cut-down version of `data_untracked`.
|
||||
#[cfg_attr(not(test), rustc_diagnostic_item = "SpanCtxt")]
|
||||
#[inline]
|
||||
pub fn ctxt(self) -> SyntaxContext {
|
||||
if self.len_with_tag_or_marker != BASE_LEN_INTERNED_MARKER {
|
||||
|
|
|
@ -303,6 +303,7 @@ symbols! {
|
|||
SliceIndex,
|
||||
SliceIter,
|
||||
Some,
|
||||
SpanCtxt,
|
||||
String,
|
||||
StructuralEq,
|
||||
StructuralPartialEq,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use rustc_data_structures::stable_hasher::{Hash64, HashStable, StableHasher};
|
||||
use rustc_hir::def_id::CrateNum;
|
||||
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
|
||||
use rustc_middle::ty::print::{PrettyPrinter, Print, Printer};
|
||||
use rustc_middle::ty::print::{PrettyPrinter, Print, PrintError, Printer};
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeVisitableExt};
|
||||
use rustc_middle::ty::{GenericArg, GenericArgKind};
|
||||
use rustc_middle::util::common::record_time;
|
||||
|
@ -200,23 +200,15 @@ struct SymbolPrinter<'tcx> {
|
|||
// symbol names should have their own printing machinery.
|
||||
|
||||
impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
||||
type Error = fmt::Error;
|
||||
|
||||
type Path = Self;
|
||||
type Region = Self;
|
||||
type Type = Self;
|
||||
type DynExistential = Self;
|
||||
type Const = Self;
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn print_region(self, _region: ty::Region<'_>) -> Result<Self::Region, Self::Error> {
|
||||
fn print_region(self, _region: ty::Region<'_>) -> Result<Self, PrintError> {
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
|
||||
fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self, PrintError> {
|
||||
match *ty.kind() {
|
||||
// Print all nominal types as paths (unlike `pretty_print_type`).
|
||||
ty::FnDef(def_id, args)
|
||||
|
@ -250,7 +242,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
fn print_dyn_existential(
|
||||
mut self,
|
||||
predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self::DynExistential, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
let mut first = true;
|
||||
for p in predicates {
|
||||
if !first {
|
||||
|
@ -262,7 +254,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
Ok(self)
|
||||
}
|
||||
|
||||
fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
|
||||
fn print_const(self, ct: ty::Const<'tcx>) -> Result<Self, PrintError> {
|
||||
// only print integers
|
||||
match (ct.kind(), ct.ty().kind()) {
|
||||
(ty::ConstKind::Value(ty::ValTree::Leaf(scalar)), ty::Int(_) | ty::Uint(_)) => {
|
||||
|
@ -280,7 +272,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
Ok(self)
|
||||
}
|
||||
|
||||
fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
|
||||
fn path_crate(self, cnum: CrateNum) -> Result<Self, PrintError> {
|
||||
self.write_str(self.tcx.crate_name(cnum).as_str())?;
|
||||
Ok(self)
|
||||
}
|
||||
|
@ -288,7 +280,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
self,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
// Similar to `pretty_path_qualified`, but for the other
|
||||
// types that are printed as paths (see `print_type` above).
|
||||
match self_ty.kind() {
|
||||
|
@ -304,11 +296,11 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
|
||||
fn path_append_impl(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
_disambiguated_data: &DisambiguatedDefPathData,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self.pretty_path_append_impl(
|
||||
|mut cx| {
|
||||
cx = print_prefix(cx)?;
|
||||
|
@ -328,9 +320,9 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
}
|
||||
fn path_append(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self = print_prefix(self)?;
|
||||
|
||||
// Skip `::{{extern}}` blocks and `::{{constructor}}` on tuple/unit structs.
|
||||
|
@ -351,9 +343,9 @@ impl<'tcx> Printer<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
}
|
||||
fn path_generic_args(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
args: &[GenericArg<'tcx>],
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
self = print_prefix(self)?;
|
||||
|
||||
let args =
|
||||
|
@ -371,9 +363,9 @@ impl<'tcx> PrettyPrinter<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
fn should_print_region(&self, _region: ty::Region<'_>) -> bool {
|
||||
false
|
||||
}
|
||||
fn comma_sep<T>(mut self, mut elems: impl Iterator<Item = T>) -> Result<Self, Self::Error>
|
||||
fn comma_sep<T>(mut self, mut elems: impl Iterator<Item = T>) -> Result<Self, PrintError>
|
||||
where
|
||||
T: Print<'tcx, Self, Output = Self, Error = Self::Error>,
|
||||
T: Print<'tcx, Self>,
|
||||
{
|
||||
if let Some(first) = elems.next() {
|
||||
self = first.print(self)?;
|
||||
|
@ -387,8 +379,8 @@ impl<'tcx> PrettyPrinter<'tcx> for &mut SymbolPrinter<'tcx> {
|
|||
|
||||
fn generic_delimiters(
|
||||
mut self,
|
||||
f: impl FnOnce(Self) -> Result<Self, Self::Error>,
|
||||
) -> Result<Self, Self::Error> {
|
||||
f: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
) -> Result<Self, PrintError> {
|
||||
write!(self, "<")?;
|
||||
|
||||
let kept_within_component = mem::replace(&mut self.keep_within_component, true);
|
||||
|
|
|
@ -6,7 +6,7 @@ use rustc_hir::def::CtorKind;
|
|||
use rustc_hir::def_id::{CrateNum, DefId};
|
||||
use rustc_hir::definitions::{DefPathData, DisambiguatedDefPathData};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::print::{Print, Printer};
|
||||
use rustc_middle::ty::print::{Print, PrintError, Printer};
|
||||
use rustc_middle::ty::{
|
||||
self, EarlyBinder, FloatTy, Instance, IntTy, Ty, TyCtxt, TypeVisitable, TypeVisitableExt,
|
||||
UintTy,
|
||||
|
@ -181,11 +181,11 @@ impl<'tcx> SymbolMangler<'tcx> {
|
|||
|
||||
fn path_append_ns<'a>(
|
||||
mut self: &'a mut Self,
|
||||
print_prefix: impl FnOnce(&'a mut Self) -> Result<&'a mut Self, !>,
|
||||
print_prefix: impl FnOnce(&'a mut Self) -> Result<&'a mut Self, PrintError>,
|
||||
ns: char,
|
||||
disambiguator: u64,
|
||||
name: &str,
|
||||
) -> Result<&'a mut Self, !> {
|
||||
) -> Result<&'a mut Self, PrintError> {
|
||||
self.push("N");
|
||||
self.out.push(ns);
|
||||
self = print_prefix(self)?;
|
||||
|
@ -194,7 +194,7 @@ impl<'tcx> SymbolMangler<'tcx> {
|
|||
Ok(self)
|
||||
}
|
||||
|
||||
fn print_backref(&mut self, i: usize) -> Result<&mut Self, !> {
|
||||
fn print_backref(&mut self, i: usize) -> Result<&mut Self, PrintError> {
|
||||
self.push("B");
|
||||
self.push_integer_62((i - self.start_offset) as u64);
|
||||
Ok(self)
|
||||
|
@ -203,8 +203,8 @@ impl<'tcx> SymbolMangler<'tcx> {
|
|||
fn in_binder<'a, T>(
|
||||
mut self: &'a mut Self,
|
||||
value: &ty::Binder<'tcx, T>,
|
||||
print_value: impl FnOnce(&'a mut Self, &T) -> Result<&'a mut Self, !>,
|
||||
) -> Result<&'a mut Self, !>
|
||||
print_value: impl FnOnce(&'a mut Self, &T) -> Result<&'a mut Self, PrintError>,
|
||||
) -> Result<&'a mut Self, PrintError>
|
||||
where
|
||||
T: TypeVisitable<TyCtxt<'tcx>>,
|
||||
{
|
||||
|
@ -230,14 +230,6 @@ impl<'tcx> SymbolMangler<'tcx> {
|
|||
}
|
||||
|
||||
impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
||||
type Error = !;
|
||||
|
||||
type Path = Self;
|
||||
type Region = Self;
|
||||
type Type = Self;
|
||||
type DynExistential = Self;
|
||||
type Const = Self;
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
@ -246,7 +238,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
mut self,
|
||||
def_id: DefId,
|
||||
args: &'tcx [GenericArg<'tcx>],
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
if let Some(&i) = self.paths.get(&(def_id, args)) {
|
||||
return self.print_backref(i);
|
||||
}
|
||||
|
@ -268,7 +260,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
args: &'tcx [GenericArg<'tcx>],
|
||||
mut self_ty: Ty<'tcx>,
|
||||
mut impl_trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
let key = self.tcx.def_key(impl_def_id);
|
||||
let parent_def_id = DefId { index: key.parent.unwrap(), ..impl_def_id };
|
||||
|
||||
|
@ -321,7 +313,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
Ok(self)
|
||||
}
|
||||
|
||||
fn print_region(self, region: ty::Region<'_>) -> Result<Self::Region, Self::Error> {
|
||||
fn print_region(self, region: ty::Region<'_>) -> Result<Self, PrintError> {
|
||||
let i = match *region {
|
||||
// Erased lifetimes use the index 0, for a
|
||||
// shorter mangling of `L_`.
|
||||
|
@ -343,7 +335,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
Ok(self)
|
||||
}
|
||||
|
||||
fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self::Type, Self::Error> {
|
||||
fn print_type(mut self, ty: Ty<'tcx>) -> Result<Self, PrintError> {
|
||||
// Basic types, never cached (single-character).
|
||||
let basic_type = match ty.kind() {
|
||||
ty::Bool => "b",
|
||||
|
@ -498,7 +490,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
fn print_dyn_existential(
|
||||
mut self,
|
||||
predicates: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Result<Self::DynExistential, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
// Okay, so this is a bit tricky. Imagine we have a trait object like
|
||||
// `dyn for<'a> Foo<'a, Bar = &'a ()>`. When we mangle this, the
|
||||
// output looks really close to the syntax, where the `Bar = &'a ()` bit
|
||||
|
@ -559,7 +551,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
Ok(self)
|
||||
}
|
||||
|
||||
fn print_const(mut self, ct: ty::Const<'tcx>) -> Result<Self::Const, Self::Error> {
|
||||
fn print_const(mut self, ct: ty::Const<'tcx>) -> Result<Self, PrintError> {
|
||||
// We only mangle a typed value if the const can be evaluated.
|
||||
let ct = ct.normalize(self.tcx, ty::ParamEnv::reveal_all());
|
||||
match ct.kind() {
|
||||
|
@ -731,7 +723,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
Ok(self)
|
||||
}
|
||||
|
||||
fn path_crate(self, cnum: CrateNum) -> Result<Self::Path, Self::Error> {
|
||||
fn path_crate(self, cnum: CrateNum) -> Result<Self, PrintError> {
|
||||
self.push("C");
|
||||
let stable_crate_id = self.tcx.def_path_hash(cnum.as_def_id()).stable_crate_id();
|
||||
self.push_disambiguator(stable_crate_id.as_u64());
|
||||
|
@ -744,7 +736,7 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
mut self,
|
||||
self_ty: Ty<'tcx>,
|
||||
trait_ref: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
assert!(trait_ref.is_some());
|
||||
let trait_ref = trait_ref.unwrap();
|
||||
|
||||
|
@ -755,20 +747,20 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
|
||||
fn path_append_impl(
|
||||
self,
|
||||
_: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
_: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
_: &DisambiguatedDefPathData,
|
||||
_: Ty<'tcx>,
|
||||
_: Option<ty::TraitRef<'tcx>>,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
// Inlined into `print_impl_path`
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
fn path_append(
|
||||
self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
disambiguated_data: &DisambiguatedDefPathData,
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
let ns = match disambiguated_data.data {
|
||||
// Extern block segments can be skipped, names from extern blocks
|
||||
// are effectively living in their parent modules.
|
||||
|
@ -806,9 +798,9 @@ impl<'tcx> Printer<'tcx> for &mut SymbolMangler<'tcx> {
|
|||
|
||||
fn path_generic_args(
|
||||
mut self,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self::Path, Self::Error>,
|
||||
print_prefix: impl FnOnce(Self) -> Result<Self, PrintError>,
|
||||
args: &[GenericArg<'tcx>],
|
||||
) -> Result<Self::Path, Self::Error> {
|
||||
) -> Result<Self, PrintError> {
|
||||
// Don't print any regions if they're all erased.
|
||||
let print_regions = args.iter().any(|arg| match arg.unpack() {
|
||||
GenericArgKind::Lifetime(r) => !r.is_erased(),
|
||||
|
|
|
@ -129,7 +129,7 @@ impl<'tcx> NormalizationFolder<'_, 'tcx> {
|
|||
self.at.cause.clone(),
|
||||
self.at.param_env,
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: tcx.mk_alias_ty(uv.def, uv.args),
|
||||
projection_ty: AliasTy::new(tcx, uv.def, uv.args),
|
||||
term: new_infer_ct.into(),
|
||||
},
|
||||
);
|
||||
|
|
|
@ -352,8 +352,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
|||
|
||||
let pred = tupled_inputs_and_output
|
||||
.map_bound(|(inputs, output)| ty::ProjectionPredicate {
|
||||
projection_ty: tcx
|
||||
.mk_alias_ty(goal.predicate.def_id(), [goal.predicate.self_ty(), inputs]),
|
||||
projection_ty: ty::AliasTy::new(
|
||||
tcx,
|
||||
goal.predicate.def_id(),
|
||||
[goal.predicate.self_ty(), inputs],
|
||||
),
|
||||
term: output.into(),
|
||||
})
|
||||
.to_predicate(tcx);
|
||||
|
@ -472,7 +475,7 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
|||
ecx,
|
||||
goal,
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: ecx.tcx().mk_alias_ty(goal.predicate.def_id(), [self_ty]),
|
||||
projection_ty: ty::AliasTy::new(ecx.tcx(), goal.predicate.def_id(), [self_ty]),
|
||||
term,
|
||||
}
|
||||
.to_predicate(tcx),
|
||||
|
@ -512,9 +515,11 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
|||
ecx,
|
||||
goal,
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: ecx
|
||||
.tcx()
|
||||
.mk_alias_ty(goal.predicate.def_id(), [self_ty, generator.resume_ty()]),
|
||||
projection_ty: ty::AliasTy::new(
|
||||
ecx.tcx(),
|
||||
goal.predicate.def_id(),
|
||||
[self_ty, generator.resume_ty()],
|
||||
),
|
||||
term,
|
||||
}
|
||||
.to_predicate(tcx),
|
||||
|
|
|
@ -313,6 +313,18 @@ pub trait TypeErrCtxtExt<'tcx> {
|
|||
predicate: ty::Predicate<'tcx>,
|
||||
call_hir_id: HirId,
|
||||
);
|
||||
|
||||
fn look_for_iterator_item_mistakes(
|
||||
&self,
|
||||
assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>],
|
||||
typeck_results: &TypeckResults<'tcx>,
|
||||
type_diffs: &[TypeError<'tcx>],
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
path_segment: &hir::PathSegment<'_>,
|
||||
args: &[hir::Expr<'_>],
|
||||
err: &mut Diagnostic,
|
||||
);
|
||||
|
||||
fn point_at_chain(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
|
@ -321,6 +333,7 @@ pub trait TypeErrCtxtExt<'tcx> {
|
|||
param_env: ty::ParamEnv<'tcx>,
|
||||
err: &mut Diagnostic,
|
||||
);
|
||||
|
||||
fn probe_assoc_types_at_expr(
|
||||
&self,
|
||||
type_diffs: &[TypeError<'tcx>],
|
||||
|
@ -3612,6 +3625,109 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn look_for_iterator_item_mistakes(
|
||||
&self,
|
||||
assocs_in_this_method: &[Option<(Span, (DefId, Ty<'tcx>))>],
|
||||
typeck_results: &TypeckResults<'tcx>,
|
||||
type_diffs: &[TypeError<'tcx>],
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
path_segment: &hir::PathSegment<'_>,
|
||||
args: &[hir::Expr<'_>],
|
||||
err: &mut Diagnostic,
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
// Special case for iterator chains, we look at potential failures of `Iterator::Item`
|
||||
// not being `: Clone` and `Iterator::map` calls with spurious trailing `;`.
|
||||
for entry in assocs_in_this_method {
|
||||
let Some((_span, (def_id, ty))) = entry else {
|
||||
continue;
|
||||
};
|
||||
for diff in type_diffs {
|
||||
let Sorts(expected_found) = diff else {
|
||||
continue;
|
||||
};
|
||||
if tcx.is_diagnostic_item(sym::IteratorItem, *def_id)
|
||||
&& path_segment.ident.name == sym::map
|
||||
&& self.can_eq(param_env, expected_found.found, *ty)
|
||||
&& let [arg] = args
|
||||
&& let hir::ExprKind::Closure(closure) = arg.kind
|
||||
{
|
||||
let body = tcx.hir().body(closure.body);
|
||||
if let hir::ExprKind::Block(block, None) = body.value.kind
|
||||
&& let None = block.expr
|
||||
&& let [.., stmt] = block.stmts
|
||||
&& let hir::StmtKind::Semi(expr) = stmt.kind
|
||||
// FIXME: actually check the expected vs found types, but right now
|
||||
// the expected is a projection that we need to resolve.
|
||||
// && let Some(tail_ty) = typeck_results.expr_ty_opt(expr)
|
||||
&& expected_found.found.is_unit()
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
expr.span.shrink_to_hi().with_hi(stmt.span.hi()),
|
||||
"consider removing this semicolon",
|
||||
String::new(),
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
let expr = if let hir::ExprKind::Block(block, None) = body.value.kind
|
||||
&& let Some(expr) = block.expr
|
||||
{
|
||||
expr
|
||||
} else {
|
||||
body.value
|
||||
};
|
||||
if let hir::ExprKind::MethodCall(path_segment, rcvr, [], span) = expr.kind
|
||||
&& path_segment.ident.name == sym::clone
|
||||
&& let Some(expr_ty) = typeck_results.expr_ty_opt(expr)
|
||||
&& let Some(rcvr_ty) = typeck_results.expr_ty_opt(rcvr)
|
||||
&& self.can_eq(param_env, expr_ty, rcvr_ty)
|
||||
&& let ty::Ref(_, ty, _) = expr_ty.kind()
|
||||
{
|
||||
err.span_label(
|
||||
span,
|
||||
format!(
|
||||
"this method call is cloning the reference `{expr_ty}`, not \
|
||||
`{ty}` which doesn't implement `Clone`",
|
||||
),
|
||||
);
|
||||
let ty::Param(..) = ty.kind() else {
|
||||
continue;
|
||||
};
|
||||
let hir = tcx.hir();
|
||||
let node = hir.get_by_def_id(hir.get_parent_item(expr.hir_id).def_id);
|
||||
|
||||
let pred = ty::Binder::dummy(ty::TraitPredicate {
|
||||
trait_ref: ty::TraitRef::from_lang_item(
|
||||
tcx,
|
||||
LangItem::Clone,
|
||||
span,
|
||||
[*ty],
|
||||
),
|
||||
polarity: ty::ImplPolarity::Positive,
|
||||
});
|
||||
let Some(generics) = node.generics() else {
|
||||
continue;
|
||||
};
|
||||
let Some(body_id) = node.body_id() else {
|
||||
continue;
|
||||
};
|
||||
suggest_restriction(
|
||||
tcx,
|
||||
hir.body_owner_def_id(body_id),
|
||||
&generics,
|
||||
&format!("type parameter `{ty}`"),
|
||||
err,
|
||||
node.fn_sig(),
|
||||
None,
|
||||
pred,
|
||||
None,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn point_at_chain(
|
||||
&self,
|
||||
expr: &hir::Expr<'_>,
|
||||
|
@ -3631,13 +3747,22 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
let mut prev_ty = self.resolve_vars_if_possible(
|
||||
typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
|
||||
);
|
||||
while let hir::ExprKind::MethodCall(_path_segment, rcvr_expr, _args, span) = expr.kind {
|
||||
while let hir::ExprKind::MethodCall(path_segment, rcvr_expr, args, span) = expr.kind {
|
||||
// Point at every method call in the chain with the resulting type.
|
||||
// vec![1, 2, 3].iter().map(mapper).sum<i32>()
|
||||
// ^^^^^^ ^^^^^^^^^^^
|
||||
expr = rcvr_expr;
|
||||
let assocs_in_this_method =
|
||||
self.probe_assoc_types_at_expr(&type_diffs, span, prev_ty, expr.hir_id, param_env);
|
||||
self.look_for_iterator_item_mistakes(
|
||||
&assocs_in_this_method,
|
||||
typeck_results,
|
||||
&type_diffs,
|
||||
param_env,
|
||||
path_segment,
|
||||
args,
|
||||
err,
|
||||
);
|
||||
assocs.push(assocs_in_this_method);
|
||||
prev_ty = self.resolve_vars_if_possible(
|
||||
typeck_results.expr_ty_adjusted_opt(expr).unwrap_or(Ty::new_misc_error(tcx)),
|
||||
|
@ -3812,7 +3937,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
// This corresponds to `<ExprTy as Iterator>::Item = _`.
|
||||
let projection = ty::Binder::dummy(ty::PredicateKind::Clause(
|
||||
ty::ClauseKind::Projection(ty::ProjectionPredicate {
|
||||
projection_ty: self.tcx.mk_alias_ty(proj.def_id, args),
|
||||
projection_ty: ty::AliasTy::new(self.tcx, proj.def_id, args),
|
||||
term: ty_var.into(),
|
||||
}),
|
||||
));
|
||||
|
|
|
@ -60,10 +60,7 @@ pub trait TypeErrCtxtExt<'tcx> {
|
|||
suggest_increasing_limit: bool,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>
|
||||
where
|
||||
T: fmt::Display
|
||||
+ TypeFoldable<TyCtxt<'tcx>>
|
||||
+ Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
|
||||
<T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug;
|
||||
T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>;
|
||||
|
||||
fn report_overflow_error<T>(
|
||||
&self,
|
||||
|
@ -73,10 +70,7 @@ pub trait TypeErrCtxtExt<'tcx> {
|
|||
mutate: impl FnOnce(&mut Diagnostic),
|
||||
) -> !
|
||||
where
|
||||
T: fmt::Display
|
||||
+ TypeFoldable<TyCtxt<'tcx>>
|
||||
+ Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
|
||||
<T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug;
|
||||
T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>;
|
||||
|
||||
fn report_overflow_no_abort(&self, obligation: PredicateObligation<'tcx>) -> ErrorGuaranteed;
|
||||
|
||||
|
@ -227,10 +221,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
mutate: impl FnOnce(&mut Diagnostic),
|
||||
) -> !
|
||||
where
|
||||
T: fmt::Display
|
||||
+ TypeFoldable<TyCtxt<'tcx>>
|
||||
+ Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
|
||||
<T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
|
||||
T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>,
|
||||
{
|
||||
let mut err = self.build_overflow_error(predicate, span, suggest_increasing_limit);
|
||||
mutate(&mut err);
|
||||
|
@ -247,10 +238,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
|
|||
suggest_increasing_limit: bool,
|
||||
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>
|
||||
where
|
||||
T: fmt::Display
|
||||
+ TypeFoldable<TyCtxt<'tcx>>
|
||||
+ Print<'tcx, FmtPrinter<'tcx, 'tcx>, Output = FmtPrinter<'tcx, 'tcx>>,
|
||||
<T as Print<'tcx, FmtPrinter<'tcx, 'tcx>>>::Error: std::fmt::Debug,
|
||||
T: fmt::Display + TypeFoldable<TyCtxt<'tcx>> + Print<'tcx, FmtPrinter<'tcx, 'tcx>>,
|
||||
{
|
||||
let predicate = self.resolve_vars_if_possible(predicate.clone());
|
||||
let mut pred_str = predicate.to_string();
|
||||
|
|
|
@ -6,7 +6,6 @@ use rustc_infer::infer::DefineOpaqueTypes;
|
|||
use rustc_infer::traits::ProjectionCacheKey;
|
||||
use rustc_infer::traits::{PolyTraitObligation, SelectionError, TraitEngine};
|
||||
use rustc_middle::mir::interpret::ErrorHandled;
|
||||
use rustc_middle::traits::DefiningAnchor;
|
||||
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::GenericArgsRef;
|
||||
|
@ -626,27 +625,9 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
ty::PredicateKind::Ambiguous => ProcessResult::Unchanged,
|
||||
ty::PredicateKind::AliasRelate(..)
|
||||
if matches!(self.selcx.infcx.defining_use_anchor, DefiningAnchor::Bubble) =>
|
||||
{
|
||||
ProcessResult::Unchanged
|
||||
ty::PredicateKind::AliasRelate(..) => {
|
||||
bug!("AliasRelate is only used for new solver")
|
||||
}
|
||||
ty::PredicateKind::AliasRelate(a, b, relate) => match relate {
|
||||
ty::AliasRelationDirection::Equate => match self
|
||||
.selcx
|
||||
.infcx
|
||||
.at(&obligation.cause, obligation.param_env)
|
||||
.eq(DefineOpaqueTypes::Yes, a, b)
|
||||
{
|
||||
Ok(inf_ok) => ProcessResult::Changed(mk_pending(inf_ok.into_obligations())),
|
||||
Err(_) => ProcessResult::Error(FulfillmentErrorCode::CodeSelectionError(
|
||||
SelectionError::Unimplemented,
|
||||
)),
|
||||
},
|
||||
ty::AliasRelationDirection::Subtype => {
|
||||
bug!("AliasRelate with subtyping is only used for new solver")
|
||||
}
|
||||
},
|
||||
ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, ty)) => {
|
||||
match self.selcx.infcx.at(&obligation.cause, obligation.param_env).eq(
|
||||
DefineOpaqueTypes::No,
|
||||
|
|
|
@ -2072,7 +2072,7 @@ fn confirm_generator_candidate<'cx, 'tcx>(
|
|||
};
|
||||
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: tcx.mk_alias_ty(obligation.predicate.def_id, trait_ref.args),
|
||||
projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args),
|
||||
term: ty.into(),
|
||||
}
|
||||
});
|
||||
|
@ -2116,7 +2116,7 @@ fn confirm_future_candidate<'cx, 'tcx>(
|
|||
debug_assert_eq!(tcx.associated_item(obligation.predicate.def_id).name, sym::Output);
|
||||
|
||||
ty::ProjectionPredicate {
|
||||
projection_ty: tcx.mk_alias_ty(obligation.predicate.def_id, trait_ref.args),
|
||||
projection_ty: ty::AliasTy::new(tcx, obligation.predicate.def_id, trait_ref.args),
|
||||
term: return_ty.into(),
|
||||
}
|
||||
});
|
||||
|
@ -2172,7 +2172,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
|
|||
};
|
||||
|
||||
let predicate =
|
||||
ty::ProjectionPredicate { projection_ty: tcx.mk_alias_ty(item_def_id, args), term };
|
||||
ty::ProjectionPredicate { projection_ty: ty::AliasTy::new(tcx, item_def_id, args), term };
|
||||
|
||||
confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false)
|
||||
.with_addl_obligations(obligations)
|
||||
|
@ -2245,7 +2245,7 @@ fn confirm_callable_candidate<'cx, 'tcx>(
|
|||
flag,
|
||||
)
|
||||
.map_bound(|(trait_ref, ret_type)| ty::ProjectionPredicate {
|
||||
projection_ty: tcx.mk_alias_ty(fn_once_output_def_id, trait_ref.args),
|
||||
projection_ty: ty::AliasTy::new(tcx, fn_once_output_def_id, trait_ref.args),
|
||||
term: ret_type.into(),
|
||||
});
|
||||
|
||||
|
|
|
@ -704,7 +704,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
|||
let ty = traits::normalize_projection_type(
|
||||
self,
|
||||
param_env,
|
||||
tcx.mk_alias_ty(tcx.lang_items().deref_target()?, trait_ref.args),
|
||||
ty::AliasTy::new(tcx, tcx.lang_items().deref_target()?, trait_ref.args),
|
||||
cause.clone(),
|
||||
0,
|
||||
// We're *intentionally* throwing these away,
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue