Auto merge of #118966 - matthiaskrgr:rollup-sdvjwy6, r=matthiaskrgr
Rollup of 3 pull requests Successful merges: - #116888 (Add discussion that concurrent access to the environment is unsafe) - #118888 (Uplift `TypeAndMut` and `ClosureKind` to `rustc_type_ir`) - #118929 (coverage: Tidy up early parts of the instrumentor pass) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
cca2bda07e
@ -378,3 +378,9 @@ pub struct IndicateAnonymousLifetime {
|
||||
pub count: usize,
|
||||
pub suggestion: String,
|
||||
}
|
||||
|
||||
impl IntoDiagnosticArg for type_ir::ClosureKind {
|
||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||
DiagnosticArgValue::Str(self.as_str().into())
|
||||
}
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
debug!(?sig, ?opt_kind);
|
||||
|
||||
let closure_kind_ty = match opt_kind {
|
||||
Some(kind) => kind.to_ty(self.tcx),
|
||||
Some(kind) => Ty::from_closure_kind(self.tcx, kind),
|
||||
|
||||
// Create a type variable (for now) to represent the closure kind.
|
||||
// It will be unified during the upvar inference phase (`upvar.rs`)
|
||||
|
@ -2018,7 +2018,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let new_def_id = self.probe(|_| {
|
||||
let trait_ref = ty::TraitRef::new(
|
||||
self.tcx,
|
||||
call_kind.to_def_id(self.tcx),
|
||||
self.tcx.fn_trait_kind_to_def_id(call_kind)?,
|
||||
[
|
||||
callee_ty,
|
||||
self.next_ty_var(TypeVariableOrigin {
|
||||
|
@ -261,7 +261,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
// Unify the (as yet unbound) type variable in the closure
|
||||
// args with the kind we inferred.
|
||||
let closure_kind_ty = closure_args.as_closure().kind_ty();
|
||||
self.demand_eqtype(span, closure_kind.to_ty(self.tcx), closure_kind_ty);
|
||||
self.demand_eqtype(
|
||||
span,
|
||||
Ty::from_closure_kind(self.tcx, closure_kind),
|
||||
closure_kind_ty,
|
||||
);
|
||||
|
||||
// If we have an origin, store it.
|
||||
if let Some(mut origin) = origin {
|
||||
|
@ -36,6 +36,17 @@ impl<'tcx> TyCtxt<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a [`ty::ClosureKind`], get the [`DefId`] of its corresponding `Fn`-family
|
||||
/// trait, if it is defined.
|
||||
pub fn fn_trait_kind_to_def_id(self, kind: ty::ClosureKind) -> Option<DefId> {
|
||||
let items = self.lang_items();
|
||||
match kind {
|
||||
ty::ClosureKind::Fn => items.fn_trait(),
|
||||
ty::ClosureKind::FnMut => items.fn_mut_trait(),
|
||||
ty::ClosureKind::FnOnce => items.fn_once_trait(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if `id` is a `DefId` of [`Fn`], [`FnMut`] or [`FnOnce`] traits.
|
||||
pub fn is_fn_trait(self, id: DefId) -> bool {
|
||||
self.fn_trait_kind_from_def_id(id).is_some()
|
||||
|
@ -15,7 +15,6 @@ TrivialTypeTraversalImpls! {
|
||||
UserTypeAnnotationIndex,
|
||||
BorrowKind,
|
||||
CastKind,
|
||||
hir::Movability,
|
||||
BasicBlock,
|
||||
SwitchTargets,
|
||||
CoroutineKind,
|
||||
|
@ -7,14 +7,13 @@ use std::fmt::Write;
|
||||
|
||||
use crate::query::Providers;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_errors::{DiagnosticArgValue, IntoDiagnosticArg};
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::{self as hir, LangItem};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_span::def_id::LocalDefIdMap;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::{Span, Symbol};
|
||||
|
||||
use super::{Ty, TyCtxt};
|
||||
use super::TyCtxt;
|
||||
|
||||
use self::BorrowKind::*;
|
||||
|
||||
@ -73,72 +72,6 @@ pub type RootVariableMinCaptureList<'tcx> = FxIndexMap<hir::HirId, MinCaptureLis
|
||||
/// Part of `MinCaptureInformationMap`; List of `CapturePlace`s.
|
||||
pub type MinCaptureList<'tcx> = Vec<CapturedPlace<'tcx>>;
|
||||
|
||||
/// Represents the various closure traits in the language. This
|
||||
/// will determine the type of the environment (`self`, in the
|
||||
/// desugaring) argument that the closure expects.
|
||||
///
|
||||
/// You can get the environment type of a closure using
|
||||
/// `tcx.closure_env_ty()`.
|
||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug, TyEncodable, TyDecodable)]
|
||||
#[derive(HashStable)]
|
||||
pub enum ClosureKind {
|
||||
// Warning: Ordering is significant here! The ordering is chosen
|
||||
// because the trait Fn is a subtrait of FnMut and so in turn, and
|
||||
// hence we order it so that Fn < FnMut < FnOnce.
|
||||
Fn,
|
||||
FnMut,
|
||||
FnOnce,
|
||||
}
|
||||
|
||||
impl ClosureKind {
|
||||
/// This is the initial value used when doing upvar inference.
|
||||
pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn;
|
||||
|
||||
pub const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
ClosureKind::Fn => "Fn",
|
||||
ClosureKind::FnMut => "FnMut",
|
||||
ClosureKind::FnOnce => "FnOnce",
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if a type that impls this closure kind
|
||||
/// must also implement `other`.
|
||||
pub fn extends(self, other: ty::ClosureKind) -> bool {
|
||||
self <= other
|
||||
}
|
||||
|
||||
/// Converts `self` to a [`DefId`] of the corresponding trait.
|
||||
///
|
||||
/// Note: the inverse of this function is [`TyCtxt::fn_trait_kind_from_def_id`].
|
||||
pub fn to_def_id(&self, tcx: TyCtxt<'_>) -> DefId {
|
||||
tcx.require_lang_item(
|
||||
match self {
|
||||
ClosureKind::Fn => LangItem::Fn,
|
||||
ClosureKind::FnMut => LangItem::FnMut,
|
||||
ClosureKind::FnOnce => LangItem::FnOnce,
|
||||
},
|
||||
None,
|
||||
)
|
||||
}
|
||||
|
||||
/// Returns the representative scalar type for this closure kind.
|
||||
/// See `Ty::to_opt_closure_kind` for more details.
|
||||
pub fn to_ty<'tcx>(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> {
|
||||
match self {
|
||||
ClosureKind::Fn => tcx.types.i8,
|
||||
ClosureKind::FnMut => tcx.types.i16,
|
||||
ClosureKind::FnOnce => tcx.types.i32,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoDiagnosticArg for ClosureKind {
|
||||
fn into_diagnostic_arg(self) -> DiagnosticArgValue<'static> {
|
||||
DiagnosticArgValue::Str(self.as_str().into())
|
||||
}
|
||||
}
|
||||
|
||||
/// A composite describing a `Place` that is captured by a closure.
|
||||
#[derive(PartialEq, Clone, Debug, TyEncodable, TyDecodable, HashStable)]
|
||||
#[derive(TypeFoldable, TypeVisitable)]
|
||||
|
@ -28,7 +28,7 @@ use crate::ty::{
|
||||
self, AdtDef, AdtDefData, AdtKind, Binder, Clause, Const, ConstData, GenericParamDefKind,
|
||||
ImplPolarity, List, ParamConst, ParamTy, PolyExistentialPredicate, PolyFnSig, Predicate,
|
||||
PredicateKind, Region, RegionKind, ReprOptions, TraitObjectVisitor, Ty, TyKind, TyVid,
|
||||
TypeAndMut, Visibility,
|
||||
Visibility,
|
||||
};
|
||||
use crate::ty::{GenericArg, GenericArgs, GenericArgsRef};
|
||||
use rustc_ast::{self as ast, attr};
|
||||
@ -88,7 +88,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
||||
type Term = ty::Term<'tcx>;
|
||||
|
||||
type Binder<T> = Binder<'tcx, T>;
|
||||
type TypeAndMut = TypeAndMut<'tcx>;
|
||||
type CanonicalVars = CanonicalVarInfos<'tcx>;
|
||||
|
||||
type Ty = Ty<'tcx>;
|
||||
@ -128,12 +127,6 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
||||
type CoercePredicate = ty::CoercePredicate<'tcx>;
|
||||
type ClosureKind = ty::ClosureKind;
|
||||
|
||||
fn ty_and_mut_to_parts(
|
||||
TypeAndMut { ty, mutbl }: TypeAndMut<'tcx>,
|
||||
) -> (Self::Ty, ty::Mutability) {
|
||||
(ty, mutbl)
|
||||
}
|
||||
|
||||
fn mk_canonical_var_infos(self, infos: &[ty::CanonicalVarInfo<Self>]) -> Self::CanonicalVars {
|
||||
self.mk_canonical_var_infos(infos)
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ pub use self::binding::BindingMode;
|
||||
pub use self::binding::BindingMode::*;
|
||||
pub use self::closure::{
|
||||
is_ancestor_or_same_capture, place_to_string_for_capture, BorrowKind, CaptureInfo,
|
||||
CapturedPlace, ClosureKind, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList,
|
||||
CapturedPlace, ClosureTypeInfo, MinCaptureInformationMap, MinCaptureList,
|
||||
RootVariableMinCaptureList, UpvarCapture, UpvarId, UpvarPath, CAPTURE_STRUCT_LOCAL,
|
||||
};
|
||||
pub use self::consts::{Const, ConstData, ConstInt, Expr, ScalarInt, UnevaluatedConst, ValTree};
|
||||
|
@ -1680,7 +1680,7 @@ pub trait PrettyPrinter<'tcx>: Printer<'tcx> + fmt::Write {
|
||||
self.wrap_binder(&sig, |sig, cx| {
|
||||
define_scoped_cx!(cx);
|
||||
|
||||
p!(print(kind), "(");
|
||||
p!(write("{kind}("));
|
||||
for (i, arg) in sig.inputs()[0].tuple_fields().iter().enumerate() {
|
||||
if i > 0 {
|
||||
p!(", ");
|
||||
@ -2754,6 +2754,10 @@ forward_display_to_print! {
|
||||
define_print! {
|
||||
(self, cx):
|
||||
|
||||
ty::TypeAndMut<'tcx> {
|
||||
p!(write("{}", self.mutbl.prefix_str()), print(self.ty))
|
||||
}
|
||||
|
||||
ty::ClauseKind<'tcx> {
|
||||
match *self {
|
||||
ty::ClauseKind::Trait(ref data) => {
|
||||
@ -2799,10 +2803,6 @@ define_print_and_forward_display! {
|
||||
p!("{{", comma_sep(self.iter()), "}}")
|
||||
}
|
||||
|
||||
ty::TypeAndMut<'tcx> {
|
||||
p!(write("{}", self.mutbl.prefix_str()), print(self.ty))
|
||||
}
|
||||
|
||||
ty::ExistentialTraitRef<'tcx> {
|
||||
// Use a type that can't appear in defaults of type parameters.
|
||||
let dummy_self = Ty::new_fresh(cx.tcx(),0);
|
||||
@ -2943,10 +2943,6 @@ define_print_and_forward_display! {
|
||||
}
|
||||
}
|
||||
|
||||
ty::ClosureKind {
|
||||
p!(write("{}", self.as_str()))
|
||||
}
|
||||
|
||||
ty::Predicate<'tcx> {
|
||||
p!(print(self.kind()))
|
||||
}
|
||||
|
@ -440,7 +440,6 @@ TrivialTypeTraversalImpls! {
|
||||
// interners).
|
||||
TrivialTypeTraversalAndLiftImpls! {
|
||||
::rustc_hir::def_id::DefId,
|
||||
::rustc_hir::Mutability,
|
||||
::rustc_hir::Unsafety,
|
||||
::rustc_target::spec::abi::Abi,
|
||||
crate::ty::ClosureKind,
|
||||
|
@ -44,23 +44,18 @@ use rustc_type_ir::PredicateKind as IrPredicateKind;
|
||||
use rustc_type_ir::RegionKind as IrRegionKind;
|
||||
use rustc_type_ir::TyKind as IrTyKind;
|
||||
use rustc_type_ir::TyKind::*;
|
||||
use rustc_type_ir::TypeAndMut as IrTypeAndMut;
|
||||
|
||||
use super::GenericParamDefKind;
|
||||
|
||||
// Re-export the `TyKind` from `rustc_type_ir` here for convenience
|
||||
// Re-export and re-parameterize some `I = TyCtxt<'tcx>` types here
|
||||
#[rustc_diagnostic_item = "TyKind"]
|
||||
pub type TyKind<'tcx> = IrTyKind<TyCtxt<'tcx>>;
|
||||
pub type RegionKind<'tcx> = IrRegionKind<TyCtxt<'tcx>>;
|
||||
pub type ConstKind<'tcx> = IrConstKind<TyCtxt<'tcx>>;
|
||||
pub type PredicateKind<'tcx> = IrPredicateKind<TyCtxt<'tcx>>;
|
||||
pub type ClauseKind<'tcx> = IrClauseKind<TyCtxt<'tcx>>;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, TyEncodable, TyDecodable)]
|
||||
#[derive(HashStable, TypeFoldable, TypeVisitable, Lift)]
|
||||
pub struct TypeAndMut<'tcx> {
|
||||
pub ty: Ty<'tcx>,
|
||||
pub mutbl: hir::Mutability,
|
||||
}
|
||||
pub type TypeAndMut<'tcx> = IrTypeAndMut<TyCtxt<'tcx>>;
|
||||
|
||||
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, TyEncodable, TyDecodable, Copy)]
|
||||
#[derive(HashStable)]
|
||||
@ -2818,6 +2813,15 @@ impl<'tcx> Ty<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Inverse of [`Ty::to_opt_closure_kind`].
|
||||
pub fn from_closure_kind(tcx: TyCtxt<'tcx>, kind: ty::ClosureKind) -> Ty<'tcx> {
|
||||
match kind {
|
||||
ty::ClosureKind::Fn => tcx.types.i8,
|
||||
ty::ClosureKind::FnMut => tcx.types.i16,
|
||||
ty::ClosureKind::FnOnce => tcx.types.i32,
|
||||
}
|
||||
}
|
||||
|
||||
/// Fast path helper for testing if a type is `Sized`.
|
||||
///
|
||||
/// Returning true means the type is known to be sized. Returning
|
||||
|
@ -13,7 +13,6 @@ use self::spans::CoverageSpans;
|
||||
|
||||
use crate::MirPass;
|
||||
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_middle::hir;
|
||||
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
|
||||
use rustc_middle::mir::coverage::*;
|
||||
@ -22,9 +21,9 @@ use rustc_middle::mir::{
|
||||
TerminatorKind,
|
||||
};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::def_id::DefId;
|
||||
use rustc_span::def_id::LocalDefId;
|
||||
use rustc_span::source_map::SourceMap;
|
||||
use rustc_span::{ExpnKind, SourceFile, Span, Symbol};
|
||||
use rustc_span::{ExpnKind, Span, Symbol};
|
||||
|
||||
/// Inserts `StatementKind::Coverage` statements that either instrument the binary with injected
|
||||
/// counters, via intrinsic `llvm.instrprof.increment`, and/or inject metadata used during codegen
|
||||
@ -39,31 +38,19 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, mir_body: &mut mir::Body<'tcx>) {
|
||||
let mir_source = mir_body.source;
|
||||
|
||||
// If the InstrumentCoverage pass is called on promoted MIRs, skip them.
|
||||
// See: https://github.com/rust-lang/rust/pull/73011#discussion_r438317601
|
||||
if mir_source.promoted.is_some() {
|
||||
trace!(
|
||||
"InstrumentCoverage skipped for {:?} (already promoted for Miri evaluation)",
|
||||
mir_source.def_id()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let is_fn_like =
|
||||
tcx.hir_node_by_def_id(mir_source.def_id().expect_local()).fn_kind().is_some();
|
||||
|
||||
// Only instrument functions, methods, and closures (not constants since they are evaluated
|
||||
// at compile time by Miri).
|
||||
// FIXME(#73156): Handle source code coverage in const eval, but note, if and when const
|
||||
// expressions get coverage spans, we will probably have to "carve out" space for const
|
||||
// expressions from coverage spans in enclosing MIR's, like we do for closures. (That might
|
||||
// be tricky if const expressions have no corresponding statements in the enclosing MIR.
|
||||
// Closures are carved out by their initial `Assign` statement.)
|
||||
if !is_fn_like {
|
||||
trace!("InstrumentCoverage skipped for {:?} (not an fn-like)", mir_source.def_id());
|
||||
// This pass runs after MIR promotion, but before promoted MIR starts to
|
||||
// be transformed, so it should never see promoted MIR.
|
||||
assert!(mir_source.promoted.is_none());
|
||||
|
||||
let def_id = mir_source.def_id().expect_local();
|
||||
|
||||
if !is_eligible_for_coverage(tcx, def_id) {
|
||||
trace!("InstrumentCoverage skipped for {def_id:?} (not eligible)");
|
||||
return;
|
||||
}
|
||||
|
||||
// An otherwise-eligible function is still skipped if its start block
|
||||
// is known to be unreachable.
|
||||
match mir_body.basic_blocks[mir::START_BLOCK].terminator().kind {
|
||||
TerminatorKind::Unreachable => {
|
||||
trace!("InstrumentCoverage skipped for unreachable `START_BLOCK`");
|
||||
@ -72,21 +59,15 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let codegen_fn_attrs = tcx.codegen_fn_attrs(mir_source.def_id());
|
||||
if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
|
||||
return;
|
||||
}
|
||||
|
||||
trace!("InstrumentCoverage starting for {:?}", mir_source.def_id());
|
||||
trace!("InstrumentCoverage starting for {def_id:?}");
|
||||
Instrumentor::new(tcx, mir_body).inject_counters();
|
||||
trace!("InstrumentCoverage done for {:?}", mir_source.def_id());
|
||||
trace!("InstrumentCoverage done for {def_id:?}");
|
||||
}
|
||||
}
|
||||
|
||||
struct Instrumentor<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
mir_body: &'a mut mir::Body<'tcx>,
|
||||
source_file: Lrc<SourceFile>,
|
||||
fn_sig_span: Span,
|
||||
body_span: Span,
|
||||
function_source_hash: u64,
|
||||
@ -96,37 +77,17 @@ struct Instrumentor<'a, 'tcx> {
|
||||
|
||||
impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self {
|
||||
let source_map = tcx.sess.source_map();
|
||||
let def_id = mir_body.source.def_id();
|
||||
let (some_fn_sig, hir_body) = fn_sig_and_body(tcx, def_id);
|
||||
let hir_info @ ExtractedHirInfo { function_source_hash, fn_sig_span, body_span } =
|
||||
extract_hir_info(tcx, mir_body.source.def_id().expect_local());
|
||||
|
||||
let body_span = get_body_span(tcx, hir_body, mir_body);
|
||||
debug!(?hir_info, "instrumenting {:?}", mir_body.source.def_id());
|
||||
|
||||
let source_file = source_map.lookup_source_file(body_span.lo());
|
||||
let fn_sig_span = match some_fn_sig.filter(|fn_sig| {
|
||||
fn_sig.span.eq_ctxt(body_span)
|
||||
&& Lrc::ptr_eq(&source_file, &source_map.lookup_source_file(fn_sig.span.lo()))
|
||||
}) {
|
||||
Some(fn_sig) => fn_sig.span.with_hi(body_span.lo()),
|
||||
None => body_span.shrink_to_lo(),
|
||||
};
|
||||
|
||||
debug!(
|
||||
"instrumenting {}: {:?}, fn sig span: {:?}, body span: {:?}",
|
||||
if tcx.is_closure(def_id) { "closure" } else { "function" },
|
||||
def_id,
|
||||
fn_sig_span,
|
||||
body_span
|
||||
);
|
||||
|
||||
let function_source_hash = hash_mir_source(tcx, hir_body);
|
||||
let basic_coverage_blocks = CoverageGraph::from_mir(mir_body);
|
||||
let coverage_counters = CoverageCounters::new(&basic_coverage_blocks);
|
||||
|
||||
Self {
|
||||
tcx,
|
||||
mir_body,
|
||||
source_file,
|
||||
fn_sig_span,
|
||||
body_span,
|
||||
function_source_hash,
|
||||
@ -136,15 +97,12 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
||||
}
|
||||
|
||||
fn inject_counters(&'a mut self) {
|
||||
let fn_sig_span = self.fn_sig_span;
|
||||
let body_span = self.body_span;
|
||||
|
||||
////////////////////////////////////////////////////
|
||||
// Compute coverage spans from the `CoverageGraph`.
|
||||
let coverage_spans = CoverageSpans::generate_coverage_spans(
|
||||
self.mir_body,
|
||||
fn_sig_span,
|
||||
body_span,
|
||||
self.fn_sig_span,
|
||||
self.body_span,
|
||||
&self.basic_coverage_blocks,
|
||||
);
|
||||
|
||||
@ -177,9 +135,10 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
||||
let source_map = self.tcx.sess.source_map();
|
||||
let body_span = self.body_span;
|
||||
|
||||
let source_file = source_map.lookup_source_file(body_span.lo());
|
||||
use rustc_session::RemapFileNameExt;
|
||||
let file_name =
|
||||
Symbol::intern(&self.source_file.name.for_codegen(self.tcx.sess).to_string_lossy());
|
||||
Symbol::intern(&source_file.name.for_codegen(self.tcx.sess).to_string_lossy());
|
||||
|
||||
let mut mappings = Vec::new();
|
||||
|
||||
@ -325,27 +284,75 @@ fn make_code_region(
|
||||
}
|
||||
}
|
||||
|
||||
fn fn_sig_and_body(
|
||||
tcx: TyCtxt<'_>,
|
||||
def_id: DefId,
|
||||
) -> (Option<&rustc_hir::FnSig<'_>>, &rustc_hir::Body<'_>) {
|
||||
fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
// Only instrument functions, methods, and closures (not constants since they are evaluated
|
||||
// at compile time by Miri).
|
||||
// FIXME(#73156): Handle source code coverage in const eval, but note, if and when const
|
||||
// expressions get coverage spans, we will probably have to "carve out" space for const
|
||||
// expressions from coverage spans in enclosing MIR's, like we do for closures. (That might
|
||||
// be tricky if const expressions have no corresponding statements in the enclosing MIR.
|
||||
// Closures are carved out by their initial `Assign` statement.)
|
||||
if !tcx.def_kind(def_id).is_fn_like() {
|
||||
trace!("InstrumentCoverage skipped for {def_id:?} (not an fn-like)");
|
||||
return false;
|
||||
}
|
||||
|
||||
if tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_COVERAGE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
|
||||
/// Function information extracted from HIR by the coverage instrumentor.
|
||||
#[derive(Debug)]
|
||||
struct ExtractedHirInfo {
|
||||
function_source_hash: u64,
|
||||
fn_sig_span: Span,
|
||||
body_span: Span,
|
||||
}
|
||||
|
||||
fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHirInfo {
|
||||
// FIXME(#79625): Consider improving MIR to provide the information needed, to avoid going back
|
||||
// to HIR for it.
|
||||
let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
|
||||
|
||||
let hir_node = tcx.hir_node_by_def_id(def_id);
|
||||
let (_, fn_body_id) =
|
||||
hir::map::associated_body(hir_node).expect("HIR node is a function with body");
|
||||
(hir_node.fn_sig(), tcx.hir().body(fn_body_id))
|
||||
let hir_body = tcx.hir().body(fn_body_id);
|
||||
|
||||
let body_span = get_body_span(tcx, hir_body, def_id);
|
||||
|
||||
// The actual signature span is only used if it has the same context and
|
||||
// filename as the body, and precedes the body.
|
||||
let maybe_fn_sig_span = hir_node.fn_sig().map(|fn_sig| fn_sig.span);
|
||||
let fn_sig_span = maybe_fn_sig_span
|
||||
.filter(|&fn_sig_span| {
|
||||
let source_map = tcx.sess.source_map();
|
||||
let file_idx = |span: Span| source_map.lookup_source_file_idx(span.lo());
|
||||
|
||||
fn_sig_span.eq_ctxt(body_span)
|
||||
&& fn_sig_span.hi() <= body_span.lo()
|
||||
&& file_idx(fn_sig_span) == file_idx(body_span)
|
||||
})
|
||||
// If so, extend it to the start of the body span.
|
||||
.map(|fn_sig_span| fn_sig_span.with_hi(body_span.lo()))
|
||||
// Otherwise, create a dummy signature span at the start of the body.
|
||||
.unwrap_or_else(|| body_span.shrink_to_lo());
|
||||
|
||||
let function_source_hash = hash_mir_source(tcx, hir_body);
|
||||
|
||||
ExtractedHirInfo { function_source_hash, fn_sig_span, body_span }
|
||||
}
|
||||
|
||||
fn get_body_span<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
hir_body: &rustc_hir::Body<'tcx>,
|
||||
mir_body: &mut mir::Body<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
) -> Span {
|
||||
let mut body_span = hir_body.value.span;
|
||||
let def_id = mir_body.source.def_id();
|
||||
|
||||
if tcx.is_closure(def_id) {
|
||||
if tcx.is_closure(def_id.to_def_id()) {
|
||||
// If the MIR function is a closure, and if the closure body span
|
||||
// starts from a macro, but it's content is not in that macro, try
|
||||
// to find a non-macro callsite, and instrument the spans there
|
||||
|
@ -3,8 +3,8 @@ use std::fmt::Debug;
|
||||
use std::hash::Hash;
|
||||
|
||||
use crate::{
|
||||
BoundVar, CanonicalVarInfo, ConstKind, DebruijnIndex, DebugWithInfcx, Mutability, RegionKind,
|
||||
TyKind, UniverseIndex,
|
||||
BoundVar, CanonicalVarInfo, ConstKind, DebruijnIndex, DebugWithInfcx, RegionKind, TyKind,
|
||||
UniverseIndex,
|
||||
};
|
||||
|
||||
pub trait Interner: Sized {
|
||||
@ -20,7 +20,6 @@ pub trait Interner: Sized {
|
||||
type Term: Copy + Debug + Hash + Ord;
|
||||
|
||||
type Binder<T>;
|
||||
type TypeAndMut: Copy + Debug + Hash + Ord;
|
||||
type CanonicalVars: Copy + Debug + Hash + Eq + IntoIterator<Item = CanonicalVarInfo<Self>>;
|
||||
|
||||
// Kinds of tys
|
||||
@ -81,8 +80,6 @@ pub trait Interner: Sized {
|
||||
type CoercePredicate: Copy + Debug + Hash + Eq;
|
||||
type ClosureKind: Copy + Debug + Hash + Eq;
|
||||
|
||||
fn ty_and_mut_to_parts(ty_and_mut: Self::TypeAndMut) -> (Self::Ty, Mutability);
|
||||
|
||||
fn mk_canonical_var_infos(self, infos: &[CanonicalVarInfo<Self>]) -> Self::CanonicalVars;
|
||||
|
||||
// FIXME: We should not have all these constructors on `Interner`, but as functions on some trait.
|
||||
|
@ -359,3 +359,45 @@ rustc_index::newtype_index! {
|
||||
#[gate_rustc_only]
|
||||
pub struct BoundVar {}
|
||||
}
|
||||
|
||||
/// Represents the various closure traits in the language. This
|
||||
/// will determine the type of the environment (`self`, in the
|
||||
/// desugaring) argument that the closure expects.
|
||||
///
|
||||
/// You can get the environment type of a closure using
|
||||
/// `tcx.closure_env_ty()`.
|
||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
|
||||
#[cfg_attr(feature = "nightly", derive(Encodable, Decodable, HashStable_NoContext))]
|
||||
pub enum ClosureKind {
|
||||
// Warning: Ordering is significant here! The ordering is chosen
|
||||
// because the trait Fn is a subtrait of FnMut and so in turn, and
|
||||
// hence we order it so that Fn < FnMut < FnOnce.
|
||||
Fn,
|
||||
FnMut,
|
||||
FnOnce,
|
||||
}
|
||||
|
||||
impl ClosureKind {
|
||||
/// This is the initial value used when doing upvar inference.
|
||||
pub const LATTICE_BOTTOM: ClosureKind = ClosureKind::Fn;
|
||||
|
||||
pub const fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
ClosureKind::Fn => "Fn",
|
||||
ClosureKind::FnMut => "FnMut",
|
||||
ClosureKind::FnOnce => "FnOnce",
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `true` if a type that impls this closure kind
|
||||
/// must also implement `other`.
|
||||
pub fn extends(self, other: ClosureKind) -> bool {
|
||||
self <= other
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for ClosureKind {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.as_str().fmt(f)
|
||||
}
|
||||
}
|
||||
|
@ -51,4 +51,6 @@ TrivialTypeTraversalImpls! {
|
||||
crate::DebruijnIndex,
|
||||
crate::AliasRelationDirection,
|
||||
crate::UniverseIndex,
|
||||
crate::Mutability,
|
||||
crate::Movability,
|
||||
}
|
||||
|
@ -4,6 +4,8 @@ use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::unify::{EqUnifyValue, UnifyKey};
|
||||
use std::fmt;
|
||||
|
||||
use crate::fold::{FallibleTypeFolder, TypeFoldable};
|
||||
use crate::visit::{TypeVisitable, TypeVisitor};
|
||||
use crate::Interner;
|
||||
use crate::{DebruijnIndex, DebugWithInfcx, InferCtxtLike, WithInfcx};
|
||||
|
||||
@ -158,7 +160,7 @@ pub enum TyKind<I: Interner> {
|
||||
Slice(I::Ty),
|
||||
|
||||
/// A raw pointer. Written as `*mut T` or `*const T`
|
||||
RawPtr(I::TypeAndMut),
|
||||
RawPtr(TypeAndMut<I>),
|
||||
|
||||
/// A reference; a pointer with an associated lifetime. Written as
|
||||
/// `&'a mut T` or `&'a T`.
|
||||
@ -410,8 +412,7 @@ impl<I: Interner> DebugWithInfcx<I> for TyKind<I> {
|
||||
Str => write!(f, "str"),
|
||||
Array(t, c) => write!(f, "[{:?}; {:?}]", &this.wrap(t), &this.wrap(c)),
|
||||
Slice(t) => write!(f, "[{:?}]", &this.wrap(t)),
|
||||
RawPtr(p) => {
|
||||
let (ty, mutbl) = I::ty_and_mut_to_parts(*p);
|
||||
RawPtr(TypeAndMut { ty, mutbl }) => {
|
||||
match mutbl {
|
||||
Mutability::Mut => write!(f, "*mut "),
|
||||
Mutability::Not => write!(f, "*const "),
|
||||
@ -831,3 +832,42 @@ impl<I: Interner> DebugWithInfcx<I> for InferTy {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(derivative::Derivative)]
|
||||
#[derivative(
|
||||
Clone(bound = ""),
|
||||
Copy(bound = ""),
|
||||
PartialOrd(bound = ""),
|
||||
Ord(bound = ""),
|
||||
PartialEq(bound = ""),
|
||||
Eq(bound = ""),
|
||||
Hash(bound = ""),
|
||||
Debug(bound = "")
|
||||
)]
|
||||
#[cfg_attr(feature = "nightly", derive(TyEncodable, TyDecodable, HashStable_NoContext))]
|
||||
pub struct TypeAndMut<I: Interner> {
|
||||
pub ty: I::Ty,
|
||||
pub mutbl: Mutability,
|
||||
}
|
||||
|
||||
impl<I: Interner> TypeFoldable<I> for TypeAndMut<I>
|
||||
where
|
||||
I::Ty: TypeFoldable<I>,
|
||||
{
|
||||
fn try_fold_with<F: FallibleTypeFolder<I>>(self, folder: &mut F) -> Result<Self, F::Error> {
|
||||
Ok(TypeAndMut {
|
||||
ty: self.ty.try_fold_with(folder)?,
|
||||
mutbl: self.mutbl.try_fold_with(folder)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interner> TypeVisitable<I> for TypeAndMut<I>
|
||||
where
|
||||
I::Ty: TypeVisitable<I>,
|
||||
{
|
||||
fn visit_with<V: TypeVisitor<I>>(&self, visitor: &mut V) -> std::ops::ControlFlow<V::BreakTy> {
|
||||
self.ty.visit_with(visitor)?;
|
||||
self.mutbl.visit_with(visitor)
|
||||
}
|
||||
}
|
||||
|
@ -313,17 +313,32 @@ impl Error for VarError {
|
||||
/// Sets the environment variable `key` to the value `value` for the currently running
|
||||
/// process.
|
||||
///
|
||||
/// Note that while concurrent access to environment variables is safe in Rust,
|
||||
/// some platforms only expose inherently unsafe non-threadsafe APIs for
|
||||
/// inspecting the environment. As a result, extra care needs to be taken when
|
||||
/// auditing calls to unsafe external FFI functions to ensure that any external
|
||||
/// environment accesses are properly synchronized with accesses in Rust.
|
||||
/// # Safety
|
||||
///
|
||||
/// Even though this function is currently not marked as `unsafe`, it needs to
|
||||
/// be because invoking it can cause undefined behaviour. The function will be
|
||||
/// marked `unsafe` in a future version of Rust. This is tracked in
|
||||
/// [rust#27970](https://github.com/rust-lang/rust/issues/27970).
|
||||
///
|
||||
/// This function is safe to call in a single-threaded program.
|
||||
///
|
||||
/// In multi-threaded programs, you must ensure that are no other threads
|
||||
/// concurrently writing or *reading*(!) from the environment through functions
|
||||
/// other than the ones in this module. You are responsible for figuring out
|
||||
/// how to achieve this, but we strongly suggest not using `set_var` or
|
||||
/// `remove_var` in multi-threaded programs at all.
|
||||
///
|
||||
/// Most C libraries, including libc itself do not advertise which functions
|
||||
/// read from the environment. Even functions from the Rust standard library do
|
||||
/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`].
|
||||
///
|
||||
/// Discussion of this unsafety on Unix may be found in:
|
||||
///
|
||||
/// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188)
|
||||
/// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2)
|
||||
///
|
||||
/// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function may panic if `key` is empty, contains an ASCII equals sign `'='`
|
||||
@ -351,17 +366,32 @@ fn _set_var(key: &OsStr, value: &OsStr) {
|
||||
|
||||
/// Removes an environment variable from the environment of the currently running process.
|
||||
///
|
||||
/// Note that while concurrent access to environment variables is safe in Rust,
|
||||
/// some platforms only expose inherently unsafe non-threadsafe APIs for
|
||||
/// inspecting the environment. As a result extra care needs to be taken when
|
||||
/// auditing calls to unsafe external FFI functions to ensure that any external
|
||||
/// environment accesses are properly synchronized with accesses in Rust.
|
||||
/// # Safety
|
||||
///
|
||||
/// Even though this function is currently not marked as `unsafe`, it needs to
|
||||
/// be because invoking it can cause undefined behaviour. The function will be
|
||||
/// marked `unsafe` in a future version of Rust. This is tracked in
|
||||
/// [rust#27970](https://github.com/rust-lang/rust/issues/27970).
|
||||
///
|
||||
/// This function is safe to call in a single-threaded program.
|
||||
///
|
||||
/// In multi-threaded programs, you must ensure that are no other threads
|
||||
/// concurrently writing or *reading*(!) from the environment through functions
|
||||
/// other than the ones in this module. You are responsible for figuring out
|
||||
/// how to achieve this, but we strongly suggest not using `set_var` or
|
||||
/// `remove_var` in multi-threaded programs at all.
|
||||
///
|
||||
/// Most C libraries, including libc itself do not advertise which functions
|
||||
/// read from the environment. Even functions from the Rust standard library do
|
||||
/// that, e.g. for DNS lookups from [`std::net::ToSocketAddrs`].
|
||||
///
|
||||
/// Discussion of this unsafety on Unix may be found in:
|
||||
///
|
||||
/// - [Austin Group Bugzilla](https://austingroupbugs.net/view.php?id=188)
|
||||
/// - [GNU C library Bugzilla](https://sourceware.org/bugzilla/show_bug.cgi?id=15607#c2)
|
||||
///
|
||||
/// [`std::net::ToSocketAddrs`]: crate::net::ToSocketAddrs
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// This function may panic if `key` is empty, contains an ASCII equals sign
|
||||
|
@ -10,8 +10,8 @@ use super::AS_PTR_CAST_MUT;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to: Ty<'_>) {
|
||||
if let ty::RawPtr(
|
||||
ptrty @ TypeAndMut {
|
||||
mutbl: Mutability::Mut, ..
|
||||
TypeAndMut {
|
||||
mutbl: Mutability::Mut, ty: ptrty,
|
||||
},
|
||||
) = cast_to.kind()
|
||||
&& let ty::RawPtr(TypeAndMut {
|
||||
@ -34,7 +34,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
|
||||
cx,
|
||||
AS_PTR_CAST_MUT,
|
||||
expr.span,
|
||||
&format!("casting the result of `as_ptr` to *{ptrty}"),
|
||||
&format!("casting the result of `as_ptr` to *mut {ptrty}"),
|
||||
"replace with",
|
||||
format!("{recv}.as_mut_ptr()"),
|
||||
applicability,
|
||||
|
Loading…
x
Reference in New Issue
Block a user