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:
bors 2024-12-15 11:08:21 +00:00
commit 0894fb0fbf
56 changed files with 1030 additions and 471 deletions

View file

@ -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,

View 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);
}

View file

@ -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;

View file

@ -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,

View file

@ -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)));
}
_ => {}
}
}
}

View file

@ -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);
}
}

View file

@ -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

View file

@ -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

View file

@ -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.

View file

@ -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
);

View file

@ -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()
}
}

View file

@ -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))

View file

@ -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));

View file

@ -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),

View file

@ -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 }))
}

View file

@ -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())
}
}

View file

@ -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
}

View file

@ -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();

View file

@ -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 => {

View file

@ -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),
}
}

View file

@ -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.

View file

@ -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 {

View file

@ -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);
}

View file

@ -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"]

View file

@ -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")]

View file

@ -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, []);
/// }
/// ```

View file

@ -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
///

View file

@ -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!());

View file

@ -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))),
}
}

View file

@ -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
///

View file

@ -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!());

View file

@ -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

View file

@ -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()
}
}

View file

@ -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)

View file

@ -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)))

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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));
}
}

View file

@ -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;
}

View file

@ -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,

View file

@ -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);

View file

@ -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")

View file

@ -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,
}
}

View file

@ -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 {

View file

@ -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):

View file

@ -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""#);
}

View file

@ -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:

View 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);
}
_ => (),
}
}
}

View file

@ -0,0 +1 @@
fn main() {}

View 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");
}

View 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;
}