Auto merge of #134332 - Zalathar:rollup-oe23hkw, r=Zalathar
Rollup of 7 pull requests Successful merges: - #130361 (std::net: Solaris supports `SOCK_CLOEXEC` as well since 11.4.) - #133406 (Add value accessor methods to `Mutex` and `RwLock`) - #133633 (don't show the full linker args unless `--verbose` is passed) - #134285 (Add some convenience helper methods on `hir::Safety`) - #134310 (Add clarity to the examples of some `Vec` & `VecDeque` methods) - #134313 (Don't make a def id for `impl_trait_in_bindings`) - #134315 (A couple of polonius fact generation cleanups) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
0894fb0fbf
56 changed files with 1030 additions and 471 deletions
|
|
@ -116,7 +116,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
|
|||
let var_origins = infcx.get_region_var_origins();
|
||||
|
||||
// If requested, emit legacy polonius facts.
|
||||
polonius::emit_facts(
|
||||
polonius::legacy::emit_facts(
|
||||
&mut all_facts,
|
||||
infcx.tcx,
|
||||
location_table,
|
||||
|
|
|
|||
184
compiler/rustc_borrowck/src/polonius/legacy/mod.rs
Normal file
184
compiler/rustc_borrowck/src/polonius/legacy/mod.rs
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
//! Functions dedicated to fact generation for the `-Zpolonius=legacy` datalog implementation.
|
||||
//!
|
||||
//! Will be removed in the future, once the in-tree `-Zpolonius=next` implementation reaches feature
|
||||
//! parity.
|
||||
|
||||
use rustc_middle::mir::{Body, LocalKind, Location, START_BLOCK};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::borrow_set::BorrowSet;
|
||||
use crate::facts::{AllFacts, PoloniusRegionVid};
|
||||
use crate::location::LocationTable;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
|
||||
mod loan_invalidations;
|
||||
mod loan_kills;
|
||||
|
||||
/// When requested, emit most of the facts needed by polonius:
|
||||
/// - moves and assignments
|
||||
/// - universal regions and their relations
|
||||
/// - CFG points and edges
|
||||
/// - loan kills
|
||||
/// - loan invalidations
|
||||
///
|
||||
/// The rest of the facts are emitted during typeck and liveness.
|
||||
pub(crate) fn emit_facts<'tcx>(
|
||||
all_facts: &mut Option<AllFacts>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
move_data: &MoveData<'_>,
|
||||
universal_region_relations: &UniversalRegionRelations<'_>,
|
||||
) {
|
||||
let Some(all_facts) = all_facts else {
|
||||
// We don't do anything if there are no facts to fill.
|
||||
return;
|
||||
};
|
||||
let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation");
|
||||
emit_move_facts(all_facts, move_data, location_table, body);
|
||||
emit_universal_region_facts(all_facts, borrow_set, universal_region_relations);
|
||||
emit_cfg_and_loan_kills_facts(all_facts, tcx, location_table, body, borrow_set);
|
||||
emit_loan_invalidations_facts(all_facts, tcx, location_table, body, borrow_set);
|
||||
}
|
||||
|
||||
/// Emit facts needed for move/init analysis: moves and assignments.
|
||||
fn emit_move_facts(
|
||||
all_facts: &mut AllFacts,
|
||||
move_data: &MoveData<'_>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'_>,
|
||||
) {
|
||||
all_facts
|
||||
.path_is_var
|
||||
.extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l)));
|
||||
|
||||
for (child, move_path) in move_data.move_paths.iter_enumerated() {
|
||||
if let Some(parent) = move_path.parent {
|
||||
all_facts.child_path.push((child, parent));
|
||||
}
|
||||
}
|
||||
|
||||
let fn_entry_start =
|
||||
location_table.start_index(Location { block: START_BLOCK, statement_index: 0 });
|
||||
|
||||
// initialized_at
|
||||
for init in move_data.inits.iter() {
|
||||
match init.location {
|
||||
InitLocation::Statement(location) => {
|
||||
let block_data = &body[location.block];
|
||||
let is_terminator = location.statement_index == block_data.statements.len();
|
||||
|
||||
if is_terminator && init.kind == InitKind::NonPanicPathOnly {
|
||||
// We are at the terminator of an init that has a panic path,
|
||||
// and where the init should not happen on panic
|
||||
|
||||
for successor in block_data.terminator().successors() {
|
||||
if body[successor].is_cleanup {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The initialization happened in (or rather, when arriving at)
|
||||
// the successors, but not in the unwind block.
|
||||
let first_statement = Location { block: successor, statement_index: 0 };
|
||||
all_facts
|
||||
.path_assigned_at_base
|
||||
.push((init.path, location_table.start_index(first_statement)));
|
||||
}
|
||||
} else {
|
||||
// In all other cases, the initialization just happens at the
|
||||
// midpoint, like any other effect.
|
||||
all_facts
|
||||
.path_assigned_at_base
|
||||
.push((init.path, location_table.mid_index(location)));
|
||||
}
|
||||
}
|
||||
// Arguments are initialized on function entry
|
||||
InitLocation::Argument(local) => {
|
||||
assert!(body.local_kind(local) == LocalKind::Arg);
|
||||
all_facts.path_assigned_at_base.push((init.path, fn_entry_start));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (local, path) in move_data.rev_lookup.iter_locals_enumerated() {
|
||||
if body.local_kind(local) != LocalKind::Arg {
|
||||
// Non-arguments start out deinitialised; we simulate this with an
|
||||
// initial move:
|
||||
all_facts.path_moved_at_base.push((path, fn_entry_start));
|
||||
}
|
||||
}
|
||||
|
||||
// moved_out_at
|
||||
// deinitialisation is assumed to always happen!
|
||||
all_facts
|
||||
.path_moved_at_base
|
||||
.extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source))));
|
||||
}
|
||||
|
||||
/// Emit universal regions facts, and their relations.
|
||||
fn emit_universal_region_facts(
|
||||
all_facts: &mut AllFacts,
|
||||
borrow_set: &BorrowSet<'_>,
|
||||
universal_region_relations: &UniversalRegionRelations<'_>,
|
||||
) {
|
||||
// 1: universal regions are modeled in Polonius as a pair:
|
||||
// - the universal region vid itself.
|
||||
// - a "placeholder loan" associated to this universal region. Since they don't exist in
|
||||
// the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index
|
||||
// added to the existing number of loans, as if they succeeded them in the set.
|
||||
//
|
||||
let universal_regions = &universal_region_relations.universal_regions;
|
||||
all_facts
|
||||
.universal_region
|
||||
.extend(universal_regions.universal_regions_iter().map(PoloniusRegionVid::from));
|
||||
let borrow_count = borrow_set.len();
|
||||
debug!(
|
||||
"emit_universal_region_facts: polonius placeholders, num_universals={}, borrow_count={}",
|
||||
universal_regions.len(),
|
||||
borrow_count
|
||||
);
|
||||
|
||||
for universal_region in universal_regions.universal_regions_iter() {
|
||||
let universal_region_idx = universal_region.index();
|
||||
let placeholder_loan_idx = borrow_count + universal_region_idx;
|
||||
all_facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into()));
|
||||
}
|
||||
|
||||
// 2: the universal region relations `outlives` constraints are emitted as
|
||||
// `known_placeholder_subset` facts.
|
||||
for (fr1, fr2) in universal_region_relations.known_outlives() {
|
||||
if fr1 != fr2 {
|
||||
debug!(
|
||||
"emit_universal_region_facts: emitting polonius `known_placeholder_subset` \
|
||||
fr1={:?}, fr2={:?}",
|
||||
fr1, fr2
|
||||
);
|
||||
all_facts.known_placeholder_subset.push((fr1.into(), fr2.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit facts about loan invalidations.
|
||||
fn emit_loan_invalidations_facts<'tcx>(
|
||||
all_facts: &mut AllFacts,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
loan_invalidations::emit_loan_invalidations(tcx, all_facts, location_table, body, borrow_set);
|
||||
}
|
||||
|
||||
/// Emit facts about CFG points and edges, as well as locations where loans are killed.
|
||||
fn emit_cfg_and_loan_kills_facts<'tcx>(
|
||||
all_facts: &mut AllFacts,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
loan_kills::emit_loan_kills(tcx, all_facts, location_table, body, borrow_set);
|
||||
}
|
||||
|
|
@ -1,184 +1 @@
|
|||
//! Functions dedicated to fact generation for the `-Zpolonius=legacy` datalog implementation.
|
||||
//!
|
||||
//! Will be removed in the future, once the in-tree `-Zpolonius=next` implementation reaches feature
|
||||
//! parity.
|
||||
|
||||
use rustc_middle::mir::{Body, LocalKind, Location, START_BLOCK};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::borrow_set::BorrowSet;
|
||||
use crate::facts::{AllFacts, PoloniusRegionVid};
|
||||
use crate::location::LocationTable;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
|
||||
mod loan_invalidations;
|
||||
mod loan_kills;
|
||||
|
||||
/// When requested, emit most of the facts needed by polonius:
|
||||
/// - moves and assignments
|
||||
/// - universal regions and their relations
|
||||
/// - CFG points and edges
|
||||
/// - loan kills
|
||||
/// - loan invalidations
|
||||
///
|
||||
/// The rest of the facts are emitted during typeck and liveness.
|
||||
pub(crate) fn emit_facts<'tcx>(
|
||||
all_facts: &mut Option<AllFacts>,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
move_data: &MoveData<'_>,
|
||||
universal_region_relations: &UniversalRegionRelations<'_>,
|
||||
) {
|
||||
let Some(all_facts) = all_facts else {
|
||||
// We don't do anything if there are no facts to fill.
|
||||
return;
|
||||
};
|
||||
let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation");
|
||||
emit_move_facts(all_facts, move_data, location_table, body);
|
||||
emit_universal_region_facts(all_facts, borrow_set, universal_region_relations);
|
||||
emit_cfg_and_loan_kills_facts(all_facts, tcx, location_table, body, borrow_set);
|
||||
emit_loan_invalidations_facts(all_facts, tcx, location_table, body, borrow_set);
|
||||
}
|
||||
|
||||
/// Emit facts needed for move/init analysis: moves and assignments.
|
||||
fn emit_move_facts(
|
||||
all_facts: &mut AllFacts,
|
||||
move_data: &MoveData<'_>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'_>,
|
||||
) {
|
||||
all_facts
|
||||
.path_is_var
|
||||
.extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l)));
|
||||
|
||||
for (child, move_path) in move_data.move_paths.iter_enumerated() {
|
||||
if let Some(parent) = move_path.parent {
|
||||
all_facts.child_path.push((child, parent));
|
||||
}
|
||||
}
|
||||
|
||||
let fn_entry_start =
|
||||
location_table.start_index(Location { block: START_BLOCK, statement_index: 0 });
|
||||
|
||||
// initialized_at
|
||||
for init in move_data.inits.iter() {
|
||||
match init.location {
|
||||
InitLocation::Statement(location) => {
|
||||
let block_data = &body[location.block];
|
||||
let is_terminator = location.statement_index == block_data.statements.len();
|
||||
|
||||
if is_terminator && init.kind == InitKind::NonPanicPathOnly {
|
||||
// We are at the terminator of an init that has a panic path,
|
||||
// and where the init should not happen on panic
|
||||
|
||||
for successor in block_data.terminator().successors() {
|
||||
if body[successor].is_cleanup {
|
||||
continue;
|
||||
}
|
||||
|
||||
// The initialization happened in (or rather, when arriving at)
|
||||
// the successors, but not in the unwind block.
|
||||
let first_statement = Location { block: successor, statement_index: 0 };
|
||||
all_facts
|
||||
.path_assigned_at_base
|
||||
.push((init.path, location_table.start_index(first_statement)));
|
||||
}
|
||||
} else {
|
||||
// In all other cases, the initialization just happens at the
|
||||
// midpoint, like any other effect.
|
||||
all_facts
|
||||
.path_assigned_at_base
|
||||
.push((init.path, location_table.mid_index(location)));
|
||||
}
|
||||
}
|
||||
// Arguments are initialized on function entry
|
||||
InitLocation::Argument(local) => {
|
||||
assert!(body.local_kind(local) == LocalKind::Arg);
|
||||
all_facts.path_assigned_at_base.push((init.path, fn_entry_start));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (local, path) in move_data.rev_lookup.iter_locals_enumerated() {
|
||||
if body.local_kind(local) != LocalKind::Arg {
|
||||
// Non-arguments start out deinitialised; we simulate this with an
|
||||
// initial move:
|
||||
all_facts.path_moved_at_base.push((path, fn_entry_start));
|
||||
}
|
||||
}
|
||||
|
||||
// moved_out_at
|
||||
// deinitialisation is assumed to always happen!
|
||||
all_facts
|
||||
.path_moved_at_base
|
||||
.extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source))));
|
||||
}
|
||||
|
||||
/// Emit universal regions facts, and their relations.
|
||||
fn emit_universal_region_facts(
|
||||
all_facts: &mut AllFacts,
|
||||
borrow_set: &BorrowSet<'_>,
|
||||
universal_region_relations: &UniversalRegionRelations<'_>,
|
||||
) {
|
||||
// 1: universal regions are modeled in Polonius as a pair:
|
||||
// - the universal region vid itself.
|
||||
// - a "placeholder loan" associated to this universal region. Since they don't exist in
|
||||
// the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index
|
||||
// added to the existing number of loans, as if they succeeded them in the set.
|
||||
//
|
||||
let universal_regions = &universal_region_relations.universal_regions;
|
||||
all_facts
|
||||
.universal_region
|
||||
.extend(universal_regions.universal_regions_iter().map(PoloniusRegionVid::from));
|
||||
let borrow_count = borrow_set.len();
|
||||
debug!(
|
||||
"emit_universal_region_facts: polonius placeholders, num_universals={}, borrow_count={}",
|
||||
universal_regions.len(),
|
||||
borrow_count
|
||||
);
|
||||
|
||||
for universal_region in universal_regions.universal_regions_iter() {
|
||||
let universal_region_idx = universal_region.index();
|
||||
let placeholder_loan_idx = borrow_count + universal_region_idx;
|
||||
all_facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into()));
|
||||
}
|
||||
|
||||
// 2: the universal region relations `outlives` constraints are emitted as
|
||||
// `known_placeholder_subset` facts.
|
||||
for (fr1, fr2) in universal_region_relations.known_outlives() {
|
||||
if fr1 != fr2 {
|
||||
debug!(
|
||||
"emit_universal_region_facts: emitting polonius `known_placeholder_subset` \
|
||||
fr1={:?}, fr2={:?}",
|
||||
fr1, fr2
|
||||
);
|
||||
all_facts.known_placeholder_subset.push((fr1.into(), fr2.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit facts about loan invalidations.
|
||||
fn emit_loan_invalidations_facts<'tcx>(
|
||||
all_facts: &mut AllFacts,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
loan_invalidations::emit_loan_invalidations(tcx, all_facts, location_table, body, borrow_set);
|
||||
}
|
||||
|
||||
/// Emit facts about CFG points and edges, as well as locations where loans are killed.
|
||||
fn emit_cfg_and_loan_kills_facts<'tcx>(
|
||||
all_facts: &mut AllFacts,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
loan_kills::emit_loan_kills(tcx, all_facts, location_table, body, borrow_set);
|
||||
}
|
||||
pub(crate) mod legacy;
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@ pub(super) fn generate<'a, 'tcx>(
|
|||
let (relevant_live_locals, boring_locals) =
|
||||
compute_relevant_live_locals(typeck.tcx(), &free_regions, body);
|
||||
|
||||
polonius::populate_access_facts(typeck, body, move_data);
|
||||
polonius::emit_access_facts(typeck, body, move_data);
|
||||
|
||||
trace::trace(
|
||||
typeck,
|
||||
|
|
|
|||
|
|
@ -11,88 +11,19 @@ use crate::location::{LocationIndex, LocationTable};
|
|||
type VarPointRelation = Vec<(Local, LocationIndex)>;
|
||||
type PathPointRelation = Vec<(MovePathIndex, LocationIndex)>;
|
||||
|
||||
struct UseFactsExtractor<'a, 'tcx> {
|
||||
var_defined_at: &'a mut VarPointRelation,
|
||||
var_used_at: &'a mut VarPointRelation,
|
||||
location_table: &'a LocationTable,
|
||||
var_dropped_at: &'a mut VarPointRelation,
|
||||
move_data: &'a MoveData<'tcx>,
|
||||
path_accessed_at_base: &'a mut PathPointRelation,
|
||||
}
|
||||
|
||||
// A Visitor to walk through the MIR and extract point-wise facts
|
||||
impl<'tcx> UseFactsExtractor<'_, 'tcx> {
|
||||
fn location_to_index(&self, location: Location) -> LocationIndex {
|
||||
self.location_table.mid_index(location)
|
||||
}
|
||||
|
||||
fn insert_def(&mut self, local: Local, location: Location) {
|
||||
debug!("UseFactsExtractor::insert_def()");
|
||||
self.var_defined_at.push((local, self.location_to_index(location)));
|
||||
}
|
||||
|
||||
fn insert_use(&mut self, local: Local, location: Location) {
|
||||
debug!("UseFactsExtractor::insert_use()");
|
||||
self.var_used_at.push((local, self.location_to_index(location)));
|
||||
}
|
||||
|
||||
fn insert_drop_use(&mut self, local: Local, location: Location) {
|
||||
debug!("UseFactsExtractor::insert_drop_use()");
|
||||
self.var_dropped_at.push((local, self.location_to_index(location)));
|
||||
}
|
||||
|
||||
fn insert_path_access(&mut self, path: MovePathIndex, location: Location) {
|
||||
debug!("UseFactsExtractor::insert_path_access({:?}, {:?})", path, location);
|
||||
self.path_accessed_at_base.push((path, self.location_to_index(location)));
|
||||
}
|
||||
|
||||
fn place_to_mpi(&self, place: &Place<'tcx>) -> Option<MovePathIndex> {
|
||||
match self.move_data.rev_lookup.find(place.as_ref()) {
|
||||
LookupResult::Exact(mpi) => Some(mpi),
|
||||
LookupResult::Parent(mmpi) => mmpi,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for UseFactsExtractor<'a, 'tcx> {
|
||||
fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
|
||||
match def_use::categorize(context) {
|
||||
Some(DefUse::Def) => self.insert_def(local, location),
|
||||
Some(DefUse::Use) => self.insert_use(local, location),
|
||||
Some(DefUse::Drop) => self.insert_drop_use(local, location),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
|
||||
self.super_place(place, context, location);
|
||||
match context {
|
||||
PlaceContext::NonMutatingUse(_) => {
|
||||
if let Some(mpi) = self.place_to_mpi(place) {
|
||||
self.insert_path_access(mpi, location);
|
||||
}
|
||||
}
|
||||
|
||||
PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
|
||||
if let Some(mpi) = self.place_to_mpi(place) {
|
||||
self.insert_path_access(mpi, location);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn populate_access_facts<'a, 'tcx>(
|
||||
/// Emit polonius facts for variable defs, uses, drops, and path accesses.
|
||||
pub(super) fn emit_access_facts<'a, 'tcx>(
|
||||
typeck: &mut TypeChecker<'a, 'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
) {
|
||||
if let Some(facts) = typeck.all_facts.as_mut() {
|
||||
debug!("populate_access_facts()");
|
||||
debug!("emit_access_facts()");
|
||||
|
||||
let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation");
|
||||
let location_table = typeck.location_table;
|
||||
|
||||
let mut extractor = UseFactsExtractor {
|
||||
let mut extractor = AccessFactsExtractor {
|
||||
var_defined_at: &mut facts.var_defined_at,
|
||||
var_used_at: &mut facts.var_used_at,
|
||||
var_dropped_at: &mut facts.var_dropped_at,
|
||||
|
|
@ -107,7 +38,6 @@ pub(super) fn populate_access_facts<'a, 'tcx>(
|
|||
"add use_of_var_derefs_origin facts - local={:?}, type={:?}",
|
||||
local, local_decl.ty
|
||||
);
|
||||
let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation");
|
||||
let universal_regions = &typeck.universal_regions;
|
||||
typeck.infcx.tcx.for_each_free_region(&local_decl.ty, |region| {
|
||||
let region_vid = universal_regions.to_region_vid(region);
|
||||
|
|
@ -119,12 +49,12 @@ pub(super) fn populate_access_facts<'a, 'tcx>(
|
|||
|
||||
/// For every potentially drop()-touched region `region` in `local`'s type
|
||||
/// (`kind`), emit a Polonius `use_of_var_derefs_origin(local, origin)` fact.
|
||||
pub(super) fn add_drop_of_var_derefs_origin<'tcx>(
|
||||
pub(super) fn emit_drop_facts<'tcx>(
|
||||
typeck: &mut TypeChecker<'_, 'tcx>,
|
||||
local: Local,
|
||||
kind: &GenericArg<'tcx>,
|
||||
) {
|
||||
debug!("add_drop_of_var_derefs_origin(local={:?}, kind={:?}", local, kind);
|
||||
debug!("emit_drop_facts(local={:?}, kind={:?}", local, kind);
|
||||
if let Some(facts) = typeck.all_facts.as_mut() {
|
||||
let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation");
|
||||
let universal_regions = &typeck.universal_regions;
|
||||
|
|
@ -134,3 +64,60 @@ pub(super) fn add_drop_of_var_derefs_origin<'tcx>(
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// MIR visitor extracting point-wise facts about accesses.
|
||||
struct AccessFactsExtractor<'a, 'tcx> {
|
||||
var_defined_at: &'a mut VarPointRelation,
|
||||
var_used_at: &'a mut VarPointRelation,
|
||||
location_table: &'a LocationTable,
|
||||
var_dropped_at: &'a mut VarPointRelation,
|
||||
move_data: &'a MoveData<'tcx>,
|
||||
path_accessed_at_base: &'a mut PathPointRelation,
|
||||
}
|
||||
|
||||
impl<'tcx> AccessFactsExtractor<'_, 'tcx> {
|
||||
fn location_to_index(&self, location: Location) -> LocationIndex {
|
||||
self.location_table.mid_index(location)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for AccessFactsExtractor<'a, 'tcx> {
|
||||
fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
|
||||
match def_use::categorize(context) {
|
||||
Some(DefUse::Def) => {
|
||||
debug!("AccessFactsExtractor - emit def");
|
||||
self.var_defined_at.push((local, self.location_to_index(location)));
|
||||
}
|
||||
Some(DefUse::Use) => {
|
||||
debug!("AccessFactsExtractor - emit use");
|
||||
self.var_used_at.push((local, self.location_to_index(location)));
|
||||
}
|
||||
Some(DefUse::Drop) => {
|
||||
debug!("AccessFactsExtractor - emit drop");
|
||||
self.var_dropped_at.push((local, self.location_to_index(location)));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
|
||||
self.super_place(place, context, location);
|
||||
|
||||
match context {
|
||||
PlaceContext::NonMutatingUse(_)
|
||||
| PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
|
||||
let path = match self.move_data.rev_lookup.find(place.as_ref()) {
|
||||
LookupResult::Exact(path) | LookupResult::Parent(Some(path)) => path,
|
||||
_ => {
|
||||
// There's no path access to emit.
|
||||
return;
|
||||
}
|
||||
};
|
||||
debug!("AccessFactsExtractor - emit path access ({path:?}, {location:?})");
|
||||
self.path_accessed_at_base.push((path, self.location_to_index(location)));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -590,7 +590,7 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
|||
// the destructor and must be live at this point.
|
||||
for &kind in &drop_data.dropck_result.kinds {
|
||||
Self::make_all_regions_live(self.elements, self.typeck, kind, live_at);
|
||||
polonius::add_drop_of_var_derefs_origin(self.typeck, dropped_local, &kind);
|
||||
polonius::emit_drop_facts(self.typeck, dropped_local, &kind);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -986,12 +986,12 @@ fn link_natively(
|
|||
let mut output = prog.stderr.clone();
|
||||
output.extend_from_slice(&prog.stdout);
|
||||
let escaped_output = escape_linker_output(&output, flavor);
|
||||
// FIXME: Add UI tests for this error.
|
||||
let err = errors::LinkingFailed {
|
||||
linker_path: &linker_path,
|
||||
exit_status: prog.status,
|
||||
command: &cmd,
|
||||
command: cmd,
|
||||
escaped_output,
|
||||
verbose: sess.opts.verbose,
|
||||
};
|
||||
sess.dcx().emit_err(err);
|
||||
// If MSVC's `link.exe` was expected but the return code
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_errors::{DiagMessage, SubdiagMessage, struct_span_code_err};
|
|||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
|
||||
use rustc_hir::weak_lang_items::WEAK_LANG_ITEMS;
|
||||
use rustc_hir::{self as hir, HirId, LangItem, lang_items};
|
||||
use rustc_hir::{HirId, LangItem, lang_items};
|
||||
use rustc_middle::middle::codegen_fn_attrs::{
|
||||
CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry,
|
||||
};
|
||||
|
|
@ -251,7 +251,7 @@ fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: LocalDefId) -> CodegenFnAttrs {
|
|||
sym::target_feature => {
|
||||
if !tcx.is_closure_like(did.to_def_id())
|
||||
&& let Some(fn_sig) = fn_sig()
|
||||
&& fn_sig.skip_binder().safety() == hir::Safety::Safe
|
||||
&& fn_sig.skip_binder().safety().is_safe()
|
||||
{
|
||||
if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
|
||||
// The `#[target_feature]` attribute is allowed on
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
//! Errors emitted by codegen_ssa
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::OsString;
|
||||
use std::io::Error;
|
||||
use std::num::ParseIntError;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
|
@ -345,21 +346,82 @@ impl<G: EmissionGuarantee> Diagnostic<'_, G> for ThorinErrorWrapper {
|
|||
}
|
||||
|
||||
pub(crate) struct LinkingFailed<'a> {
|
||||
pub linker_path: &'a PathBuf,
|
||||
pub linker_path: &'a Path,
|
||||
pub exit_status: ExitStatus,
|
||||
pub command: &'a Command,
|
||||
pub command: Command,
|
||||
pub escaped_output: String,
|
||||
pub verbose: bool,
|
||||
}
|
||||
|
||||
impl<G: EmissionGuarantee> Diagnostic<'_, G> for LinkingFailed<'_> {
|
||||
fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
|
||||
fn into_diag(mut self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> {
|
||||
let mut diag = Diag::new(dcx, level, fluent::codegen_ssa_linking_failed);
|
||||
diag.arg("linker_path", format!("{}", self.linker_path.display()));
|
||||
diag.arg("exit_status", format!("{}", self.exit_status));
|
||||
|
||||
let contains_undefined_ref = self.escaped_output.contains("undefined reference to");
|
||||
|
||||
diag.note(format!("{:?}", self.command)).note(self.escaped_output);
|
||||
if self.verbose {
|
||||
diag.note(format!("{:?}", self.command));
|
||||
} else {
|
||||
enum ArgGroup {
|
||||
Regular(OsString),
|
||||
Objects(usize),
|
||||
Rlibs(PathBuf, Vec<OsString>),
|
||||
}
|
||||
|
||||
// Omit rust object files and fold rlibs in the error by default to make linker errors a
|
||||
// bit less verbose.
|
||||
let orig_args = self.command.take_args();
|
||||
let mut args: Vec<ArgGroup> = vec![];
|
||||
for arg in orig_args {
|
||||
if arg.as_encoded_bytes().ends_with(b".rcgu.o") {
|
||||
if let Some(ArgGroup::Objects(n)) = args.last_mut() {
|
||||
*n += 1;
|
||||
} else {
|
||||
args.push(ArgGroup::Objects(1));
|
||||
}
|
||||
} else if arg.as_encoded_bytes().ends_with(b".rlib") {
|
||||
let rlib_path = Path::new(&arg);
|
||||
let dir = rlib_path.parent().unwrap();
|
||||
let filename = rlib_path.file_name().unwrap().to_owned();
|
||||
if let Some(ArgGroup::Rlibs(parent, rlibs)) = args.last_mut() {
|
||||
if parent == dir {
|
||||
rlibs.push(filename);
|
||||
} else {
|
||||
args.push(ArgGroup::Rlibs(dir.to_owned(), vec![filename]));
|
||||
}
|
||||
} else {
|
||||
args.push(ArgGroup::Rlibs(dir.to_owned(), vec![filename]));
|
||||
}
|
||||
} else {
|
||||
args.push(ArgGroup::Regular(arg));
|
||||
}
|
||||
}
|
||||
self.command.args(args.into_iter().map(|arg_group| match arg_group {
|
||||
ArgGroup::Regular(arg) => arg,
|
||||
ArgGroup::Objects(n) => OsString::from(format!("<{n} object files omitted>")),
|
||||
ArgGroup::Rlibs(dir, rlibs) => {
|
||||
let mut arg = dir.into_os_string();
|
||||
arg.push("/{");
|
||||
let mut first = true;
|
||||
for rlib in rlibs {
|
||||
if !first {
|
||||
arg.push(",");
|
||||
}
|
||||
first = false;
|
||||
arg.push(rlib);
|
||||
}
|
||||
arg.push("}");
|
||||
arg
|
||||
}
|
||||
}));
|
||||
|
||||
diag.note(format!("{:?}", self.command));
|
||||
diag.note("some arguments are omitted. use `--verbose` to show all linker arguments");
|
||||
}
|
||||
|
||||
diag.note(self.escaped_output);
|
||||
|
||||
// Trying to match an error from OS linkers
|
||||
// which by now we have no way to translate.
|
||||
|
|
|
|||
|
|
@ -57,9 +57,8 @@ fn is_promotable_const_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
|
|||
Some(stab) => {
|
||||
if cfg!(debug_assertions) && stab.promotable {
|
||||
let sig = tcx.fn_sig(def_id);
|
||||
assert_eq!(
|
||||
sig.skip_binder().safety(),
|
||||
hir::Safety::Safe,
|
||||
assert!(
|
||||
sig.skip_binder().safety().is_safe(),
|
||||
"don't mark const unsafe fns as promotable",
|
||||
// https://github.com/rust-lang/rust/pull/53851#issuecomment-418760682
|
||||
);
|
||||
|
|
|
|||
|
|
@ -3434,6 +3434,19 @@ impl Safety {
|
|||
Self::Safe => "",
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_unsafe(self) -> bool {
|
||||
!self.is_safe()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn is_safe(self) -> bool {
|
||||
match self {
|
||||
Self::Unsafe => false,
|
||||
Self::Safe => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Safety {
|
||||
|
|
@ -3478,7 +3491,7 @@ impl FnHeader {
|
|||
}
|
||||
|
||||
pub fn is_unsafe(&self) -> bool {
|
||||
matches!(&self.safety, Safety::Unsafe)
|
||||
self.safety.is_unsafe()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use rustc_data_structures::unord::{UnordMap, UnordSet};
|
|||
use rustc_errors::MultiSpan;
|
||||
use rustc_errors::codes::*;
|
||||
use rustc_hir::def::{CtorKind, DefKind};
|
||||
use rustc_hir::{Node, Safety, intravisit};
|
||||
use rustc_hir::{Node, intravisit};
|
||||
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
|
||||
use rustc_infer::traits::{Obligation, ObligationCauseCode};
|
||||
use rustc_lint_defs::builtin::{
|
||||
|
|
@ -161,7 +161,7 @@ fn check_unsafe_fields(tcx: TyCtxt<'_>, item_def_id: LocalDefId) {
|
|||
};
|
||||
let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id);
|
||||
for field in def.all_fields() {
|
||||
if field.safety != Safety::Unsafe {
|
||||
if !field.safety.is_unsafe() {
|
||||
continue;
|
||||
}
|
||||
let Ok(field_ty) = tcx.try_normalize_erasing_regions(typing_env, field.ty(tcx, args))
|
||||
|
|
|
|||
|
|
@ -863,7 +863,8 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
let outer_universe = self.infcx.universe();
|
||||
|
||||
let result = if let ty::FnPtr(_, hdr_b) = b.kind()
|
||||
&& let (hir::Safety::Safe, hir::Safety::Unsafe) = (fn_ty_a.safety(), hdr_b.safety)
|
||||
&& fn_ty_a.safety().is_safe()
|
||||
&& hdr_b.safety.is_unsafe()
|
||||
{
|
||||
let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a);
|
||||
self.unify_and(unsafe_a, b, to_unsafe)
|
||||
|
|
@ -925,7 +926,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> {
|
|||
|
||||
// Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396).
|
||||
|
||||
if b_hdr.safety == hir::Safety::Safe
|
||||
if b_hdr.safety.is_safe()
|
||||
&& !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty()
|
||||
{
|
||||
return Err(TypeError::TargetFeatureCast(def_id));
|
||||
|
|
|
|||
|
|
@ -762,7 +762,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>(
|
|||
if let Some(def_id) = typeck_results.type_dependent_def_id(ex.hir_id)
|
||||
&& let method_ty = self.fcx.tcx.type_of(def_id).instantiate_identity()
|
||||
&& let sig = method_ty.fn_sig(self.fcx.tcx)
|
||||
&& let hir::Safety::Unsafe = sig.safety()
|
||||
&& sig.safety().is_unsafe()
|
||||
{
|
||||
let mut collector = InferVarCollector {
|
||||
value: (ex.hir_id, ex.span, UnsafeUseReason::Method),
|
||||
|
|
@ -782,7 +782,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>(
|
|||
|
||||
if func_ty.is_fn()
|
||||
&& let sig = func_ty.fn_sig(self.fcx.tcx)
|
||||
&& let hir::Safety::Unsafe = sig.safety()
|
||||
&& sig.safety().is_unsafe()
|
||||
{
|
||||
let mut collector = InferVarCollector {
|
||||
value: (ex.hir_id, ex.span, UnsafeUseReason::Call),
|
||||
|
|
@ -813,7 +813,7 @@ fn compute_unsafe_infer_vars<'a, 'tcx>(
|
|||
// `is_fn` excludes closures, but those can't be unsafe.
|
||||
if ty.is_fn()
|
||||
&& let sig = ty.fn_sig(self.fcx.tcx)
|
||||
&& let hir::Safety::Unsafe = sig.safety()
|
||||
&& sig.safety().is_unsafe()
|
||||
{
|
||||
let mut collector = InferVarCollector {
|
||||
value: (ex.hir_id, ex.span, UnsafeUseReason::Path),
|
||||
|
|
|
|||
|
|
@ -586,7 +586,7 @@ impl<'tcx> Interner for TyCtxt<'tcx> {
|
|||
}
|
||||
|
||||
fn trait_is_unsafe(self, trait_def_id: Self::DefId) -> bool {
|
||||
self.trait_def(trait_def_id).safety == hir::Safety::Unsafe
|
||||
self.trait_def(trait_def_id).safety.is_unsafe()
|
||||
}
|
||||
|
||||
fn is_impl_trait_in_trait(self, def_id: DefId) -> bool {
|
||||
|
|
@ -722,7 +722,7 @@ impl<'tcx> rustc_type_ir::inherent::Safety<TyCtxt<'tcx>> for hir::Safety {
|
|||
}
|
||||
|
||||
fn is_safe(self) -> bool {
|
||||
matches!(self, hir::Safety::Safe)
|
||||
self.is_safe()
|
||||
}
|
||||
|
||||
fn prefix_str(self) -> &'static str {
|
||||
|
|
@ -2521,7 +2521,7 @@ impl<'tcx> TyCtxt<'tcx> {
|
|||
/// that is, a `fn` type that is equivalent in every way for being
|
||||
/// unsafe.
|
||||
pub fn safe_to_unsafe_fn_ty(self, sig: PolyFnSig<'tcx>) -> Ty<'tcx> {
|
||||
assert_eq!(sig.safety(), hir::Safety::Safe);
|
||||
assert!(sig.safety().is_safe());
|
||||
Ty::new_fn_ptr(self, sig.map_bound(|sig| ty::FnSig { safety: hir::Safety::Unsafe, ..sig }))
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,9 +33,9 @@ use rustc_data_structures::intern::Interned;
|
|||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::steal::Steal;
|
||||
use rustc_errors::{Diag, ErrorGuaranteed, StashKey};
|
||||
use rustc_hir::LangItem;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res};
|
||||
use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap};
|
||||
use rustc_hir::{LangItem, Safety};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_macros::{
|
||||
Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable,
|
||||
|
|
@ -1281,7 +1281,7 @@ impl VariantDef {
|
|||
|
||||
/// Returns whether this variant has unsafe fields.
|
||||
pub fn has_unsafe_fields(&self) -> bool {
|
||||
self.fields.iter().any(|x| x.safety == Safety::Unsafe)
|
||||
self.fields.iter().any(|x| x.safety.is_unsafe())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1291,7 +1291,7 @@ impl<'tcx> Ty<'tcx> {
|
|||
/// Checks whether this type is an ADT that has unsafe fields.
|
||||
pub fn has_unsafe_fields(self) -> bool {
|
||||
if let ty::Adt(adt_def, ..) = self.kind() {
|
||||
adt_def.all_fields().any(|x| x.safety == hir::Safety::Unsafe)
|
||||
adt_def.all_fields().any(|x| x.safety.is_unsafe())
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use std::ops::Bound;
|
|||
|
||||
use rustc_errors::DiagArgValue;
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability, Safety};
|
||||
use rustc_hir::{self as hir, BindingMode, ByRef, HirId, Mutability};
|
||||
use rustc_middle::middle::codegen_fn_attrs::TargetFeature;
|
||||
use rustc_middle::mir::BorrowKind;
|
||||
use rustc_middle::span_bug;
|
||||
|
|
@ -342,7 +342,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
|||
PatKind::Leaf { subpatterns, .. } => {
|
||||
if let ty::Adt(adt_def, ..) = pat.ty.kind() {
|
||||
for pat in subpatterns {
|
||||
if adt_def.non_enum_variant().fields[pat.field].safety == Safety::Unsafe {
|
||||
if adt_def.non_enum_variant().fields[pat.field].safety.is_unsafe() {
|
||||
self.requires_unsafe(pat.pattern.span, UseOfUnsafeField);
|
||||
}
|
||||
}
|
||||
|
|
@ -367,7 +367,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
|||
PatKind::Variant { adt_def, args: _, variant_index, subpatterns } => {
|
||||
for pat in subpatterns {
|
||||
let field = &pat.field;
|
||||
if adt_def.variant(*variant_index).fields[*field].safety == Safety::Unsafe {
|
||||
if adt_def.variant(*variant_index).fields[*field].safety.is_unsafe() {
|
||||
self.requires_unsafe(pat.pattern.span, UseOfUnsafeField);
|
||||
}
|
||||
}
|
||||
|
|
@ -479,7 +479,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
|||
return; // don't visit the whole expression
|
||||
}
|
||||
ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
|
||||
if self.thir[fun].ty.fn_sig(self.tcx).safety() == hir::Safety::Unsafe {
|
||||
if self.thir[fun].ty.fn_sig(self.tcx).safety().is_unsafe() {
|
||||
let func_id = if let ty::FnDef(func_id, _) = self.thir[fun].ty.kind() {
|
||||
Some(*func_id)
|
||||
} else {
|
||||
|
|
@ -623,7 +623,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
|||
ExprKind::Field { lhs, variant_index, name } => {
|
||||
let lhs = &self.thir[lhs];
|
||||
if let ty::Adt(adt_def, _) = lhs.ty.kind() {
|
||||
if adt_def.variant(variant_index).fields[name].safety == Safety::Unsafe {
|
||||
if adt_def.variant(variant_index).fields[name].safety.is_unsafe() {
|
||||
self.requires_unsafe(expr.span, UseOfUnsafeField);
|
||||
} else if adt_def.is_union() {
|
||||
if let Some(assigned_ty) = self.assignment_info {
|
||||
|
|
@ -1112,11 +1112,7 @@ pub(crate) fn check_unsafety(tcx: TyCtxt<'_>, def: LocalDefId) {
|
|||
|
||||
let hir_id = tcx.local_def_id_to_hir_id(def);
|
||||
let safety_context = tcx.hir().fn_sig_by_hir_id(hir_id).map_or(SafetyContext::Safe, |fn_sig| {
|
||||
if fn_sig.header.safety == hir::Safety::Unsafe {
|
||||
SafetyContext::UnsafeFn
|
||||
} else {
|
||||
SafetyContext::Safe
|
||||
}
|
||||
if fn_sig.header.safety.is_unsafe() { SafetyContext::UnsafeFn } else { SafetyContext::Safe }
|
||||
});
|
||||
let body_target_features = &tcx.body_codegen_attrs(def.to_def_id()).target_features;
|
||||
let mut warnings = Vec::new();
|
||||
|
|
|
|||
|
|
@ -715,7 +715,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
attrs: &[Attribute],
|
||||
) {
|
||||
match target {
|
||||
Target::Fn => {
|
||||
Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent)
|
||||
| Target::Fn => {
|
||||
// `#[target_feature]` is not allowed in lang items.
|
||||
if let Some((lang_item, _)) = hir::lang_items::extract(attrs)
|
||||
// Calling functions with `#[target_feature]` is
|
||||
|
|
@ -732,7 +733,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
|
|||
});
|
||||
}
|
||||
}
|
||||
Target::Method(MethodKind::Trait { body: true } | MethodKind::Inherent) => {}
|
||||
// FIXME: #[target_feature] was previously erroneously allowed on statements and some
|
||||
// crates used this, so only emit a warning.
|
||||
Target::Statement => {
|
||||
|
|
|
|||
|
|
@ -356,6 +356,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
|
|||
let kind = match self.impl_trait_context {
|
||||
ImplTraitContext::Universal => DefKind::TyParam,
|
||||
ImplTraitContext::Existential => DefKind::OpaqueTy,
|
||||
ImplTraitContext::InBinding => return visit::walk_ty(self, ty),
|
||||
};
|
||||
let id = self.create_def(*id, name, kind, ty.span);
|
||||
match self.impl_trait_context {
|
||||
|
|
@ -365,6 +366,7 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
|
|||
ImplTraitContext::Existential => {
|
||||
self.with_parent(id, |this| visit::walk_ty(this, ty))
|
||||
}
|
||||
ImplTraitContext::InBinding => unreachable!(),
|
||||
};
|
||||
}
|
||||
_ => visit::walk_ty(self, ty),
|
||||
|
|
@ -374,6 +376,13 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> {
|
|||
fn visit_stmt(&mut self, stmt: &'a Stmt) {
|
||||
match stmt.kind {
|
||||
StmtKind::MacCall(..) => self.visit_macro_invoc(stmt.id),
|
||||
// FIXME(impl_trait_in_bindings): We don't really have a good way of
|
||||
// introducing the right `ImplTraitContext` here for all the cases we
|
||||
// care about, in case we want to introduce ITIB to other positions
|
||||
// such as turbofishes (e.g. `foo::<impl Fn()>(|| {})`).
|
||||
StmtKind::Let(ref local) => self.with_impl_trait(ImplTraitContext::InBinding, |this| {
|
||||
visit::walk_local(this, local)
|
||||
}),
|
||||
_ => visit::walk_stmt(self, stmt),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -190,6 +190,7 @@ impl InvocationParent {
|
|||
enum ImplTraitContext {
|
||||
Existential,
|
||||
Universal,
|
||||
InBinding,
|
||||
}
|
||||
|
||||
/// Used for tracking import use types which will be used for redundant import checking.
|
||||
|
|
|
|||
|
|
@ -439,7 +439,7 @@ impl<'tcx> Printer<'tcx> for SymbolMangler<'tcx> {
|
|||
let sig = sig_tys.with(hdr);
|
||||
self.push("F");
|
||||
self.in_binder(&sig, |cx, sig| {
|
||||
if sig.safety == hir::Safety::Unsafe {
|
||||
if sig.safety.is_unsafe() {
|
||||
cx.push("U");
|
||||
}
|
||||
match sig.abi {
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ pub fn type_allowed_to_implement_copy<'tcx>(
|
|||
return Err(CopyImplementationError::HasDestructor);
|
||||
}
|
||||
|
||||
if impl_safety == hir::Safety::Safe && self_type.has_unsafe_fields() {
|
||||
if impl_safety.is_safe() && self_type.has_unsafe_fields() {
|
||||
return Err(CopyImplementationError::HasUnsafeFields);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1869,7 +1869,7 @@ impl<T, A: Allocator> VecDeque<T, A> {
|
|||
///
|
||||
/// # Panics
|
||||
///
|
||||
/// Panics if `index` is greater than deque's length
|
||||
/// Panics if `index` is strictly greater than deque's length
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -1884,6 +1884,9 @@ impl<T, A: Allocator> VecDeque<T, A> {
|
|||
///
|
||||
/// vec_deque.insert(1, 'd');
|
||||
/// assert_eq!(vec_deque, &['a', 'd', 'b', 'c']);
|
||||
///
|
||||
/// vec_deque.insert(4, 'e');
|
||||
/// assert_eq!(vec_deque, &['a', 'd', 'b', 'c', 'e']);
|
||||
/// ```
|
||||
#[stable(feature = "deque_extras_15", since = "1.5.0")]
|
||||
#[track_caller]
|
||||
|
|
@ -1928,13 +1931,13 @@ impl<T, A: Allocator> VecDeque<T, A> {
|
|||
/// use std::collections::VecDeque;
|
||||
///
|
||||
/// let mut buf = VecDeque::new();
|
||||
/// buf.push_back(1);
|
||||
/// buf.push_back(2);
|
||||
/// buf.push_back(3);
|
||||
/// assert_eq!(buf, [1, 2, 3]);
|
||||
/// buf.push_back('a');
|
||||
/// buf.push_back('b');
|
||||
/// buf.push_back('c');
|
||||
/// assert_eq!(buf, ['a', 'b', 'c']);
|
||||
///
|
||||
/// assert_eq!(buf.remove(1), Some(2));
|
||||
/// assert_eq!(buf, [1, 3]);
|
||||
/// assert_eq!(buf.remove(1), Some('b'));
|
||||
/// assert_eq!(buf, ['a', 'c']);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[rustc_confusables("delete", "take")]
|
||||
|
|
@ -1982,10 +1985,10 @@ impl<T, A: Allocator> VecDeque<T, A> {
|
|||
/// ```
|
||||
/// use std::collections::VecDeque;
|
||||
///
|
||||
/// let mut buf: VecDeque<_> = [1, 2, 3].into();
|
||||
/// let mut buf: VecDeque<_> = ['a', 'b', 'c'].into();
|
||||
/// let buf2 = buf.split_off(1);
|
||||
/// assert_eq!(buf, [1]);
|
||||
/// assert_eq!(buf2, [2, 3]);
|
||||
/// assert_eq!(buf, ['a']);
|
||||
/// assert_eq!(buf2, ['b', 'c']);
|
||||
/// ```
|
||||
#[inline]
|
||||
#[must_use = "use `.truncate()` if you don't need the other half"]
|
||||
|
|
|
|||
|
|
@ -1953,11 +1953,11 @@ impl<T, A: Allocator> Vec<T, A> {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let mut vec = vec![1, 2, 3];
|
||||
/// vec.insert(1, 4);
|
||||
/// assert_eq!(vec, [1, 4, 2, 3]);
|
||||
/// vec.insert(4, 5);
|
||||
/// assert_eq!(vec, [1, 4, 2, 3, 5]);
|
||||
/// let mut vec = vec!['a', 'b', 'c'];
|
||||
/// vec.insert(1, 'd');
|
||||
/// assert_eq!(vec, ['a', 'd', 'b', 'c']);
|
||||
/// vec.insert(4, 'e');
|
||||
/// assert_eq!(vec, ['a', 'd', 'b', 'c', 'e']);
|
||||
/// ```
|
||||
///
|
||||
/// # Time complexity
|
||||
|
|
@ -2024,9 +2024,9 @@ impl<T, A: Allocator> Vec<T, A> {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let mut v = vec![1, 2, 3];
|
||||
/// assert_eq!(v.remove(1), 2);
|
||||
/// assert_eq!(v, [1, 3]);
|
||||
/// let mut v = vec!['a', 'b', 'c'];
|
||||
/// assert_eq!(v.remove(1), 'b');
|
||||
/// assert_eq!(v, ['a', 'c']);
|
||||
/// ```
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
#[track_caller]
|
||||
|
|
@ -2715,10 +2715,10 @@ impl<T, A: Allocator> Vec<T, A> {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let mut vec = vec![1, 2, 3];
|
||||
/// let mut vec = vec!['a', 'b', 'c'];
|
||||
/// let vec2 = vec.split_off(1);
|
||||
/// assert_eq!(vec, [1]);
|
||||
/// assert_eq!(vec2, [2, 3]);
|
||||
/// assert_eq!(vec, ['a']);
|
||||
/// assert_eq!(vec2, ['b', 'c']);
|
||||
/// ```
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[inline]
|
||||
|
|
@ -2982,9 +2982,9 @@ impl<T: Clone, A: Allocator> Vec<T, A> {
|
|||
/// vec.resize(3, "world");
|
||||
/// assert_eq!(vec, ["hello", "world", "world"]);
|
||||
///
|
||||
/// let mut vec = vec![1, 2, 3, 4];
|
||||
/// vec.resize(2, 0);
|
||||
/// assert_eq!(vec, [1, 2]);
|
||||
/// let mut vec = vec!['a', 'b', 'c', 'd'];
|
||||
/// vec.resize(2, '_');
|
||||
/// assert_eq!(vec, ['a', 'b']);
|
||||
/// ```
|
||||
#[cfg(not(no_global_oom_handling))]
|
||||
#[stable(feature = "vec_resize", since = "1.5.0")]
|
||||
|
|
|
|||
|
|
@ -1883,23 +1883,23 @@ impl<T> [T] {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let v = [1, 2, 3, 4, 5, 6];
|
||||
/// let v = ['a', 'b', 'c'];
|
||||
///
|
||||
/// {
|
||||
/// let (left, right) = v.split_at(0);
|
||||
/// assert_eq!(left, []);
|
||||
/// assert_eq!(right, [1, 2, 3, 4, 5, 6]);
|
||||
/// assert_eq!(right, ['a', 'b', 'c']);
|
||||
/// }
|
||||
///
|
||||
/// {
|
||||
/// let (left, right) = v.split_at(2);
|
||||
/// assert_eq!(left, [1, 2]);
|
||||
/// assert_eq!(right, [3, 4, 5, 6]);
|
||||
/// assert_eq!(left, ['a', 'b']);
|
||||
/// assert_eq!(right, ['c']);
|
||||
/// }
|
||||
///
|
||||
/// {
|
||||
/// let (left, right) = v.split_at(6);
|
||||
/// assert_eq!(left, [1, 2, 3, 4, 5, 6]);
|
||||
/// let (left, right) = v.split_at(3);
|
||||
/// assert_eq!(left, ['a', 'b', 'c']);
|
||||
/// assert_eq!(right, []);
|
||||
/// }
|
||||
/// ```
|
||||
|
|
@ -1969,23 +1969,23 @@ impl<T> [T] {
|
|||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// let v = [1, 2, 3, 4, 5, 6];
|
||||
/// let v = ['a', 'b', 'c'];
|
||||
///
|
||||
/// unsafe {
|
||||
/// let (left, right) = v.split_at_unchecked(0);
|
||||
/// assert_eq!(left, []);
|
||||
/// assert_eq!(right, [1, 2, 3, 4, 5, 6]);
|
||||
/// assert_eq!(right, ['a', 'b', 'c']);
|
||||
/// }
|
||||
///
|
||||
/// unsafe {
|
||||
/// let (left, right) = v.split_at_unchecked(2);
|
||||
/// assert_eq!(left, [1, 2]);
|
||||
/// assert_eq!(right, [3, 4, 5, 6]);
|
||||
/// assert_eq!(left, ['a', 'b']);
|
||||
/// assert_eq!(right, ['c']);
|
||||
/// }
|
||||
///
|
||||
/// unsafe {
|
||||
/// let (left, right) = v.split_at_unchecked(6);
|
||||
/// assert_eq!(left, [1, 2, 3, 4, 5, 6]);
|
||||
/// let (left, right) = v.split_at_unchecked(3);
|
||||
/// assert_eq!(left, ['a', 'b', 'c']);
|
||||
/// assert_eq!(right, []);
|
||||
/// }
|
||||
/// ```
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ mod tests;
|
|||
use crate::cell::UnsafeCell;
|
||||
use crate::fmt;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::ManuallyDrop;
|
||||
use crate::mem::{self, ManuallyDrop};
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use crate::ptr::NonNull;
|
||||
use crate::sync::{LockResult, TryLockError, TryLockResult, poison};
|
||||
use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult, poison};
|
||||
use crate::sys::sync as sys;
|
||||
|
||||
/// A mutual exclusion primitive useful for protecting shared data
|
||||
|
|
@ -273,6 +273,100 @@ impl<T> Mutex<T> {
|
|||
pub const fn new(t: T) -> Mutex<T> {
|
||||
Mutex { inner: sys::Mutex::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) }
|
||||
}
|
||||
|
||||
/// Returns the contained value by cloning it.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.get_cloned().unwrap(), 7);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
pub fn get_cloned(&self) -> Result<T, PoisonError<()>>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
match self.lock() {
|
||||
Ok(guard) => Ok((*guard).clone()),
|
||||
Err(_) => Err(PoisonError::new(())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the contained value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error containing the provided `value` instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.get_cloned().unwrap(), 7);
|
||||
/// mutex.set(11).unwrap();
|
||||
/// assert_eq!(mutex.get_cloned().unwrap(), 11);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
pub fn set(&self, value: T) -> Result<(), PoisonError<T>> {
|
||||
if mem::needs_drop::<T>() {
|
||||
// If the contained value has non-trivial destructor, we
|
||||
// call that destructor after the lock being released.
|
||||
self.replace(value).map(drop)
|
||||
} else {
|
||||
match self.lock() {
|
||||
Ok(mut guard) => {
|
||||
*guard = value;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(PoisonError::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces the contained value with `value`, and returns the old contained value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error containing the provided `value` instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::Mutex;
|
||||
///
|
||||
/// let mut mutex = Mutex::new(7);
|
||||
///
|
||||
/// assert_eq!(mutex.replace(11).unwrap(), 7);
|
||||
/// assert_eq!(mutex.get_cloned().unwrap(), 11);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
pub fn replace(&self, value: T) -> LockResult<T> {
|
||||
match self.lock() {
|
||||
Ok(mut guard) => Ok(mem::replace(&mut *guard, value)),
|
||||
Err(_) => Err(PoisonError::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Mutex<T> {
|
||||
|
|
@ -290,7 +384,8 @@ impl<T: ?Sized> Mutex<T> {
|
|||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error once the mutex is acquired.
|
||||
/// this call will return an error once the mutex is acquired. The acquired
|
||||
/// mutex guard will be contained in the returned error.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
|
@ -331,7 +426,8 @@ impl<T: ?Sized> Mutex<T> {
|
|||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return the [`Poisoned`] error if the mutex would
|
||||
/// otherwise be acquired.
|
||||
/// otherwise be acquired. An acquired lock guard will be contained
|
||||
/// in the returned error.
|
||||
///
|
||||
/// If the mutex could not be acquired because it is already locked, then
|
||||
/// this call will return the [`WouldBlock`] error.
|
||||
|
|
@ -438,7 +534,8 @@ impl<T: ?Sized> Mutex<T> {
|
|||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error instead.
|
||||
/// this call will return an error containing the the underlying data
|
||||
/// instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -465,7 +562,8 @@ impl<T: ?Sized> Mutex<T> {
|
|||
/// # Errors
|
||||
///
|
||||
/// If another user of this mutex panicked while holding the mutex, then
|
||||
/// this call will return an error instead.
|
||||
/// this call will return an error containing a mutable reference to the
|
||||
/// underlying data instead.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1,13 +1,34 @@
|
|||
use crate::fmt::Debug;
|
||||
use crate::ops::FnMut;
|
||||
use crate::panic::{self, AssertUnwindSafe};
|
||||
use crate::sync::atomic::{AtomicUsize, Ordering};
|
||||
use crate::sync::mpsc::channel;
|
||||
use crate::sync::{Arc, Condvar, MappedMutexGuard, Mutex, MutexGuard, TryLockError};
|
||||
use crate::thread;
|
||||
use crate::{hint, mem, thread};
|
||||
|
||||
struct Packet<T>(Arc<(Mutex<T>, Condvar)>);
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
struct NonCopy(i32);
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
struct NonCopyNeedsDrop(i32);
|
||||
|
||||
impl Drop for NonCopyNeedsDrop {
|
||||
fn drop(&mut self) {
|
||||
hint::black_box(());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_needs_drop() {
|
||||
assert!(!mem::needs_drop::<NonCopy>());
|
||||
assert!(mem::needs_drop::<NonCopyNeedsDrop>());
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
struct Cloneable(i32);
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let m = Mutex::new(());
|
||||
|
|
@ -57,6 +78,21 @@ fn try_lock() {
|
|||
*m.try_lock().unwrap() = ();
|
||||
}
|
||||
|
||||
fn new_poisoned_mutex<T>(value: T) -> Mutex<T> {
|
||||
let mutex = Mutex::new(value);
|
||||
|
||||
let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
let _guard = mutex.lock().unwrap();
|
||||
|
||||
panic!("test panic to poison mutex");
|
||||
}));
|
||||
|
||||
assert!(catch_unwind_result.is_err());
|
||||
assert!(mutex.is_poisoned());
|
||||
|
||||
mutex
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_inner() {
|
||||
let m = Mutex::new(NonCopy(10));
|
||||
|
|
@ -83,21 +119,31 @@ fn test_into_inner_drop() {
|
|||
|
||||
#[test]
|
||||
fn test_into_inner_poison() {
|
||||
let m = Arc::new(Mutex::new(NonCopy(10)));
|
||||
let m2 = m.clone();
|
||||
let _ = thread::spawn(move || {
|
||||
let _lock = m2.lock().unwrap();
|
||||
panic!("test panic in inner thread to poison mutex");
|
||||
})
|
||||
.join();
|
||||
let m = new_poisoned_mutex(NonCopy(10));
|
||||
|
||||
assert!(m.is_poisoned());
|
||||
match Arc::try_unwrap(m).unwrap().into_inner() {
|
||||
match m.into_inner() {
|
||||
Err(e) => assert_eq!(e.into_inner(), NonCopy(10)),
|
||||
Ok(x) => panic!("into_inner of poisoned Mutex is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_cloned() {
|
||||
let m = Mutex::new(Cloneable(10));
|
||||
|
||||
assert_eq!(m.get_cloned().unwrap(), Cloneable(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_cloned_poison() {
|
||||
let m = new_poisoned_mutex(Cloneable(10));
|
||||
|
||||
match m.get_cloned() {
|
||||
Err(e) => assert_eq!(e.into_inner(), ()),
|
||||
Ok(x) => panic!("get of poisoned Mutex is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_mut() {
|
||||
let mut m = Mutex::new(NonCopy(10));
|
||||
|
|
@ -107,21 +153,90 @@ fn test_get_mut() {
|
|||
|
||||
#[test]
|
||||
fn test_get_mut_poison() {
|
||||
let m = Arc::new(Mutex::new(NonCopy(10)));
|
||||
let m2 = m.clone();
|
||||
let _ = thread::spawn(move || {
|
||||
let _lock = m2.lock().unwrap();
|
||||
panic!("test panic in inner thread to poison mutex");
|
||||
})
|
||||
.join();
|
||||
let mut m = new_poisoned_mutex(NonCopy(10));
|
||||
|
||||
assert!(m.is_poisoned());
|
||||
match Arc::try_unwrap(m).unwrap().get_mut() {
|
||||
match m.get_mut() {
|
||||
Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)),
|
||||
Ok(x) => panic!("get_mut of poisoned Mutex is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = Mutex::new(init());
|
||||
|
||||
assert_eq!(*m.lock().unwrap(), init());
|
||||
m.set(value()).unwrap();
|
||||
assert_eq!(*m.lock().unwrap(), value());
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_poison() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = new_poisoned_mutex(init());
|
||||
|
||||
match m.set(value()) {
|
||||
Err(e) => {
|
||||
assert_eq!(e.into_inner(), value());
|
||||
assert_eq!(m.into_inner().unwrap_err().into_inner(), init());
|
||||
}
|
||||
Ok(x) => panic!("set of poisoned Mutex is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = Mutex::new(init());
|
||||
|
||||
assert_eq!(*m.lock().unwrap(), init());
|
||||
assert_eq!(m.replace(value()).unwrap(), init());
|
||||
assert_eq!(*m.lock().unwrap(), value());
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace_poison() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = new_poisoned_mutex(init());
|
||||
|
||||
match m.replace(value()) {
|
||||
Err(e) => {
|
||||
assert_eq!(e.into_inner(), value());
|
||||
assert_eq!(m.into_inner().unwrap_err().into_inner(), init());
|
||||
}
|
||||
Ok(x) => panic!("replace of poisoned Mutex is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_mutex_arc_condvar() {
|
||||
let packet = Packet(Arc::new((Mutex::new(false), Condvar::new())));
|
||||
|
|
@ -269,7 +384,7 @@ fn test_mapping_mapped_guard() {
|
|||
fn panic_while_mapping_unlocked_poison() {
|
||||
let lock = Mutex::new(());
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.lock().unwrap();
|
||||
let _guard = MutexGuard::map::<(), _>(guard, |_| panic!());
|
||||
});
|
||||
|
|
@ -282,7 +397,7 @@ fn panic_while_mapping_unlocked_poison() {
|
|||
Err(TryLockError::Poisoned(_)) => {}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.lock().unwrap();
|
||||
let _guard = MutexGuard::try_map::<(), _>(guard, |_| panic!());
|
||||
});
|
||||
|
|
@ -295,7 +410,7 @@ fn panic_while_mapping_unlocked_poison() {
|
|||
Err(TryLockError::Poisoned(_)) => {}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.lock().unwrap();
|
||||
let guard = MutexGuard::map::<(), _>(guard, |val| val);
|
||||
let _guard = MappedMutexGuard::map::<(), _>(guard, |_| panic!());
|
||||
|
|
@ -309,7 +424,7 @@ fn panic_while_mapping_unlocked_poison() {
|
|||
Err(TryLockError::Poisoned(_)) => {}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.lock().unwrap();
|
||||
let guard = MutexGuard::map::<(), _>(guard, |val| val);
|
||||
let _guard = MappedMutexGuard::try_map::<(), _>(guard, |_| panic!());
|
||||
|
|
|
|||
|
|
@ -87,8 +87,8 @@ pub struct Guard {
|
|||
///
|
||||
/// Both [`Mutex`]es and [`RwLock`]s are poisoned whenever a thread fails while the lock
|
||||
/// is held. The precise semantics for when a lock is poisoned is documented on
|
||||
/// each lock, but once a lock is poisoned then all future acquisitions will
|
||||
/// return this error.
|
||||
/// each lock. For a lock in the poisoned state, unless the state is cleared manually,
|
||||
/// all future acquisitions will return this error.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -118,7 +118,7 @@ pub struct Guard {
|
|||
/// [`RwLock`]: crate::sync::RwLock
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub struct PoisonError<T> {
|
||||
guard: T,
|
||||
data: T,
|
||||
#[cfg(not(panic = "unwind"))]
|
||||
_never: !,
|
||||
}
|
||||
|
|
@ -147,14 +147,15 @@ pub enum TryLockError<T> {
|
|||
/// A type alias for the result of a lock method which can be poisoned.
|
||||
///
|
||||
/// The [`Ok`] variant of this result indicates that the primitive was not
|
||||
/// poisoned, and the `Guard` is contained within. The [`Err`] variant indicates
|
||||
/// poisoned, and the operation result is contained within. The [`Err`] variant indicates
|
||||
/// that the primitive was poisoned. Note that the [`Err`] variant *also* carries
|
||||
/// the associated guard, and it can be acquired through the [`into_inner`]
|
||||
/// method.
|
||||
/// an associated value assigned by the lock method, and it can be acquired through the
|
||||
/// [`into_inner`] method. The semantics of the associated value depends on the corresponding
|
||||
/// lock method.
|
||||
///
|
||||
/// [`into_inner`]: PoisonError::into_inner
|
||||
#[stable(feature = "rust1", since = "1.0.0")]
|
||||
pub type LockResult<Guard> = Result<Guard, PoisonError<Guard>>;
|
||||
pub type LockResult<T> = Result<T, PoisonError<T>>;
|
||||
|
||||
/// A type alias for the result of a nonblocking locking method.
|
||||
///
|
||||
|
|
@ -195,8 +196,8 @@ impl<T> PoisonError<T> {
|
|||
/// This method may panic if std was built with `panic="abort"`.
|
||||
#[cfg(panic = "unwind")]
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn new(guard: T) -> PoisonError<T> {
|
||||
PoisonError { guard }
|
||||
pub fn new(data: T) -> PoisonError<T> {
|
||||
PoisonError { data }
|
||||
}
|
||||
|
||||
/// Creates a `PoisonError`.
|
||||
|
|
@ -208,12 +209,12 @@ impl<T> PoisonError<T> {
|
|||
#[cfg(not(panic = "unwind"))]
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
#[track_caller]
|
||||
pub fn new(_guard: T) -> PoisonError<T> {
|
||||
pub fn new(_data: T) -> PoisonError<T> {
|
||||
panic!("PoisonError created in a libstd built with panic=\"abort\"")
|
||||
}
|
||||
|
||||
/// Consumes this error indicating that a lock is poisoned, returning the
|
||||
/// underlying guard to allow access regardless.
|
||||
/// associated data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -238,21 +239,21 @@ impl<T> PoisonError<T> {
|
|||
/// ```
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn into_inner(self) -> T {
|
||||
self.guard
|
||||
self.data
|
||||
}
|
||||
|
||||
/// Reaches into this error indicating that a lock is poisoned, returning a
|
||||
/// reference to the underlying guard to allow access regardless.
|
||||
/// reference to the associated data.
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn get_ref(&self) -> &T {
|
||||
&self.guard
|
||||
&self.data
|
||||
}
|
||||
|
||||
/// Reaches into this error indicating that a lock is poisoned, returning a
|
||||
/// mutable reference to the underlying guard to allow access regardless.
|
||||
/// mutable reference to the associated data.
|
||||
#[stable(feature = "sync_poison", since = "1.2.0")]
|
||||
pub fn get_mut(&mut self) -> &mut T {
|
||||
&mut self.guard
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -322,6 +323,6 @@ where
|
|||
match result {
|
||||
Ok(t) => Ok(f(t)),
|
||||
#[cfg(panic = "unwind")]
|
||||
Err(PoisonError { guard }) => Err(PoisonError::new(f(guard))),
|
||||
Err(PoisonError { data }) => Err(PoisonError::new(f(data))),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ mod tests;
|
|||
use crate::cell::UnsafeCell;
|
||||
use crate::fmt;
|
||||
use crate::marker::PhantomData;
|
||||
use crate::mem::{ManuallyDrop, forget};
|
||||
use crate::mem::{self, ManuallyDrop, forget};
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use crate::ptr::NonNull;
|
||||
use crate::sync::{LockResult, PoisonError, TryLockError, TryLockResult, poison};
|
||||
|
|
@ -224,6 +224,103 @@ impl<T> RwLock<T> {
|
|||
pub const fn new(t: T) -> RwLock<T> {
|
||||
RwLock { inner: sys::RwLock::new(), poison: poison::Flag::new(), data: UnsafeCell::new(t) }
|
||||
}
|
||||
|
||||
/// Returns the contained value by cloning it.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the `RwLock` is poisoned. An
|
||||
/// `RwLock` is poisoned whenever a writer panics while holding an exclusive
|
||||
/// lock.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::RwLock;
|
||||
///
|
||||
/// let mut lock = RwLock::new(7);
|
||||
///
|
||||
/// assert_eq!(lock.get_cloned().unwrap(), 7);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
pub fn get_cloned(&self) -> Result<T, PoisonError<()>>
|
||||
where
|
||||
T: Clone,
|
||||
{
|
||||
match self.read() {
|
||||
Ok(guard) => Ok((*guard).clone()),
|
||||
Err(_) => Err(PoisonError::new(())),
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the contained value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error containing the provided `value` if
|
||||
/// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer
|
||||
/// panics while holding an exclusive lock.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::RwLock;
|
||||
///
|
||||
/// let mut lock = RwLock::new(7);
|
||||
///
|
||||
/// assert_eq!(lock.get_cloned().unwrap(), 7);
|
||||
/// lock.set(11).unwrap();
|
||||
/// assert_eq!(lock.get_cloned().unwrap(), 11);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
pub fn set(&self, value: T) -> Result<(), PoisonError<T>> {
|
||||
if mem::needs_drop::<T>() {
|
||||
// If the contained value has non-trivial destructor, we
|
||||
// call that destructor after the lock being released.
|
||||
self.replace(value).map(drop)
|
||||
} else {
|
||||
match self.write() {
|
||||
Ok(mut guard) => {
|
||||
*guard = value;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Err(_) => Err(PoisonError::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Replaces the contained value with `value`, and returns the old contained value.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error containing the provided `value` if
|
||||
/// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer
|
||||
/// panics while holding an exclusive lock.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// #![feature(lock_value_accessors)]
|
||||
///
|
||||
/// use std::sync::RwLock;
|
||||
///
|
||||
/// let mut lock = RwLock::new(7);
|
||||
///
|
||||
/// assert_eq!(lock.replace(11).unwrap(), 7);
|
||||
/// assert_eq!(lock.get_cloned().unwrap(), 11);
|
||||
/// ```
|
||||
#[unstable(feature = "lock_value_accessors", issue = "133407")]
|
||||
pub fn replace(&self, value: T) -> LockResult<T> {
|
||||
match self.write() {
|
||||
Ok(mut guard) => Ok(mem::replace(&mut *guard, value)),
|
||||
Err(_) => Err(PoisonError::new(value)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> RwLock<T> {
|
||||
|
|
@ -244,7 +341,8 @@ impl<T: ?Sized> RwLock<T> {
|
|||
/// This function will return an error if the `RwLock` is poisoned. An
|
||||
/// `RwLock` is poisoned whenever a writer panics while holding an exclusive
|
||||
/// lock. The failure will occur immediately after the lock has been
|
||||
/// acquired.
|
||||
/// acquired. The acquired lock guard will be contained in the returned
|
||||
/// error.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
|
@ -292,7 +390,8 @@ impl<T: ?Sized> RwLock<T> {
|
|||
/// This function will return the [`Poisoned`] error if the `RwLock` is
|
||||
/// poisoned. An `RwLock` is poisoned whenever a writer panics while holding
|
||||
/// an exclusive lock. `Poisoned` will only be returned if the lock would
|
||||
/// have otherwise been acquired.
|
||||
/// have otherwise been acquired. An acquired lock guard will be contained
|
||||
/// in the returned error.
|
||||
///
|
||||
/// This function will return the [`WouldBlock`] error if the `RwLock` could
|
||||
/// not be acquired because it was already locked exclusively.
|
||||
|
|
@ -337,7 +436,8 @@ impl<T: ?Sized> RwLock<T> {
|
|||
///
|
||||
/// This function will return an error if the `RwLock` is poisoned. An
|
||||
/// `RwLock` is poisoned whenever a writer panics while holding an exclusive
|
||||
/// lock. An error will be returned when the lock is acquired.
|
||||
/// lock. An error will be returned when the lock is acquired. The acquired
|
||||
/// lock guard will be contained in the returned error.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
|
|
@ -380,7 +480,8 @@ impl<T: ?Sized> RwLock<T> {
|
|||
/// This function will return the [`Poisoned`] error if the `RwLock` is
|
||||
/// poisoned. An `RwLock` is poisoned whenever a writer panics while holding
|
||||
/// an exclusive lock. `Poisoned` will only be returned if the lock would
|
||||
/// have otherwise been acquired.
|
||||
/// have otherwise been acquired. An acquired lock guard will be contained
|
||||
/// in the returned error.
|
||||
///
|
||||
/// This function will return the [`WouldBlock`] error if the `RwLock` could
|
||||
/// not be acquired because it was already locked exclusively.
|
||||
|
|
@ -481,10 +582,10 @@ impl<T: ?Sized> RwLock<T> {
|
|||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the `RwLock` is poisoned. An
|
||||
/// `RwLock` is poisoned whenever a writer panics while holding an exclusive
|
||||
/// lock. An error will only be returned if the lock would have otherwise
|
||||
/// been acquired.
|
||||
/// This function will return an error containing the underlying data if
|
||||
/// the `RwLock` is poisoned. An `RwLock` is poisoned whenever a writer
|
||||
/// panics while holding an exclusive lock. An error will only be returned
|
||||
/// if the lock would have otherwise been acquired.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
@ -514,10 +615,11 @@ impl<T: ?Sized> RwLock<T> {
|
|||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// This function will return an error if the `RwLock` is poisoned. An
|
||||
/// `RwLock` is poisoned whenever a writer panics while holding an exclusive
|
||||
/// lock. An error will only be returned if the lock would have otherwise
|
||||
/// been acquired.
|
||||
/// This function will return an error containing a mutable reference to
|
||||
/// the underlying data if the `RwLock` is poisoned. An `RwLock` is
|
||||
/// poisoned whenever a writer panics while holding an exclusive lock.
|
||||
/// An error will only be returned if the lock would have otherwise been
|
||||
/// acquired.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
|
|
|
|||
|
|
@ -1,16 +1,37 @@
|
|||
use rand::Rng;
|
||||
|
||||
use crate::fmt::Debug;
|
||||
use crate::ops::FnMut;
|
||||
use crate::panic::{self, AssertUnwindSafe};
|
||||
use crate::sync::atomic::{AtomicUsize, Ordering};
|
||||
use crate::sync::mpsc::channel;
|
||||
use crate::sync::{
|
||||
Arc, MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard,
|
||||
TryLockError,
|
||||
};
|
||||
use crate::thread;
|
||||
use crate::{hint, mem, thread};
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
struct NonCopy(i32);
|
||||
|
||||
#[derive(Eq, PartialEq, Debug)]
|
||||
struct NonCopyNeedsDrop(i32);
|
||||
|
||||
impl Drop for NonCopyNeedsDrop {
|
||||
fn drop(&mut self) {
|
||||
hint::black_box(());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_needs_drop() {
|
||||
assert!(!mem::needs_drop::<NonCopy>());
|
||||
assert!(mem::needs_drop::<NonCopyNeedsDrop>());
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
struct Cloneable(i32);
|
||||
|
||||
#[test]
|
||||
fn smoke() {
|
||||
let l = RwLock::new(());
|
||||
|
|
@ -255,6 +276,21 @@ fn test_rwlock_try_write() {
|
|||
drop(mapped_read_guard);
|
||||
}
|
||||
|
||||
fn new_poisoned_rwlock<T>(value: T) -> RwLock<T> {
|
||||
let lock = RwLock::new(value);
|
||||
|
||||
let catch_unwind_result = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
let _guard = lock.write().unwrap();
|
||||
|
||||
panic!("test panic to poison RwLock");
|
||||
}));
|
||||
|
||||
assert!(catch_unwind_result.is_err());
|
||||
assert!(lock.is_poisoned());
|
||||
|
||||
lock
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_into_inner() {
|
||||
let m = RwLock::new(NonCopy(10));
|
||||
|
|
@ -281,21 +317,31 @@ fn test_into_inner_drop() {
|
|||
|
||||
#[test]
|
||||
fn test_into_inner_poison() {
|
||||
let m = Arc::new(RwLock::new(NonCopy(10)));
|
||||
let m2 = m.clone();
|
||||
let _ = thread::spawn(move || {
|
||||
let _lock = m2.write().unwrap();
|
||||
panic!("test panic in inner thread to poison RwLock");
|
||||
})
|
||||
.join();
|
||||
let m = new_poisoned_rwlock(NonCopy(10));
|
||||
|
||||
assert!(m.is_poisoned());
|
||||
match Arc::try_unwrap(m).unwrap().into_inner() {
|
||||
match m.into_inner() {
|
||||
Err(e) => assert_eq!(e.into_inner(), NonCopy(10)),
|
||||
Ok(x) => panic!("into_inner of poisoned RwLock is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_cloned() {
|
||||
let m = RwLock::new(Cloneable(10));
|
||||
|
||||
assert_eq!(m.get_cloned().unwrap(), Cloneable(10));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_cloned_poison() {
|
||||
let m = new_poisoned_rwlock(Cloneable(10));
|
||||
|
||||
match m.get_cloned() {
|
||||
Err(e) => assert_eq!(e.into_inner(), ()),
|
||||
Ok(x) => panic!("get of poisoned RwLock is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_mut() {
|
||||
let mut m = RwLock::new(NonCopy(10));
|
||||
|
|
@ -305,21 +351,90 @@ fn test_get_mut() {
|
|||
|
||||
#[test]
|
||||
fn test_get_mut_poison() {
|
||||
let m = Arc::new(RwLock::new(NonCopy(10)));
|
||||
let m2 = m.clone();
|
||||
let _ = thread::spawn(move || {
|
||||
let _lock = m2.write().unwrap();
|
||||
panic!("test panic in inner thread to poison RwLock");
|
||||
})
|
||||
.join();
|
||||
let mut m = new_poisoned_rwlock(NonCopy(10));
|
||||
|
||||
assert!(m.is_poisoned());
|
||||
match Arc::try_unwrap(m).unwrap().get_mut() {
|
||||
match m.get_mut() {
|
||||
Err(e) => assert_eq!(*e.into_inner(), NonCopy(10)),
|
||||
Ok(x) => panic!("get_mut of poisoned RwLock is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = RwLock::new(init());
|
||||
|
||||
assert_eq!(*m.read().unwrap(), init());
|
||||
m.set(value()).unwrap();
|
||||
assert_eq!(*m.read().unwrap(), value());
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_poison() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = new_poisoned_rwlock(init());
|
||||
|
||||
match m.set(value()) {
|
||||
Err(e) => {
|
||||
assert_eq!(e.into_inner(), value());
|
||||
assert_eq!(m.into_inner().unwrap_err().into_inner(), init());
|
||||
}
|
||||
Ok(x) => panic!("set of poisoned RwLock is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = RwLock::new(init());
|
||||
|
||||
assert_eq!(*m.read().unwrap(), init());
|
||||
assert_eq!(m.replace(value()).unwrap(), init());
|
||||
assert_eq!(*m.read().unwrap(), value());
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_replace_poison() {
|
||||
fn inner<T>(mut init: impl FnMut() -> T, mut value: impl FnMut() -> T)
|
||||
where
|
||||
T: Debug + Eq,
|
||||
{
|
||||
let m = new_poisoned_rwlock(init());
|
||||
|
||||
match m.replace(value()) {
|
||||
Err(e) => {
|
||||
assert_eq!(e.into_inner(), value());
|
||||
assert_eq!(m.into_inner().unwrap_err().into_inner(), init());
|
||||
}
|
||||
Ok(x) => panic!("replace of poisoned RwLock is Ok: {x:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
inner(|| NonCopy(10), || NonCopy(20));
|
||||
inner(|| NonCopyNeedsDrop(10), || NonCopyNeedsDrop(20));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_guard_covariance() {
|
||||
fn do_stuff<'a>(_: RwLockReadGuard<'_, &'a i32>, _: &'a i32) {}
|
||||
|
|
@ -370,7 +485,7 @@ fn test_mapping_mapped_guard() {
|
|||
fn panic_while_mapping_read_unlocked_no_poison() {
|
||||
let lock = RwLock::new(());
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.read().unwrap();
|
||||
let _guard = RwLockReadGuard::map::<(), _>(guard, |_| panic!());
|
||||
});
|
||||
|
|
@ -385,7 +500,7 @@ fn panic_while_mapping_read_unlocked_no_poison() {
|
|||
}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.read().unwrap();
|
||||
let _guard = RwLockReadGuard::try_map::<(), _>(guard, |_| panic!());
|
||||
});
|
||||
|
|
@ -400,7 +515,7 @@ fn panic_while_mapping_read_unlocked_no_poison() {
|
|||
}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.read().unwrap();
|
||||
let guard = RwLockReadGuard::map::<(), _>(guard, |val| val);
|
||||
let _guard = MappedRwLockReadGuard::map::<(), _>(guard, |_| panic!());
|
||||
|
|
@ -416,7 +531,7 @@ fn panic_while_mapping_read_unlocked_no_poison() {
|
|||
}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.read().unwrap();
|
||||
let guard = RwLockReadGuard::map::<(), _>(guard, |val| val);
|
||||
let _guard = MappedRwLockReadGuard::try_map::<(), _>(guard, |_| panic!());
|
||||
|
|
@ -439,7 +554,7 @@ fn panic_while_mapping_read_unlocked_no_poison() {
|
|||
fn panic_while_mapping_write_unlocked_poison() {
|
||||
let lock = RwLock::new(());
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.write().unwrap();
|
||||
let _guard = RwLockWriteGuard::map::<(), _>(guard, |_| panic!());
|
||||
});
|
||||
|
|
@ -452,7 +567,7 @@ fn panic_while_mapping_write_unlocked_poison() {
|
|||
Err(TryLockError::Poisoned(_)) => {}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.write().unwrap();
|
||||
let _guard = RwLockWriteGuard::try_map::<(), _>(guard, |_| panic!());
|
||||
});
|
||||
|
|
@ -467,7 +582,7 @@ fn panic_while_mapping_write_unlocked_poison() {
|
|||
Err(TryLockError::Poisoned(_)) => {}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.write().unwrap();
|
||||
let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val);
|
||||
let _guard = MappedRwLockWriteGuard::map::<(), _>(guard, |_| panic!());
|
||||
|
|
@ -483,7 +598,7 @@ fn panic_while_mapping_write_unlocked_poison() {
|
|||
Err(TryLockError::Poisoned(_)) => {}
|
||||
}
|
||||
|
||||
let _ = crate::panic::catch_unwind(|| {
|
||||
let _ = panic::catch_unwind(|| {
|
||||
let guard = lock.write().unwrap();
|
||||
let guard = RwLockWriteGuard::map::<(), _>(guard, |val| val);
|
||||
let _guard = MappedRwLockWriteGuard::try_map::<(), _>(guard, |_| panic!());
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@ impl Socket {
|
|||
target_os = "netbsd",
|
||||
target_os = "openbsd",
|
||||
target_os = "nto",
|
||||
target_os = "solaris",
|
||||
))] {
|
||||
// On platforms that support it we pass the SOCK_CLOEXEC
|
||||
// flag to atomically create the socket and set it as
|
||||
|
|
|
|||
|
|
@ -1626,10 +1626,7 @@ pub(crate) trait PrintWithSpace {
|
|||
|
||||
impl PrintWithSpace for hir::Safety {
|
||||
fn print_with_space(&self) -> &str {
|
||||
match self {
|
||||
hir::Safety::Unsafe => "unsafe ",
|
||||
hir::Safety::Safe => "",
|
||||
}
|
||||
self.prefix_str()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -469,7 +469,7 @@ fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[cl
|
|||
|
||||
let unsafety_flag = match myitem.kind {
|
||||
clean::FunctionItem(_) | clean::ForeignFunctionItem(..)
|
||||
if myitem.fn_header(tcx).unwrap().safety == hir::Safety::Unsafe =>
|
||||
if myitem.fn_header(tcx).unwrap().safety.is_unsafe() =>
|
||||
{
|
||||
"<sup title=\"unsafe function\">⚠</sup>"
|
||||
}
|
||||
|
|
@ -1926,9 +1926,7 @@ fn item_static(
|
|||
buffer,
|
||||
"{vis}{safe}static {mutability}{name}: {typ}",
|
||||
vis = visibility_print_with_space(it, cx),
|
||||
safe = safety
|
||||
.map(|safe| if safe == hir::Safety::Unsafe { "unsafe " } else { "" })
|
||||
.unwrap_or(""),
|
||||
safe = safety.map(|safe| safe.prefix_str()).unwrap_or(""),
|
||||
mutability = s.mutability.print_with_space(),
|
||||
name = it.name.unwrap(),
|
||||
typ = s.type_.print(cx)
|
||||
|
|
|
|||
|
|
@ -639,7 +639,7 @@ impl FromClean<clean::BareFunctionDecl> for FunctionPointer {
|
|||
let clean::BareFunctionDecl { safety, generic_params, decl, abi } = bare_decl;
|
||||
FunctionPointer {
|
||||
header: FunctionHeader {
|
||||
is_unsafe: matches!(safety, rustc_hir::Safety::Unsafe),
|
||||
is_unsafe: safety.is_unsafe(),
|
||||
is_const: false,
|
||||
is_async: false,
|
||||
abi: convert_abi(abi),
|
||||
|
|
@ -669,7 +669,7 @@ impl FromClean<clean::Trait> for Trait {
|
|||
fn from_clean(trait_: clean::Trait, renderer: &JsonRenderer<'_>) -> Self {
|
||||
let tcx = renderer.tcx;
|
||||
let is_auto = trait_.is_auto(tcx);
|
||||
let is_unsafe = trait_.safety(tcx) == rustc_hir::Safety::Unsafe;
|
||||
let is_unsafe = trait_.safety(tcx).is_unsafe();
|
||||
let is_dyn_compatible = trait_.is_dyn_compatible(tcx);
|
||||
let clean::Trait { items, generics, bounds, .. } = trait_;
|
||||
Trait {
|
||||
|
|
@ -711,7 +711,7 @@ impl FromClean<clean::Impl> for Impl {
|
|||
ty::ImplPolarity::Negative => true,
|
||||
};
|
||||
Impl {
|
||||
is_unsafe: safety == rustc_hir::Safety::Unsafe,
|
||||
is_unsafe: safety.is_unsafe(),
|
||||
generics: generics.into_json(renderer),
|
||||
provided_trait_methods: provided_trait_methods
|
||||
.into_iter()
|
||||
|
|
@ -840,7 +840,7 @@ fn convert_static(
|
|||
Static {
|
||||
type_: (*stat.type_).into_json(renderer),
|
||||
is_mutable: stat.mutability == ast::Mutability::Mut,
|
||||
is_unsafe: safety == rustc_hir::Safety::Unsafe,
|
||||
is_unsafe: safety.is_unsafe(),
|
||||
expr: stat
|
||||
.expr
|
||||
.map(|e| rendered_const(tcx, tcx.hir().body(e), tcx.hir().body_owner_def_id(e)))
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use rustc_errors::Applicability;
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item};
|
||||
use rustc_hir::{
|
||||
self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, Safety, UnsafeSource,
|
||||
self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, UnsafeSource,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
|
|
@ -421,7 +421,7 @@ impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
|
|||
id: LocalDefId,
|
||||
) -> Self::Result {
|
||||
if let Some(header) = kind.header()
|
||||
&& header.safety == Safety::Unsafe
|
||||
&& header.safety.is_unsafe()
|
||||
{
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -281,7 +281,7 @@ fn check_inputs(
|
|||
}
|
||||
|
||||
fn check_sig<'tcx>(closure_sig: FnSig<'tcx>, call_sig: FnSig<'tcx>) -> bool {
|
||||
call_sig.safety == Safety::Safe && !has_late_bound_to_non_late_bound_regions(closure_sig, call_sig)
|
||||
call_sig.safety.is_safe() && !has_late_bound_to_non_late_bound_regions(closure_sig, call_sig)
|
||||
}
|
||||
|
||||
/// This walks through both signatures and checks for any time a late-bound region is expected by an
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::source::snippet;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{Body, ExprKind, FnDecl, ImplicitSelfKind, Safety};
|
||||
use rustc_hir::{Body, ExprKind, FnDecl, ImplicitSelfKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty;
|
||||
use rustc_span::Span;
|
||||
|
|
@ -34,7 +34,7 @@ pub fn check_fn(cx: &LateContext<'_>, kind: FnKind<'_>, decl: &FnDecl<'_>, body:
|
|||
ImplicitSelfKind::None => return,
|
||||
};
|
||||
|
||||
let name = if sig.header.safety == Safety::Unsafe {
|
||||
let name = if sig.header.safety.is_unsafe() {
|
||||
name.strip_suffix("_unchecked").unwrap_or(name)
|
||||
} else {
|
||||
name
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ fn check_raw_ptr<'tcx>(
|
|||
body: &'tcx hir::Body<'tcx>,
|
||||
def_id: LocalDefId,
|
||||
) {
|
||||
if safety == hir::Safety::Safe && cx.effective_visibilities.is_exported(def_id) {
|
||||
if safety.is_safe() && cx.effective_visibilities.is_exported(def_id) {
|
||||
let raw_ptrs = iter_input_pats(decl, body)
|
||||
.filter_map(|arg| raw_ptr_arg(cx, arg))
|
||||
.collect::<HirIdSet>();
|
||||
|
|
@ -58,7 +58,7 @@ fn check_raw_ptr<'tcx>(
|
|||
},
|
||||
hir::ExprKind::MethodCall(_, recv, args, _) => {
|
||||
let def_id = typeck.type_dependent_def_id(e.hir_id).unwrap();
|
||||
if cx.tcx.fn_sig(def_id).skip_binder().skip_binder().safety == hir::Safety::Unsafe {
|
||||
if cx.tcx.fn_sig(def_id).skip_binder().skip_binder().safety.is_unsafe() {
|
||||
check_arg(cx, &raw_ptrs, recv);
|
||||
for arg in args {
|
||||
check_arg(cx, &raw_ptrs, arg);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::{implements_trait, is_type_lang_item};
|
||||
use clippy_utils::{return_ty, trait_ref_of_method};
|
||||
use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem, Safety};
|
||||
use rustc_hir::{GenericParamKind, ImplItem, ImplItemKind, LangItem};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_session::declare_lint_pass;
|
||||
use rustc_span::sym;
|
||||
|
|
@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString {
|
|||
if let ImplItemKind::Fn(ref signature, _) = impl_item.kind
|
||||
// #11201
|
||||
&& let header = signature.header
|
||||
&& header.safety == Safety::Safe
|
||||
&& header.safety.is_safe()
|
||||
&& header.abi == Abi::Rust
|
||||
&& impl_item.ident.name == sym::to_string
|
||||
&& let decl = signature.decl
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
|
|||
use clippy_utils::visitors::{Descend, Visitable, for_each_expr};
|
||||
use core::ops::ControlFlow::Continue;
|
||||
use hir::def::{DefKind, Res};
|
||||
use hir::{BlockCheckMode, ExprKind, QPath, Safety, UnOp};
|
||||
use hir::{BlockCheckMode, ExprKind, QPath, UnOp};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_hir as hir;
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
|
|
@ -133,7 +133,7 @@ fn collect_unsafe_exprs<'tcx>(
|
|||
ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr),
|
||||
_ => return Continue(Descend::Yes),
|
||||
};
|
||||
if sig.safety() == Safety::Unsafe {
|
||||
if sig.safety().is_unsafe() {
|
||||
unsafe_ops.push(("unsafe function call occurs here", expr.span));
|
||||
}
|
||||
},
|
||||
|
|
@ -144,7 +144,7 @@ fn collect_unsafe_exprs<'tcx>(
|
|||
.type_dependent_def_id(expr.hir_id)
|
||||
.map(|def_id| cx.tcx.fn_sig(def_id))
|
||||
{
|
||||
if sig.skip_binder().safety() == Safety::Unsafe {
|
||||
if sig.skip_binder().safety().is_unsafe() {
|
||||
unsafe_ops.push(("unsafe method call occurs here", expr.span));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
|
|||
if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
|
||||
let name = impl_item.ident.name;
|
||||
let id = impl_item.owner_id;
|
||||
if sig.header.safety == hir::Safety::Unsafe {
|
||||
if sig.header.safety.is_unsafe() {
|
||||
// can't be implemented for unsafe new
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ use rustc_hir::hir_id::{HirId, HirIdMap};
|
|||
use rustc_hir::intravisit::{Visitor, walk_expr};
|
||||
use rustc_hir::{
|
||||
self as hir, AnonConst, BinOpKind, BindingMode, Body, Expr, ExprKind, FnRetTy, FnSig, GenericArg, ImplItemKind,
|
||||
ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, Safety, TraitFn, TraitItem, TraitItemKind, TyKind,
|
||||
ItemKind, Lifetime, Mutability, Node, Param, PatKind, QPath, TraitFn, TraitItem, TraitItemKind, TyKind,
|
||||
};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_infer::traits::{Obligation, ObligationCause};
|
||||
|
|
@ -541,7 +541,7 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio
|
|||
.collect();
|
||||
if let Some(args) = args
|
||||
&& !args.is_empty()
|
||||
&& body.is_none_or(|body| sig.header.safety == Safety::Unsafe || contains_unsafe_block(cx, body.value))
|
||||
&& body.is_none_or(|body| sig.header.safety.is_unsafe() || contains_unsafe_block(cx, body.value))
|
||||
{
|
||||
span_lint_and_then(
|
||||
cx,
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
|
|||
let item_has_safety_comment = item_has_safety_comment(cx, item);
|
||||
match (&item.kind, item_has_safety_comment) {
|
||||
// lint unsafe impl without safety comment
|
||||
(ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.safety == hir::Safety::Unsafe => {
|
||||
(ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.safety.is_unsafe() => {
|
||||
if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id())
|
||||
&& !is_unsafe_from_proc_macro(cx, item.span)
|
||||
{
|
||||
|
|
@ -227,7 +227,7 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
|
|||
}
|
||||
},
|
||||
// lint safe impl with unnecessary safety comment
|
||||
(ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.safety == hir::Safety::Safe => {
|
||||
(ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.safety.is_safe() => {
|
||||
if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
|
||||
let (span, help_span) = mk_spans(pos);
|
||||
|
||||
|
|
|
|||
|
|
@ -373,7 +373,7 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
|
|||
TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ty_search_pat(ty).1),
|
||||
TyKind::Ref(_, MutTy { ty, .. }) => (Pat::Str("&"), ty_search_pat(ty).1),
|
||||
TyKind::BareFn(bare_fn) => (
|
||||
if bare_fn.safety == Safety::Unsafe {
|
||||
if bare_fn.safety.is_unsafe() {
|
||||
Pat::Str("unsafe")
|
||||
} else if bare_fn.abi != Abi::Rust {
|
||||
Pat::Str("extern")
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
|||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{Expr, FnDecl, LangItem, Safety, TyKind};
|
||||
use rustc_hir::{Expr, FnDecl, LangItem, TyKind};
|
||||
use rustc_infer::infer::TyCtxtInferExt;
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::mir::ConstValue;
|
||||
|
|
@ -531,7 +531,7 @@ pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
|
|||
/// Returns `true` if the given type is an `unsafe` function.
|
||||
pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::FnDef(..) | ty::FnPtr(..) => ty.fn_sig(cx.tcx).safety() == Safety::Unsafe,
|
||||
ty::FnDef(..) | ty::FnPtr(..) => ty.fn_sig(cx.tcx).safety().is_unsafe(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
|
|||
use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr};
|
||||
use rustc_hir::{
|
||||
AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, LetExpr, Pat, QPath,
|
||||
Safety, Stmt, UnOp, UnsafeSource, StructTailExpr,
|
||||
Stmt, UnOp, UnsafeSource, StructTailExpr,
|
||||
};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
|
|
@ -426,15 +426,15 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
|
|||
.cx
|
||||
.typeck_results()
|
||||
.type_dependent_def_id(e.hir_id)
|
||||
.is_some_and(|id| self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe) =>
|
||||
.is_some_and(|id| self.cx.tcx.fn_sig(id).skip_binder().safety().is_unsafe()) =>
|
||||
{
|
||||
ControlFlow::Break(())
|
||||
},
|
||||
ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
|
||||
ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe => {
|
||||
ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety().is_unsafe() => {
|
||||
ControlFlow::Break(())
|
||||
},
|
||||
ty::FnPtr(_, hdr) if hdr.safety == Safety::Unsafe => ControlFlow::Break(()),
|
||||
ty::FnPtr(_, hdr) if hdr.safety.is_unsafe() => ControlFlow::Break(()),
|
||||
_ => walk_expr(self, e),
|
||||
},
|
||||
ExprKind::Path(ref p)
|
||||
|
|
@ -458,7 +458,7 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
|
|||
}
|
||||
fn visit_nested_item(&mut self, id: ItemId) -> Self::Result {
|
||||
if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind
|
||||
&& i.safety == Safety::Unsafe
|
||||
&& i.safety.is_unsafe()
|
||||
{
|
||||
ControlFlow::Break(())
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -325,6 +325,12 @@ impl Rustc {
|
|||
self
|
||||
}
|
||||
|
||||
/// Pass the `--verbose` flag.
|
||||
pub fn verbose(&mut self) -> &mut Self {
|
||||
self.cmd.arg("--verbose");
|
||||
self
|
||||
}
|
||||
|
||||
/// `EXTRARSCXXFLAGS`
|
||||
pub fn extra_rs_cxx_flags(&mut self) -> &mut Self {
|
||||
// Adapted from tools.mk (trimmed):
|
||||
|
|
|
|||
|
|
@ -15,8 +15,9 @@ fn main() {
|
|||
.link_args("b c")
|
||||
.link_args("d e")
|
||||
.link_arg("f")
|
||||
.arg("--print=link-args")
|
||||
.run_fail()
|
||||
.assert_stderr_contains(r#""a" "b" "c" "d" "e" "f""#);
|
||||
.assert_stdout_contains(r#""a" "b" "c" "d" "e" "f""#);
|
||||
rustc()
|
||||
.input("empty.rs")
|
||||
.linker_flavor(linker)
|
||||
|
|
@ -24,6 +25,7 @@ fn main() {
|
|||
.arg("-Zpre-link-args=b c")
|
||||
.arg("-Zpre-link-args=d e")
|
||||
.arg("-Zpre-link-arg=f")
|
||||
.arg("--print=link-args")
|
||||
.run_fail()
|
||||
.assert_stderr_contains(r#""a" "b" "c" "d" "e" "f""#);
|
||||
.assert_stdout_contains(r#""a" "b" "c" "d" "e" "f""#);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,13 +14,13 @@ fn main() {
|
|||
rustc().input("depb.rs").run();
|
||||
rustc().input("depc.rs").run();
|
||||
|
||||
let output = rustc().input("empty.rs").cfg("bar").run_fail();
|
||||
output.assert_stderr_contains(needle_from_libs(&["testa", "testb", "testa"]));
|
||||
let output = rustc().input("empty.rs").cfg("bar").arg("--print=link-args").run_fail();
|
||||
output.assert_stdout_contains(needle_from_libs(&["testa", "testb", "testa"]));
|
||||
|
||||
let output = rustc().input("empty.rs").run_fail();
|
||||
output.assert_stderr_contains(needle_from_libs(&["testa"]));
|
||||
output.assert_stderr_not_contains(needle_from_libs(&["testb"]));
|
||||
output.assert_stderr_not_contains(needle_from_libs(&["testa", "testa", "testa"]));
|
||||
let output = rustc().input("empty.rs").arg("--print=link-args").run_fail();
|
||||
output.assert_stdout_contains(needle_from_libs(&["testa"]));
|
||||
output.assert_stdout_not_contains(needle_from_libs(&["testb"]));
|
||||
output.assert_stdout_not_contains(needle_from_libs(&["testa", "testa", "testa"]));
|
||||
// Adjacent identical native libraries are no longer deduplicated if
|
||||
// they come from different crates (https://github.com/rust-lang/rust/pull/103311)
|
||||
// so the following will fail:
|
||||
|
|
|
|||
13
tests/run-make/linker-warning/fake-linker.rs
Normal file
13
tests/run-make/linker-warning/fake-linker.rs
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
fn main() {
|
||||
for arg in std::env::args() {
|
||||
match &*arg {
|
||||
"run_make_info" => println!("foo"),
|
||||
"run_make_warn" => eprintln!("warning: bar"),
|
||||
"run_make_error" => {
|
||||
eprintln!("error: baz");
|
||||
std::process::exit(1);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
1
tests/run-make/linker-warning/main.rs
Normal file
1
tests/run-make/linker-warning/main.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
fn main() {}
|
||||
28
tests/run-make/linker-warning/rmake.rs
Normal file
28
tests/run-make/linker-warning/rmake.rs
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
use run_make_support::{Rustc, rustc};
|
||||
|
||||
fn run_rustc() -> Rustc {
|
||||
let mut rustc = rustc();
|
||||
rustc.arg("main.rs").output("main").linker("./fake-linker");
|
||||
rustc
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// first, compile our linker
|
||||
rustc().arg("fake-linker.rs").output("fake-linker").run();
|
||||
|
||||
// Make sure we don't show the linker args unless `--verbose` is passed
|
||||
run_rustc()
|
||||
.link_arg("run_make_error")
|
||||
.verbose()
|
||||
.run_fail()
|
||||
.assert_stderr_contains_regex("fake-linker.*run_make_error")
|
||||
.assert_stderr_not_contains("object files omitted")
|
||||
.assert_stderr_contains_regex(r"lib(/|\\\\)libstd");
|
||||
run_rustc()
|
||||
.link_arg("run_make_error")
|
||||
.run_fail()
|
||||
.assert_stderr_contains("fake-linker")
|
||||
.assert_stderr_contains("object files omitted")
|
||||
.assert_stderr_contains_regex(r"\{")
|
||||
.assert_stderr_not_contains_regex(r"lib(/|\\\\)libstd");
|
||||
}
|
||||
10
tests/ui/impl-trait/in-bindings/dont-make-def-id.rs
Normal file
10
tests/ui/impl-trait/in-bindings/dont-make-def-id.rs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
//@ check-pass
|
||||
|
||||
// Make sure we don't create an opaque def id for ITIB.
|
||||
|
||||
#![crate_type = "lib"]
|
||||
#![feature(impl_trait_in_bindings)]
|
||||
|
||||
fn foo() {
|
||||
let _: impl Sized = 0;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue