Auto merge of #135682 - matthiaskrgr:rollup-cl7zlt1, r=matthiaskrgr
Rollup of 7 pull requests Successful merges: - #133700 (const-eval: detect more pointers as definitely not-null) - #135290 (Encode constraints that hold at all points as logical edges in location-sensitive polonius) - #135478 (Run clippy for rustc_codegen_gcc on CI) - #135583 (Move `std::pipe::*` into `std::io`) - #135612 (Include x scripts in tarballs) - #135624 (ci: mirror buildkit image to ghcr) - #135661 (Stabilize `float_next_up_down`) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
0493557083
25 changed files with 444 additions and 377 deletions
23
.github/workflows/ghcr.yml
vendored
23
.github/workflows/ghcr.yml
vendored
|
@ -48,10 +48,21 @@ jobs:
|
||||||
|
|
||||||
- name: Mirror DockerHub
|
- name: Mirror DockerHub
|
||||||
run: |
|
run: |
|
||||||
# DockerHub image we want to mirror
|
# List of DockerHub images to mirror to ghcr.io
|
||||||
image="ubuntu:22.04"
|
images=(
|
||||||
|
# Mirrored because used by the mingw-check-tidy, which doesn't cache Docker images
|
||||||
|
"ubuntu:22.04"
|
||||||
|
# Mirrored because used by all linux CI jobs, including mingw-check-tidy
|
||||||
|
"moby/buildkit:buildx-stable-1"
|
||||||
|
)
|
||||||
|
|
||||||
# Mirror image from DockerHub to ghcr.io
|
# Mirror each image from DockerHub to ghcr.io
|
||||||
./crane copy \
|
for img in "${images[@]}"; do
|
||||||
docker.io/${image} \
|
echo "Mirroring ${img}..."
|
||||||
ghcr.io/${{ github.repository_owner }}/${image}
|
# Remove namespace from the image if any.
|
||||||
|
# E.g. "moby/buildkit:buildx-stable-1" becomes "buildkit:buildx-stable-1"
|
||||||
|
dest_image=$(echo "${img}" | cut -d'/' -f2-)
|
||||||
|
./crane copy \
|
||||||
|
"docker.io/${img}" \
|
||||||
|
"ghcr.io/${{ github.repository_owner }}/${dest_image}"
|
||||||
|
done
|
||||||
|
|
|
@ -9,16 +9,21 @@ use rustc_middle::ty::{RegionVid, TyCtxt};
|
||||||
use rustc_mir_dataflow::points::PointIndex;
|
use rustc_mir_dataflow::points::PointIndex;
|
||||||
|
|
||||||
use super::{LiveLoans, LocalizedOutlivesConstraintSet};
|
use super::{LiveLoans, LocalizedOutlivesConstraintSet};
|
||||||
|
use crate::constraints::OutlivesConstraint;
|
||||||
use crate::dataflow::BorrowIndex;
|
use crate::dataflow::BorrowIndex;
|
||||||
use crate::region_infer::values::LivenessValues;
|
use crate::region_infer::values::LivenessValues;
|
||||||
|
use crate::type_check::Locations;
|
||||||
use crate::{BorrowSet, PlaceConflictBias, places_conflict};
|
use crate::{BorrowSet, PlaceConflictBias, places_conflict};
|
||||||
|
|
||||||
/// With the full graph of constraints, we can compute loan reachability, stop at kills, and trace
|
/// Compute loan reachability, stop at kills, and trace loan liveness throughout the CFG, by
|
||||||
/// loan liveness throughout the CFG.
|
/// traversing the full graph of constraints that combines:
|
||||||
|
/// - the localized constraints (the physical edges),
|
||||||
|
/// - with the constraints that hold at all points (the logical edges).
|
||||||
pub(super) fn compute_loan_liveness<'tcx>(
|
pub(super) fn compute_loan_liveness<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
body: &Body<'tcx>,
|
body: &Body<'tcx>,
|
||||||
liveness: &LivenessValues,
|
liveness: &LivenessValues,
|
||||||
|
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
|
||||||
borrow_set: &BorrowSet<'tcx>,
|
borrow_set: &BorrowSet<'tcx>,
|
||||||
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
|
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
|
||||||
) -> LiveLoans {
|
) -> LiveLoans {
|
||||||
|
@ -29,7 +34,11 @@ pub(super) fn compute_loan_liveness<'tcx>(
|
||||||
// edges when visualizing the constraint graph anyways.
|
// edges when visualizing the constraint graph anyways.
|
||||||
let kills = collect_kills(body, tcx, borrow_set);
|
let kills = collect_kills(body, tcx, borrow_set);
|
||||||
|
|
||||||
let graph = index_constraints(&localized_outlives_constraints);
|
// Create the full graph with the physical edges we've localized earlier, and the logical edges
|
||||||
|
// of constraints that hold at all points.
|
||||||
|
let logical_constraints =
|
||||||
|
outlives_constraints.filter(|c| matches!(c.locations, Locations::All(_)));
|
||||||
|
let graph = LocalizedConstraintGraph::new(&localized_outlives_constraints, logical_constraints);
|
||||||
let mut visited = FxHashSet::default();
|
let mut visited = FxHashSet::default();
|
||||||
let mut stack = Vec::new();
|
let mut stack = Vec::new();
|
||||||
|
|
||||||
|
@ -108,7 +117,7 @@ pub(super) fn compute_loan_liveness<'tcx>(
|
||||||
let is_loan_killed =
|
let is_loan_killed =
|
||||||
kills.get(¤t_location).is_some_and(|kills| kills.contains(&loan_idx));
|
kills.get(¤t_location).is_some_and(|kills| kills.contains(&loan_idx));
|
||||||
|
|
||||||
for succ in outgoing_edges(&graph, node) {
|
for succ in graph.outgoing_edges(node) {
|
||||||
// If the loan is killed at this point, it is killed _on exit_. But only during
|
// If the loan is killed at this point, it is killed _on exit_. But only during
|
||||||
// forward traversal.
|
// forward traversal.
|
||||||
if is_loan_killed {
|
if is_loan_killed {
|
||||||
|
@ -125,9 +134,17 @@ pub(super) fn compute_loan_liveness<'tcx>(
|
||||||
live_loans
|
live_loans
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The localized constraint graph is currently the per-node map of its physical edges. In the
|
/// The localized constraint graph indexes the physical and logical edges to compute a given node's
|
||||||
/// future, we'll add logical edges to model constraints that hold at all points in the CFG.
|
/// successors during traversal.
|
||||||
type LocalizedConstraintGraph = FxHashMap<LocalizedNode, FxIndexSet<LocalizedNode>>;
|
struct LocalizedConstraintGraph {
|
||||||
|
/// The actual, physical, edges we have recorded for a given node.
|
||||||
|
edges: FxHashMap<LocalizedNode, FxIndexSet<LocalizedNode>>,
|
||||||
|
|
||||||
|
/// The logical edges representing the outlives constraints that hold at all points in the CFG,
|
||||||
|
/// which we don't localize to avoid creating a lot of unnecessary edges in the graph. Some CFGs
|
||||||
|
/// can be big, and we don't need to create such a physical edge for every point in the CFG.
|
||||||
|
logical_edges: FxHashMap<RegionVid, FxIndexSet<RegionVid>>,
|
||||||
|
}
|
||||||
|
|
||||||
/// A node in the graph to be traversed, one of the two vertices of a localized outlives constraint.
|
/// A node in the graph to be traversed, one of the two vertices of a localized outlives constraint.
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||||
|
@ -136,24 +153,44 @@ struct LocalizedNode {
|
||||||
point: PointIndex,
|
point: PointIndex,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Traverses the constraints and returns the indexable graph of edges per node.
|
impl LocalizedConstraintGraph {
|
||||||
fn index_constraints(constraints: &LocalizedOutlivesConstraintSet) -> LocalizedConstraintGraph {
|
/// Traverses the constraints and returns the indexed graph of edges per node.
|
||||||
let mut edges = LocalizedConstraintGraph::default();
|
fn new<'tcx>(
|
||||||
for constraint in &constraints.outlives {
|
constraints: &LocalizedOutlivesConstraintSet,
|
||||||
let source = LocalizedNode { region: constraint.source, point: constraint.from };
|
logical_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
|
||||||
let target = LocalizedNode { region: constraint.target, point: constraint.to };
|
) -> Self {
|
||||||
edges.entry(source).or_default().insert(target);
|
let mut edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
|
||||||
|
for constraint in &constraints.outlives {
|
||||||
|
let source = LocalizedNode { region: constraint.source, point: constraint.from };
|
||||||
|
let target = LocalizedNode { region: constraint.target, point: constraint.to };
|
||||||
|
edges.entry(source).or_default().insert(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut logical_edges: FxHashMap<_, FxIndexSet<_>> = FxHashMap::default();
|
||||||
|
for constraint in logical_constraints {
|
||||||
|
logical_edges.entry(constraint.sup).or_default().insert(constraint.sub);
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalizedConstraintGraph { edges, logical_edges }
|
||||||
}
|
}
|
||||||
|
|
||||||
edges
|
/// Returns the outgoing edges of a given node, not its transitive closure.
|
||||||
}
|
fn outgoing_edges(&self, node: LocalizedNode) -> impl Iterator<Item = LocalizedNode> + use<'_> {
|
||||||
|
// The outgoing edges are:
|
||||||
/// Returns the outgoing edges of a given node, not its transitive closure.
|
// - the physical edges present at this node,
|
||||||
fn outgoing_edges(
|
// - the materialized logical edges that exist virtually at all points for this node's
|
||||||
graph: &LocalizedConstraintGraph,
|
// region, localized at this point.
|
||||||
node: LocalizedNode,
|
let physical_edges =
|
||||||
) -> impl Iterator<Item = LocalizedNode> + use<'_> {
|
self.edges.get(&node).into_iter().flat_map(|targets| targets.iter().copied());
|
||||||
graph.get(&node).into_iter().flat_map(|edges| edges.iter().copied())
|
let materialized_edges =
|
||||||
|
self.logical_edges.get(&node.region).into_iter().flat_map(move |targets| {
|
||||||
|
targets
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.map(move |target| LocalizedNode { point: node.point, region: target })
|
||||||
|
});
|
||||||
|
physical_edges.chain(materialized_edges)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Traverses the MIR and collects kills.
|
/// Traverses the MIR and collects kills.
|
||||||
|
|
|
@ -130,6 +130,7 @@ impl PoloniusContext {
|
||||||
tcx,
|
tcx,
|
||||||
body,
|
body,
|
||||||
regioncx.liveness_constraints(),
|
regioncx.liveness_constraints(),
|
||||||
|
regioncx.outlives_constraints(),
|
||||||
borrow_set,
|
borrow_set,
|
||||||
&localized_outlives_constraints,
|
&localized_outlives_constraints,
|
||||||
);
|
);
|
||||||
|
|
|
@ -22,23 +22,11 @@ pub(super) fn convert_typeck_constraints<'tcx>(
|
||||||
for outlives_constraint in outlives_constraints {
|
for outlives_constraint in outlives_constraints {
|
||||||
match outlives_constraint.locations {
|
match outlives_constraint.locations {
|
||||||
Locations::All(_) => {
|
Locations::All(_) => {
|
||||||
// For now, turn logical constraints holding at all points into physical edges at
|
// We don't turn constraints holding at all points into physical edges at every
|
||||||
// every point in the graph.
|
// point in the graph. They are encoded into *traversal* instead: a given node's
|
||||||
// FIXME: encode this into *traversal* instead.
|
// successors will combine these logical edges with the regular, physical, localized
|
||||||
for (block, bb) in body.basic_blocks.iter_enumerated() {
|
// edges.
|
||||||
let statement_count = bb.statements.len();
|
continue;
|
||||||
for statement_index in 0..=statement_count {
|
|
||||||
let current_location = Location { block, statement_index };
|
|
||||||
let current_point = liveness.point_from_location(current_location);
|
|
||||||
|
|
||||||
localized_outlives_constraints.push(LocalizedOutlivesConstraint {
|
|
||||||
source: outlives_constraint.sup,
|
|
||||||
from: current_point,
|
|
||||||
target: outlives_constraint.sub,
|
|
||||||
to: current_point,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Locations::Single(location) => {
|
Locations::Single(location) => {
|
||||||
|
|
|
@ -1481,22 +1481,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||||
/// Test if this value might be null.
|
/// Test if this value might be null.
|
||||||
/// If the machine does not support ptr-to-int casts, this is conservative.
|
/// If the machine does not support ptr-to-int casts, this is conservative.
|
||||||
pub fn scalar_may_be_null(&self, scalar: Scalar<M::Provenance>) -> InterpResult<'tcx, bool> {
|
pub fn scalar_may_be_null(&self, scalar: Scalar<M::Provenance>) -> InterpResult<'tcx, bool> {
|
||||||
interp_ok(match scalar.try_to_scalar_int() {
|
match scalar.try_to_scalar_int() {
|
||||||
Ok(int) => int.is_null(),
|
Ok(int) => interp_ok(int.is_null()),
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
// Can only happen during CTFE.
|
// We can't cast this pointer to an integer. Can only happen during CTFE.
|
||||||
let ptr = scalar.to_pointer(self)?;
|
let ptr = scalar.to_pointer(self)?;
|
||||||
match self.ptr_try_get_alloc_id(ptr, 0) {
|
match self.ptr_try_get_alloc_id(ptr, 0) {
|
||||||
Ok((alloc_id, offset, _)) => {
|
Ok((alloc_id, offset, _)) => {
|
||||||
let size = self.get_alloc_info(alloc_id).size;
|
let info = self.get_alloc_info(alloc_id);
|
||||||
// If the pointer is out-of-bounds, it may be null.
|
// If the pointer is in-bounds (including "at the end"), it is definitely not null.
|
||||||
// Note that one-past-the-end (offset == size) is still inbounds, and never null.
|
if offset <= info.size {
|
||||||
offset > size
|
return interp_ok(false);
|
||||||
|
}
|
||||||
|
// If the allocation is N-aligned, and the offset is not divisible by N,
|
||||||
|
// then `base + offset` has a non-zero remainder after division by `N`,
|
||||||
|
// which means `base + offset` cannot be null.
|
||||||
|
if offset.bytes() % info.align.bytes() != 0 {
|
||||||
|
return interp_ok(false);
|
||||||
|
}
|
||||||
|
// We don't know enough, this might be null.
|
||||||
|
interp_ok(true)
|
||||||
}
|
}
|
||||||
Err(_offset) => bug!("a non-int scalar is always a pointer"),
|
Err(_offset) => bug!("a non-int scalar is always a pointer"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Turning a "maybe pointer" into a proper pointer (and some information
|
/// Turning a "maybe pointer" into a proper pointer (and some information
|
||||||
|
|
|
@ -504,7 +504,6 @@ impl f128 {
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// #![feature(f128)]
|
/// #![feature(f128)]
|
||||||
/// #![feature(float_next_up_down)]
|
|
||||||
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
||||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||||
///
|
///
|
||||||
|
@ -516,13 +515,15 @@ impl f128 {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// This operation corresponds to IEEE-754 `nextUp`.
|
||||||
|
///
|
||||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||||
/// [`INFINITY`]: Self::INFINITY
|
/// [`INFINITY`]: Self::INFINITY
|
||||||
/// [`MIN`]: Self::MIN
|
/// [`MIN`]: Self::MIN
|
||||||
/// [`MAX`]: Self::MAX
|
/// [`MAX`]: Self::MAX
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[doc(alias = "nextUp")]
|
||||||
#[unstable(feature = "f128", issue = "116909")]
|
#[unstable(feature = "f128", issue = "116909")]
|
||||||
// #[unstable(feature = "float_next_up_down", issue = "91399")]
|
|
||||||
pub const fn next_up(self) -> Self {
|
pub const fn next_up(self) -> Self {
|
||||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||||
// denormals to zero. This is in general unsound and unsupported, but here
|
// denormals to zero. This is in general unsound and unsupported, but here
|
||||||
|
@ -558,7 +559,6 @@ impl f128 {
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// #![feature(f128)]
|
/// #![feature(f128)]
|
||||||
/// #![feature(float_next_up_down)]
|
|
||||||
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
||||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||||
///
|
///
|
||||||
|
@ -570,13 +570,15 @@ impl f128 {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// This operation corresponds to IEEE-754 `nextDown`.
|
||||||
|
///
|
||||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||||
/// [`INFINITY`]: Self::INFINITY
|
/// [`INFINITY`]: Self::INFINITY
|
||||||
/// [`MIN`]: Self::MIN
|
/// [`MIN`]: Self::MIN
|
||||||
/// [`MAX`]: Self::MAX
|
/// [`MAX`]: Self::MAX
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[doc(alias = "nextDown")]
|
||||||
#[unstable(feature = "f128", issue = "116909")]
|
#[unstable(feature = "f128", issue = "116909")]
|
||||||
// #[unstable(feature = "float_next_up_down", issue = "91399")]
|
|
||||||
pub const fn next_down(self) -> Self {
|
pub const fn next_down(self) -> Self {
|
||||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||||
// denormals to zero. This is in general unsound and unsupported, but here
|
// denormals to zero. This is in general unsound and unsupported, but here
|
||||||
|
|
|
@ -497,7 +497,6 @@ impl f16 {
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// #![feature(f16)]
|
/// #![feature(f16)]
|
||||||
/// #![feature(float_next_up_down)]
|
|
||||||
/// # // FIXME(f16_f128): ABI issues on MSVC
|
/// # // FIXME(f16_f128): ABI issues on MSVC
|
||||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||||
///
|
///
|
||||||
|
@ -509,13 +508,15 @@ impl f16 {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// This operation corresponds to IEEE-754 `nextUp`.
|
||||||
|
///
|
||||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||||
/// [`INFINITY`]: Self::INFINITY
|
/// [`INFINITY`]: Self::INFINITY
|
||||||
/// [`MIN`]: Self::MIN
|
/// [`MIN`]: Self::MIN
|
||||||
/// [`MAX`]: Self::MAX
|
/// [`MAX`]: Self::MAX
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[doc(alias = "nextUp")]
|
||||||
#[unstable(feature = "f16", issue = "116909")]
|
#[unstable(feature = "f16", issue = "116909")]
|
||||||
// #[unstable(feature = "float_next_up_down", issue = "91399")]
|
|
||||||
pub const fn next_up(self) -> Self {
|
pub const fn next_up(self) -> Self {
|
||||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||||
// denormals to zero. This is in general unsound and unsupported, but here
|
// denormals to zero. This is in general unsound and unsupported, but here
|
||||||
|
@ -551,7 +552,6 @@ impl f16 {
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// #![feature(f16)]
|
/// #![feature(f16)]
|
||||||
/// #![feature(float_next_up_down)]
|
|
||||||
/// # // FIXME(f16_f128): ABI issues on MSVC
|
/// # // FIXME(f16_f128): ABI issues on MSVC
|
||||||
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
/// # #[cfg(all(target_arch = "x86_64", target_os = "linux"))] {
|
||||||
///
|
///
|
||||||
|
@ -563,13 +563,15 @@ impl f16 {
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// This operation corresponds to IEEE-754 `nextDown`.
|
||||||
|
///
|
||||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||||
/// [`INFINITY`]: Self::INFINITY
|
/// [`INFINITY`]: Self::INFINITY
|
||||||
/// [`MIN`]: Self::MIN
|
/// [`MIN`]: Self::MIN
|
||||||
/// [`MAX`]: Self::MAX
|
/// [`MAX`]: Self::MAX
|
||||||
#[inline]
|
#[inline]
|
||||||
|
#[doc(alias = "nextDown")]
|
||||||
#[unstable(feature = "f16", issue = "116909")]
|
#[unstable(feature = "f16", issue = "116909")]
|
||||||
// #[unstable(feature = "float_next_up_down", issue = "91399")]
|
|
||||||
pub const fn next_down(self) -> Self {
|
pub const fn next_down(self) -> Self {
|
||||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||||
// denormals to zero. This is in general unsound and unsupported, but here
|
// denormals to zero. This is in general unsound and unsupported, but here
|
||||||
|
|
|
@ -726,7 +726,6 @@ impl f32 {
|
||||||
/// is finite `x == x.next_up().next_down()` also holds.
|
/// is finite `x == x.next_up().next_down()` also holds.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// #![feature(float_next_up_down)]
|
|
||||||
/// // f32::EPSILON is the difference between 1.0 and the next number up.
|
/// // f32::EPSILON is the difference between 1.0 and the next number up.
|
||||||
/// assert_eq!(1.0f32.next_up(), 1.0 + f32::EPSILON);
|
/// assert_eq!(1.0f32.next_up(), 1.0 + f32::EPSILON);
|
||||||
/// // But not for most numbers.
|
/// // But not for most numbers.
|
||||||
|
@ -734,12 +733,16 @@ impl f32 {
|
||||||
/// assert_eq!(16777216f32.next_up(), 16777218.0);
|
/// assert_eq!(16777216f32.next_up(), 16777218.0);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// This operation corresponds to IEEE-754 `nextUp`.
|
||||||
|
///
|
||||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||||
/// [`INFINITY`]: Self::INFINITY
|
/// [`INFINITY`]: Self::INFINITY
|
||||||
/// [`MIN`]: Self::MIN
|
/// [`MIN`]: Self::MIN
|
||||||
/// [`MAX`]: Self::MAX
|
/// [`MAX`]: Self::MAX
|
||||||
#[inline]
|
#[inline]
|
||||||
#[unstable(feature = "float_next_up_down", issue = "91399")]
|
#[doc(alias = "nextUp")]
|
||||||
|
#[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
|
||||||
|
#[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
|
||||||
pub const fn next_up(self) -> Self {
|
pub const fn next_up(self) -> Self {
|
||||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||||
// denormals to zero. This is in general unsound and unsupported, but here
|
// denormals to zero. This is in general unsound and unsupported, but here
|
||||||
|
@ -774,7 +777,6 @@ impl f32 {
|
||||||
/// is finite `x == x.next_down().next_up()` also holds.
|
/// is finite `x == x.next_down().next_up()` also holds.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// #![feature(float_next_up_down)]
|
|
||||||
/// let x = 1.0f32;
|
/// let x = 1.0f32;
|
||||||
/// // Clamp value into range [0, 1).
|
/// // Clamp value into range [0, 1).
|
||||||
/// let clamped = x.clamp(0.0, 1.0f32.next_down());
|
/// let clamped = x.clamp(0.0, 1.0f32.next_down());
|
||||||
|
@ -782,12 +784,16 @@ impl f32 {
|
||||||
/// assert_eq!(clamped.next_up(), 1.0);
|
/// assert_eq!(clamped.next_up(), 1.0);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// This operation corresponds to IEEE-754 `nextDown`.
|
||||||
|
///
|
||||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||||
/// [`INFINITY`]: Self::INFINITY
|
/// [`INFINITY`]: Self::INFINITY
|
||||||
/// [`MIN`]: Self::MIN
|
/// [`MIN`]: Self::MIN
|
||||||
/// [`MAX`]: Self::MAX
|
/// [`MAX`]: Self::MAX
|
||||||
#[inline]
|
#[inline]
|
||||||
#[unstable(feature = "float_next_up_down", issue = "91399")]
|
#[doc(alias = "nextDown")]
|
||||||
|
#[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
|
||||||
|
#[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
|
||||||
pub const fn next_down(self) -> Self {
|
pub const fn next_down(self) -> Self {
|
||||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||||
// denormals to zero. This is in general unsound and unsupported, but here
|
// denormals to zero. This is in general unsound and unsupported, but here
|
||||||
|
|
|
@ -743,7 +743,6 @@ impl f64 {
|
||||||
/// is finite `x == x.next_up().next_down()` also holds.
|
/// is finite `x == x.next_up().next_down()` also holds.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// #![feature(float_next_up_down)]
|
|
||||||
/// // f64::EPSILON is the difference between 1.0 and the next number up.
|
/// // f64::EPSILON is the difference between 1.0 and the next number up.
|
||||||
/// assert_eq!(1.0f64.next_up(), 1.0 + f64::EPSILON);
|
/// assert_eq!(1.0f64.next_up(), 1.0 + f64::EPSILON);
|
||||||
/// // But not for most numbers.
|
/// // But not for most numbers.
|
||||||
|
@ -751,12 +750,16 @@ impl f64 {
|
||||||
/// assert_eq!(9007199254740992f64.next_up(), 9007199254740994.0);
|
/// assert_eq!(9007199254740992f64.next_up(), 9007199254740994.0);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// This operation corresponds to IEEE-754 `nextUp`.
|
||||||
|
///
|
||||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||||
/// [`INFINITY`]: Self::INFINITY
|
/// [`INFINITY`]: Self::INFINITY
|
||||||
/// [`MIN`]: Self::MIN
|
/// [`MIN`]: Self::MIN
|
||||||
/// [`MAX`]: Self::MAX
|
/// [`MAX`]: Self::MAX
|
||||||
#[inline]
|
#[inline]
|
||||||
#[unstable(feature = "float_next_up_down", issue = "91399")]
|
#[doc(alias = "nextUp")]
|
||||||
|
#[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
|
||||||
|
#[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
|
||||||
pub const fn next_up(self) -> Self {
|
pub const fn next_up(self) -> Self {
|
||||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||||
// denormals to zero. This is in general unsound and unsupported, but here
|
// denormals to zero. This is in general unsound and unsupported, but here
|
||||||
|
@ -791,7 +794,6 @@ impl f64 {
|
||||||
/// is finite `x == x.next_down().next_up()` also holds.
|
/// is finite `x == x.next_down().next_up()` also holds.
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// #![feature(float_next_up_down)]
|
|
||||||
/// let x = 1.0f64;
|
/// let x = 1.0f64;
|
||||||
/// // Clamp value into range [0, 1).
|
/// // Clamp value into range [0, 1).
|
||||||
/// let clamped = x.clamp(0.0, 1.0f64.next_down());
|
/// let clamped = x.clamp(0.0, 1.0f64.next_down());
|
||||||
|
@ -799,12 +801,16 @@ impl f64 {
|
||||||
/// assert_eq!(clamped.next_up(), 1.0);
|
/// assert_eq!(clamped.next_up(), 1.0);
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
|
/// This operation corresponds to IEEE-754 `nextDown`.
|
||||||
|
///
|
||||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||||
/// [`INFINITY`]: Self::INFINITY
|
/// [`INFINITY`]: Self::INFINITY
|
||||||
/// [`MIN`]: Self::MIN
|
/// [`MIN`]: Self::MIN
|
||||||
/// [`MAX`]: Self::MAX
|
/// [`MAX`]: Self::MAX
|
||||||
#[inline]
|
#[inline]
|
||||||
#[unstable(feature = "float_next_up_down", issue = "91399")]
|
#[doc(alias = "nextDown")]
|
||||||
|
#[stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
|
||||||
|
#[rustc_const_stable(feature = "float_next_up_down", since = "CURRENT_RUSTC_VERSION")]
|
||||||
pub const fn next_down(self) -> Self {
|
pub const fn next_down(self) -> Self {
|
||||||
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
// Some targets violate Rust's assumption of IEEE semantics, e.g. by flushing
|
||||||
// denormals to zero. This is in general unsound and unsupported, but here
|
// denormals to zero. This is in general unsound and unsupported, but here
|
||||||
|
|
|
@ -330,6 +330,7 @@ pub use self::{
|
||||||
};
|
};
|
||||||
use crate::mem::take;
|
use crate::mem::take;
|
||||||
use crate::ops::{Deref, DerefMut};
|
use crate::ops::{Deref, DerefMut};
|
||||||
|
use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner};
|
||||||
use crate::{cmp, fmt, slice, str, sys};
|
use crate::{cmp, fmt, slice, str, sys};
|
||||||
|
|
||||||
mod buffered;
|
mod buffered;
|
||||||
|
@ -3250,3 +3251,251 @@ impl<B: BufRead> Iterator for Lines<B> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create anonymous pipe that is close-on-exec and blocking.
|
||||||
|
///
|
||||||
|
/// # Behavior
|
||||||
|
///
|
||||||
|
/// A pipe is a synchronous, unidirectional data channel between two or more processes, like an
|
||||||
|
/// interprocess [`mpsc`](crate::sync::mpsc) provided by the OS. In particular:
|
||||||
|
///
|
||||||
|
/// * A read on a [`PipeReader`] blocks until the pipe is non-empty.
|
||||||
|
/// * A write on a [`PipeWriter`] blocks when the pipe is full.
|
||||||
|
/// * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`]
|
||||||
|
/// returns EOF.
|
||||||
|
/// * [`PipeReader`] can be shared, but only one process will consume the data in the pipe.
|
||||||
|
///
|
||||||
|
/// # Capacity
|
||||||
|
///
|
||||||
|
/// Pipe capacity is platform dependent. To quote the Linux [man page]:
|
||||||
|
///
|
||||||
|
/// > Different implementations have different limits for the pipe capacity. Applications should
|
||||||
|
/// > not rely on a particular capacity: an application should be designed so that a reading process
|
||||||
|
/// > consumes data as soon as it is available, so that a writing process does not remain blocked.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// #![feature(anonymous_pipe)]
|
||||||
|
/// # #[cfg(miri)] fn main() {}
|
||||||
|
/// # #[cfg(not(miri))]
|
||||||
|
/// # fn main() -> std::io::Result<()> {
|
||||||
|
/// # use std::process::Command;
|
||||||
|
/// # use std::io::{Read, Write};
|
||||||
|
/// let (ping_rx, mut ping_tx) = std::io::pipe()?;
|
||||||
|
/// let (mut pong_rx, pong_tx) = std::io::pipe()?;
|
||||||
|
///
|
||||||
|
/// // Spawn a process that echoes its input.
|
||||||
|
/// let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?;
|
||||||
|
///
|
||||||
|
/// ping_tx.write_all(b"hello")?;
|
||||||
|
/// // Close to unblock echo_server's reader.
|
||||||
|
/// drop(ping_tx);
|
||||||
|
///
|
||||||
|
/// let mut buf = String::new();
|
||||||
|
/// // Block until echo_server's writer is closed.
|
||||||
|
/// pong_rx.read_to_string(&mut buf)?;
|
||||||
|
/// assert_eq!(&buf, "hello");
|
||||||
|
///
|
||||||
|
/// echo_server.wait()?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
/// [pipe]: https://man7.org/linux/man-pages/man2/pipe.2.html
|
||||||
|
/// [CreatePipe]: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe
|
||||||
|
/// [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html
|
||||||
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
|
#[inline]
|
||||||
|
pub fn pipe() -> Result<(PipeReader, PipeWriter)> {
|
||||||
|
pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Read end of the anonymous pipe.
|
||||||
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PipeReader(pub(crate) AnonPipe);
|
||||||
|
|
||||||
|
/// Write end of the anonymous pipe.
|
||||||
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PipeWriter(pub(crate) AnonPipe);
|
||||||
|
|
||||||
|
impl PipeReader {
|
||||||
|
/// Create a new [`PipeReader`] instance that shares the same underlying file description.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// #![feature(anonymous_pipe)]
|
||||||
|
/// # #[cfg(miri)] fn main() {}
|
||||||
|
/// # #[cfg(not(miri))]
|
||||||
|
/// # fn main() -> std::io::Result<()> {
|
||||||
|
/// # use std::fs;
|
||||||
|
/// # use std::io::Write;
|
||||||
|
/// # use std::process::Command;
|
||||||
|
/// const NUM_SLOT: u8 = 2;
|
||||||
|
/// const NUM_PROC: u8 = 5;
|
||||||
|
/// const OUTPUT: &str = "work.txt";
|
||||||
|
///
|
||||||
|
/// let mut jobs = vec![];
|
||||||
|
/// let (reader, mut writer) = std::io::pipe()?;
|
||||||
|
///
|
||||||
|
/// // Write NUM_SLOT characters the pipe.
|
||||||
|
/// writer.write_all(&[b'|'; NUM_SLOT as usize])?;
|
||||||
|
///
|
||||||
|
/// // Spawn several processes that read a character from the pipe, do some work, then
|
||||||
|
/// // write back to the pipe. When the pipe is empty, the processes block, so only
|
||||||
|
/// // NUM_SLOT processes can be working at any given time.
|
||||||
|
/// for _ in 0..NUM_PROC {
|
||||||
|
/// jobs.push(
|
||||||
|
/// Command::new("bash")
|
||||||
|
/// .args(["-c",
|
||||||
|
/// &format!(
|
||||||
|
/// "read -n 1\n\
|
||||||
|
/// echo -n 'x' >> '{OUTPUT}'\n\
|
||||||
|
/// echo -n '|'",
|
||||||
|
/// ),
|
||||||
|
/// ])
|
||||||
|
/// .stdin(reader.try_clone()?)
|
||||||
|
/// .stdout(writer.try_clone()?)
|
||||||
|
/// .spawn()?,
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Wait for all jobs to finish.
|
||||||
|
/// for mut job in jobs {
|
||||||
|
/// job.wait()?;
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// // Check our work and clean up.
|
||||||
|
/// let xs = fs::read_to_string(OUTPUT)?;
|
||||||
|
/// fs::remove_file(OUTPUT)?;
|
||||||
|
/// assert_eq!(xs, "x".repeat(NUM_PROC.into()));
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
|
pub fn try_clone(&self) -> Result<Self> {
|
||||||
|
self.0.try_clone().map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PipeWriter {
|
||||||
|
/// Create a new [`PipeWriter`] instance that shares the same underlying file description.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
///
|
||||||
|
/// ```no_run
|
||||||
|
/// #![feature(anonymous_pipe)]
|
||||||
|
/// # #[cfg(miri)] fn main() {}
|
||||||
|
/// # #[cfg(not(miri))]
|
||||||
|
/// # fn main() -> std::io::Result<()> {
|
||||||
|
/// # use std::process::Command;
|
||||||
|
/// # use std::io::Read;
|
||||||
|
/// let (mut reader, writer) = std::io::pipe()?;
|
||||||
|
///
|
||||||
|
/// // Spawn a process that writes to stdout and stderr.
|
||||||
|
/// let mut peer = Command::new("bash")
|
||||||
|
/// .args([
|
||||||
|
/// "-c",
|
||||||
|
/// "echo -n foo\n\
|
||||||
|
/// echo -n bar >&2"
|
||||||
|
/// ])
|
||||||
|
/// .stdout(writer.try_clone()?)
|
||||||
|
/// .stderr(writer)
|
||||||
|
/// .spawn()?;
|
||||||
|
///
|
||||||
|
/// // Read and check the result.
|
||||||
|
/// let mut msg = String::new();
|
||||||
|
/// reader.read_to_string(&mut msg)?;
|
||||||
|
/// assert_eq!(&msg, "foobar");
|
||||||
|
///
|
||||||
|
/// peer.wait()?;
|
||||||
|
/// # Ok(())
|
||||||
|
/// # }
|
||||||
|
/// ```
|
||||||
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
|
pub fn try_clone(&self) -> Result<Self> {
|
||||||
|
self.0.try_clone().map(Self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
|
impl Read for &PipeReader {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||||
|
self.0.read(buf)
|
||||||
|
}
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
|
||||||
|
self.0.read_vectored(bufs)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_read_vectored(&self) -> bool {
|
||||||
|
self.0.is_read_vectored()
|
||||||
|
}
|
||||||
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
|
||||||
|
self.0.read_to_end(buf)
|
||||||
|
}
|
||||||
|
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
|
||||||
|
self.0.read_buf(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
|
impl Read for PipeReader {
|
||||||
|
fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
|
||||||
|
self.0.read(buf)
|
||||||
|
}
|
||||||
|
fn read_vectored(&mut self, bufs: &mut [IoSliceMut<'_>]) -> Result<usize> {
|
||||||
|
self.0.read_vectored(bufs)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn is_read_vectored(&self) -> bool {
|
||||||
|
self.0.is_read_vectored()
|
||||||
|
}
|
||||||
|
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
|
||||||
|
self.0.read_to_end(buf)
|
||||||
|
}
|
||||||
|
fn read_buf(&mut self, buf: BorrowedCursor<'_>) -> Result<()> {
|
||||||
|
self.0.read_buf(buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
|
impl Write for &PipeWriter {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||||
|
self.0.write(buf)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
|
||||||
|
self.0.write_vectored(bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
self.0.is_write_vectored()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
|
impl Write for PipeWriter {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> Result<usize> {
|
||||||
|
self.0.write(buf)
|
||||||
|
}
|
||||||
|
#[inline]
|
||||||
|
fn flush(&mut self) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result<usize> {
|
||||||
|
self.0.write_vectored(bufs)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn is_write_vectored(&self) -> bool {
|
||||||
|
self.0.is_write_vectored()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -823,3 +823,20 @@ fn try_oom_error() {
|
||||||
let io_err = io::Error::from(reserve_err);
|
let io_err = io::Error::from(reserve_err);
|
||||||
assert_eq!(io::ErrorKind::OutOfMemory, io_err.kind());
|
assert_eq!(io::ErrorKind::OutOfMemory, io_err.kind());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(all(windows, unix, not(miri)))]
|
||||||
|
fn pipe_creation_clone_and_rw() {
|
||||||
|
let (rx, tx) = std::io::pipe().unwrap();
|
||||||
|
|
||||||
|
tx.try_clone().unwrap().write_all(b"12345").unwrap();
|
||||||
|
drop(tx);
|
||||||
|
|
||||||
|
let mut rx2 = rx.try_clone().unwrap();
|
||||||
|
drop(rx);
|
||||||
|
|
||||||
|
let mut s = String::new();
|
||||||
|
rx2.read_to_string(&mut s).unwrap();
|
||||||
|
drop(rx2);
|
||||||
|
assert_eq!(s, "12345");
|
||||||
|
}
|
||||||
|
|
|
@ -333,7 +333,6 @@
|
||||||
#![feature(extend_one)]
|
#![feature(extend_one)]
|
||||||
#![feature(float_gamma)]
|
#![feature(float_gamma)]
|
||||||
#![feature(float_minimum_maximum)]
|
#![feature(float_minimum_maximum)]
|
||||||
#![feature(float_next_up_down)]
|
|
||||||
#![feature(fmt_internals)]
|
#![feature(fmt_internals)]
|
||||||
#![feature(hasher_prefixfree_extras)]
|
#![feature(hasher_prefixfree_extras)]
|
||||||
#![feature(hashmap_internals)]
|
#![feature(hashmap_internals)]
|
||||||
|
@ -596,8 +595,6 @@ pub mod panic;
|
||||||
#[unstable(feature = "pattern_type_macro", issue = "123646")]
|
#[unstable(feature = "pattern_type_macro", issue = "123646")]
|
||||||
pub mod pat;
|
pub mod pat;
|
||||||
pub mod path;
|
pub mod path;
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
|
||||||
pub mod pipe;
|
|
||||||
pub mod process;
|
pub mod process;
|
||||||
#[unstable(feature = "random", issue = "130703")]
|
#[unstable(feature = "random", issue = "130703")]
|
||||||
pub mod random;
|
pub mod random;
|
||||||
|
|
|
@ -1,258 +0,0 @@
|
||||||
//! A cross-platform anonymous pipe.
|
|
||||||
//!
|
|
||||||
//! This module provides support for anonymous OS pipes, like [pipe] on Linux or [CreatePipe] on
|
|
||||||
//! Windows.
|
|
||||||
//!
|
|
||||||
//! # Behavior
|
|
||||||
//!
|
|
||||||
//! A pipe is a synchronous, unidirectional data channel between two or more processes, like an
|
|
||||||
//! interprocess [`mpsc`](crate::sync::mpsc) provided by the OS. In particular:
|
|
||||||
//!
|
|
||||||
//! * A read on a [`PipeReader`] blocks until the pipe is non-empty.
|
|
||||||
//! * A write on a [`PipeWriter`] blocks when the pipe is full.
|
|
||||||
//! * When all copies of a [`PipeWriter`] are closed, a read on the corresponding [`PipeReader`]
|
|
||||||
//! returns EOF.
|
|
||||||
//! * [`PipeReader`] can be shared, but only one process will consume the data in the pipe.
|
|
||||||
//!
|
|
||||||
//! # Capacity
|
|
||||||
//!
|
|
||||||
//! Pipe capacity is platform dependent. To quote the Linux [man page]:
|
|
||||||
//!
|
|
||||||
//! > Different implementations have different limits for the pipe capacity. Applications should
|
|
||||||
//! > not rely on a particular capacity: an application should be designed so that a reading process
|
|
||||||
//! > consumes data as soon as it is available, so that a writing process does not remain blocked.
|
|
||||||
//!
|
|
||||||
//! # Examples
|
|
||||||
//!
|
|
||||||
//! ```no_run
|
|
||||||
//! #![feature(anonymous_pipe)]
|
|
||||||
//! # #[cfg(miri)] fn main() {}
|
|
||||||
//! # #[cfg(not(miri))]
|
|
||||||
//! # fn main() -> std::io::Result<()> {
|
|
||||||
//! # use std::process::Command;
|
|
||||||
//! # use std::io::{Read, Write};
|
|
||||||
//! let (ping_rx, mut ping_tx) = std::pipe::pipe()?;
|
|
||||||
//! let (mut pong_rx, pong_tx) = std::pipe::pipe()?;
|
|
||||||
//!
|
|
||||||
//! // Spawn a process that echoes its input.
|
|
||||||
//! let mut echo_server = Command::new("cat").stdin(ping_rx).stdout(pong_tx).spawn()?;
|
|
||||||
//!
|
|
||||||
//! ping_tx.write_all(b"hello")?;
|
|
||||||
//! // Close to unblock echo_server's reader.
|
|
||||||
//! drop(ping_tx);
|
|
||||||
//!
|
|
||||||
//! let mut buf = String::new();
|
|
||||||
//! // Block until echo_server's writer is closed.
|
|
||||||
//! pong_rx.read_to_string(&mut buf)?;
|
|
||||||
//! assert_eq!(&buf, "hello");
|
|
||||||
//!
|
|
||||||
//! echo_server.wait()?;
|
|
||||||
//! # Ok(())
|
|
||||||
//! # }
|
|
||||||
//! ```
|
|
||||||
//! [pipe]: https://man7.org/linux/man-pages/man2/pipe.2.html
|
|
||||||
//! [CreatePipe]: https://learn.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-createpipe
|
|
||||||
//! [man page]: https://man7.org/linux/man-pages/man7/pipe.7.html
|
|
||||||
use crate::io;
|
|
||||||
use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner};
|
|
||||||
|
|
||||||
/// Create anonymous pipe that is close-on-exec and blocking.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// See the [module-level](crate::pipe) documentation for examples.
|
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
|
||||||
#[inline]
|
|
||||||
pub fn pipe() -> io::Result<(PipeReader, PipeWriter)> {
|
|
||||||
pipe_inner().map(|(reader, writer)| (PipeReader(reader), PipeWriter(writer)))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read end of the anonymous pipe.
|
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PipeReader(pub(crate) AnonPipe);
|
|
||||||
|
|
||||||
/// Write end of the anonymous pipe.
|
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PipeWriter(pub(crate) AnonPipe);
|
|
||||||
|
|
||||||
impl PipeReader {
|
|
||||||
/// Create a new [`PipeReader`] instance that shares the same underlying file description.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// #![feature(anonymous_pipe)]
|
|
||||||
/// # #[cfg(miri)] fn main() {}
|
|
||||||
/// # #[cfg(not(miri))]
|
|
||||||
/// # fn main() -> std::io::Result<()> {
|
|
||||||
/// # use std::fs;
|
|
||||||
/// # use std::io::Write;
|
|
||||||
/// # use std::process::Command;
|
|
||||||
/// const NUM_SLOT: u8 = 2;
|
|
||||||
/// const NUM_PROC: u8 = 5;
|
|
||||||
/// const OUTPUT: &str = "work.txt";
|
|
||||||
///
|
|
||||||
/// let mut jobs = vec![];
|
|
||||||
/// let (reader, mut writer) = std::pipe::pipe()?;
|
|
||||||
///
|
|
||||||
/// // Write NUM_SLOT characters the pipe.
|
|
||||||
/// writer.write_all(&[b'|'; NUM_SLOT as usize])?;
|
|
||||||
///
|
|
||||||
/// // Spawn several processes that read a character from the pipe, do some work, then
|
|
||||||
/// // write back to the pipe. When the pipe is empty, the processes block, so only
|
|
||||||
/// // NUM_SLOT processes can be working at any given time.
|
|
||||||
/// for _ in 0..NUM_PROC {
|
|
||||||
/// jobs.push(
|
|
||||||
/// Command::new("bash")
|
|
||||||
/// .args(["-c",
|
|
||||||
/// &format!(
|
|
||||||
/// "read -n 1\n\
|
|
||||||
/// echo -n 'x' >> '{OUTPUT}'\n\
|
|
||||||
/// echo -n '|'",
|
|
||||||
/// ),
|
|
||||||
/// ])
|
|
||||||
/// .stdin(reader.try_clone()?)
|
|
||||||
/// .stdout(writer.try_clone()?)
|
|
||||||
/// .spawn()?,
|
|
||||||
/// );
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // Wait for all jobs to finish.
|
|
||||||
/// for mut job in jobs {
|
|
||||||
/// job.wait()?;
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// // Check our work and clean up.
|
|
||||||
/// let xs = fs::read_to_string(OUTPUT)?;
|
|
||||||
/// fs::remove_file(OUTPUT)?;
|
|
||||||
/// assert_eq!(xs, "x".repeat(NUM_PROC.into()));
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
|
||||||
pub fn try_clone(&self) -> io::Result<Self> {
|
|
||||||
self.0.try_clone().map(Self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PipeWriter {
|
|
||||||
/// Create a new [`PipeWriter`] instance that shares the same underlying file description.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```no_run
|
|
||||||
/// #![feature(anonymous_pipe)]
|
|
||||||
/// # #[cfg(miri)] fn main() {}
|
|
||||||
/// # #[cfg(not(miri))]
|
|
||||||
/// # fn main() -> std::io::Result<()> {
|
|
||||||
/// # use std::process::Command;
|
|
||||||
/// # use std::io::Read;
|
|
||||||
/// let (mut reader, writer) = std::pipe::pipe()?;
|
|
||||||
///
|
|
||||||
/// // Spawn a process that writes to stdout and stderr.
|
|
||||||
/// let mut peer = Command::new("bash")
|
|
||||||
/// .args([
|
|
||||||
/// "-c",
|
|
||||||
/// "echo -n foo\n\
|
|
||||||
/// echo -n bar >&2"
|
|
||||||
/// ])
|
|
||||||
/// .stdout(writer.try_clone()?)
|
|
||||||
/// .stderr(writer)
|
|
||||||
/// .spawn()?;
|
|
||||||
///
|
|
||||||
/// // Read and check the result.
|
|
||||||
/// let mut msg = String::new();
|
|
||||||
/// reader.read_to_string(&mut msg)?;
|
|
||||||
/// assert_eq!(&msg, "foobar");
|
|
||||||
///
|
|
||||||
/// peer.wait()?;
|
|
||||||
/// # Ok(())
|
|
||||||
/// # }
|
|
||||||
/// ```
|
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
|
||||||
pub fn try_clone(&self) -> io::Result<Self> {
|
|
||||||
self.0.try_clone().map(Self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
|
||||||
impl io::Read for &PipeReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
self.0.read(buf)
|
|
||||||
}
|
|
||||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
|
||||||
self.0.read_vectored(bufs)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn is_read_vectored(&self) -> bool {
|
|
||||||
self.0.is_read_vectored()
|
|
||||||
}
|
|
||||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
|
||||||
self.0.read_to_end(buf)
|
|
||||||
}
|
|
||||||
fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
|
|
||||||
self.0.read_buf(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
|
||||||
impl io::Read for PipeReader {
|
|
||||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
|
||||||
self.0.read(buf)
|
|
||||||
}
|
|
||||||
fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result<usize> {
|
|
||||||
self.0.read_vectored(bufs)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn is_read_vectored(&self) -> bool {
|
|
||||||
self.0.is_read_vectored()
|
|
||||||
}
|
|
||||||
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
|
|
||||||
self.0.read_to_end(buf)
|
|
||||||
}
|
|
||||||
fn read_buf(&mut self, buf: io::BorrowedCursor<'_>) -> io::Result<()> {
|
|
||||||
self.0.read_buf(buf)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
|
||||||
impl io::Write for &PipeWriter {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.0.write(buf)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
|
||||||
self.0.write_vectored(bufs)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_write_vectored(&self) -> bool {
|
|
||||||
self.0.is_write_vectored()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
|
||||||
impl io::Write for PipeWriter {
|
|
||||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
|
||||||
self.0.write(buf)
|
|
||||||
}
|
|
||||||
#[inline]
|
|
||||||
fn flush(&mut self) -> io::Result<()> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result<usize> {
|
|
||||||
self.0.write_vectored(bufs)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn is_write_vectored(&self) -> bool {
|
|
||||||
self.0.is_write_vectored()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,19 +0,0 @@
|
||||||
use crate::io::{Read, Write};
|
|
||||||
use crate::pipe::pipe;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[cfg(all(windows, unix, not(miri)))]
|
|
||||||
fn pipe_creation_clone_and_rw() {
|
|
||||||
let (rx, tx) = pipe().unwrap();
|
|
||||||
|
|
||||||
tx.try_clone().unwrap().write_all(b"12345").unwrap();
|
|
||||||
drop(tx);
|
|
||||||
|
|
||||||
let mut rx2 = rx.try_clone().unwrap();
|
|
||||||
drop(rx);
|
|
||||||
|
|
||||||
let mut s = String::new();
|
|
||||||
rx2.read_to_string(&mut s).unwrap();
|
|
||||||
drop(rx2);
|
|
||||||
assert_eq!(s, "12345");
|
|
||||||
}
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::io;
|
use crate::io::{self, PipeReader, PipeWriter};
|
||||||
use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
use crate::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
|
||||||
use crate::pipe::{PipeReader, PipeWriter};
|
|
||||||
use crate::process::Stdio;
|
use crate::process::Stdio;
|
||||||
use crate::sys::fd::FileDesc;
|
use crate::sys::fd::FileDesc;
|
||||||
use crate::sys::pipe::anon_pipe;
|
use crate::sys::pipe::anon_pipe;
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::io;
|
use crate::io::{self, PipeReader, PipeWriter};
|
||||||
use crate::pipe::{PipeReader, PipeWriter};
|
|
||||||
use crate::process::Stdio;
|
use crate::process::Stdio;
|
||||||
pub use crate::sys::pipe::AnonPipe;
|
pub use crate::sys::pipe::AnonPipe;
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
|
use crate::io::{self, PipeReader, PipeWriter};
|
||||||
use crate::os::windows::io::{
|
use crate::os::windows::io::{
|
||||||
AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle,
|
AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle,
|
||||||
};
|
};
|
||||||
use crate::pipe::{PipeReader, PipeWriter};
|
|
||||||
use crate::process::Stdio;
|
use crate::process::Stdio;
|
||||||
|
use crate::ptr;
|
||||||
use crate::sys::c;
|
use crate::sys::c;
|
||||||
use crate::sys::handle::Handle;
|
use crate::sys::handle::Handle;
|
||||||
use crate::sys_common::{FromInner, IntoInner};
|
use crate::sys_common::{FromInner, IntoInner};
|
||||||
use crate::{io, ptr};
|
|
||||||
|
|
||||||
pub type AnonPipe = Handle;
|
pub type AnonPipe = Handle;
|
||||||
|
|
||||||
|
|
|
@ -52,15 +52,14 @@ use crate::cmp::min;
|
||||||
use crate::fs::{File, Metadata};
|
use crate::fs::{File, Metadata};
|
||||||
use crate::io::copy::generic_copy;
|
use crate::io::copy::generic_copy;
|
||||||
use crate::io::{
|
use crate::io::{
|
||||||
BufRead, BufReader, BufWriter, Error, Read, Result, StderrLock, StdinLock, StdoutLock, Take,
|
BufRead, BufReader, BufWriter, Error, PipeReader, PipeWriter, Read, Result, StderrLock,
|
||||||
Write,
|
StdinLock, StdoutLock, Take, Write,
|
||||||
};
|
};
|
||||||
use crate::mem::ManuallyDrop;
|
use crate::mem::ManuallyDrop;
|
||||||
use crate::net::TcpStream;
|
use crate::net::TcpStream;
|
||||||
use crate::os::unix::fs::FileTypeExt;
|
use crate::os::unix::fs::FileTypeExt;
|
||||||
use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||||
use crate::os::unix::net::UnixStream;
|
use crate::os::unix::net::UnixStream;
|
||||||
use crate::pipe::{PipeReader, PipeWriter};
|
|
||||||
use crate::process::{ChildStderr, ChildStdin, ChildStdout};
|
use crate::process::{ChildStderr, ChildStdin, ChildStdout};
|
||||||
use crate::ptr;
|
use crate::ptr;
|
||||||
use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
fn main() {
|
fn main() {
|
||||||
#[cfg(all(not(miri), any(unix, windows)))]
|
#[cfg(all(not(miri), any(unix, windows)))]
|
||||||
{
|
{
|
||||||
use std::io::Read;
|
use std::io::{Read, pipe};
|
||||||
use std::pipe::pipe;
|
|
||||||
use std::{env, process};
|
use std::{env, process};
|
||||||
|
|
||||||
if env::var("I_AM_THE_CHILD").is_ok() {
|
if env::var("I_AM_THE_CHILD").is_ok() {
|
||||||
|
|
|
@ -334,6 +334,7 @@ lint_any!(
|
||||||
CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri";
|
CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri";
|
||||||
Clippy, "src/tools/clippy", "clippy";
|
Clippy, "src/tools/clippy", "clippy";
|
||||||
CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
|
CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
|
||||||
|
CodegenGcc, "compiler/rustc_codegen_gcc", "rustc-codegen-gcc";
|
||||||
Compiletest, "src/tools/compiletest", "compiletest";
|
Compiletest, "src/tools/compiletest", "compiletest";
|
||||||
CoverageDump, "src/tools/coverage-dump", "coverage-dump";
|
CoverageDump, "src/tools/coverage-dump", "coverage-dump";
|
||||||
Jsondocck, "src/tools/jsondocck", "jsondocck";
|
Jsondocck, "src/tools/jsondocck", "jsondocck";
|
||||||
|
@ -400,6 +401,12 @@ impl Step for CI {
|
||||||
],
|
],
|
||||||
forbid: vec![],
|
forbid: vec![],
|
||||||
};
|
};
|
||||||
|
builder.ensure(Std {
|
||||||
|
target: self.target,
|
||||||
|
config: self.config.merge(&library_clippy_cfg),
|
||||||
|
crates: vec![],
|
||||||
|
});
|
||||||
|
|
||||||
let compiler_clippy_cfg = LintConfig {
|
let compiler_clippy_cfg = LintConfig {
|
||||||
allow: vec!["clippy::all".into()],
|
allow: vec!["clippy::all".into()],
|
||||||
warn: vec![],
|
warn: vec![],
|
||||||
|
@ -419,16 +426,21 @@ impl Step for CI {
|
||||||
],
|
],
|
||||||
forbid: vec![],
|
forbid: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
builder.ensure(Std {
|
|
||||||
target: self.target,
|
|
||||||
config: self.config.merge(&library_clippy_cfg),
|
|
||||||
crates: vec![],
|
|
||||||
});
|
|
||||||
builder.ensure(Rustc {
|
builder.ensure(Rustc {
|
||||||
target: self.target,
|
target: self.target,
|
||||||
config: self.config.merge(&compiler_clippy_cfg),
|
config: self.config.merge(&compiler_clippy_cfg),
|
||||||
crates: vec![],
|
crates: vec![],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let rustc_codegen_gcc = LintConfig {
|
||||||
|
allow: vec![],
|
||||||
|
warn: vec![],
|
||||||
|
deny: vec!["warnings".into()],
|
||||||
|
forbid: vec![],
|
||||||
|
};
|
||||||
|
builder.ensure(CodegenGcc {
|
||||||
|
target: self.target,
|
||||||
|
config: self.config.merge(&rustc_codegen_gcc),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -994,20 +994,24 @@ impl Step for PlainSourceTarball {
|
||||||
|
|
||||||
// This is the set of root paths which will become part of the source package
|
// This is the set of root paths which will become part of the source package
|
||||||
let src_files = [
|
let src_files = [
|
||||||
|
// tidy-alphabetical-start
|
||||||
|
".gitmodules",
|
||||||
|
"Cargo.lock",
|
||||||
|
"Cargo.toml",
|
||||||
|
"config.example.toml",
|
||||||
|
"configure",
|
||||||
|
"CONTRIBUTING.md",
|
||||||
"COPYRIGHT",
|
"COPYRIGHT",
|
||||||
"LICENSE-APACHE",
|
"LICENSE-APACHE",
|
||||||
|
"license-metadata.json",
|
||||||
"LICENSE-MIT",
|
"LICENSE-MIT",
|
||||||
"CONTRIBUTING.md",
|
|
||||||
"README.md",
|
"README.md",
|
||||||
"RELEASES.md",
|
"RELEASES.md",
|
||||||
"REUSE.toml",
|
"REUSE.toml",
|
||||||
"license-metadata.json",
|
"x",
|
||||||
"configure",
|
"x.ps1",
|
||||||
"x.py",
|
"x.py",
|
||||||
"config.example.toml",
|
// tidy-alphabetical-end
|
||||||
"Cargo.toml",
|
|
||||||
"Cargo.lock",
|
|
||||||
".gitmodules",
|
|
||||||
];
|
];
|
||||||
let src_dirs = ["src", "compiler", "library", "tests", "LICENSES"];
|
let src_dirs = ["src", "compiler", "library", "tests", "LICENSES"];
|
||||||
|
|
||||||
|
|
|
@ -900,6 +900,7 @@ impl<'a> Builder<'a> {
|
||||||
clippy::BuildManifest,
|
clippy::BuildManifest,
|
||||||
clippy::CargoMiri,
|
clippy::CargoMiri,
|
||||||
clippy::Clippy,
|
clippy::Clippy,
|
||||||
|
clippy::CodegenGcc,
|
||||||
clippy::CollectLicenseMetadata,
|
clippy::CollectLicenseMetadata,
|
||||||
clippy::Compiletest,
|
clippy::Compiletest,
|
||||||
clippy::CoverageDump,
|
clippy::CoverageDump,
|
||||||
|
|
|
@ -25,7 +25,7 @@ enum Binary {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_broken_pipe_handled_gracefully(bin: Binary, mut cmd: Command) {
|
fn check_broken_pipe_handled_gracefully(bin: Binary, mut cmd: Command) {
|
||||||
let (reader, writer) = std::pipe::pipe().unwrap();
|
let (reader, writer) = std::io::pipe().unwrap();
|
||||||
drop(reader); // close read-end
|
drop(reader); // close read-end
|
||||||
cmd.stdout(writer).stderr(Stdio::piped());
|
cmd.stdout(writer).stderr(Stdio::piped());
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,13 @@ const MAYBE_NULL: () = {
|
||||||
let ptr = &x as *const i32;
|
let ptr = &x as *const i32;
|
||||||
// This one is still unambiguous...
|
// This one is still unambiguous...
|
||||||
assert!(!ptr.is_null());
|
assert!(!ptr.is_null());
|
||||||
// but once we shift outside the allocation, we might become null.
|
// and in fact, any offset not visible by 4 (the alignment) cannot be null,
|
||||||
|
// even if it goes out-of-bounds...
|
||||||
|
assert!(!ptr.wrapping_byte_add(13).is_null());
|
||||||
|
assert!(!ptr.wrapping_byte_add(18).is_null());
|
||||||
|
assert!(!ptr.wrapping_byte_sub(1).is_null());
|
||||||
|
// ... but once we shift outside the allocation, with an offset divisible by 4,
|
||||||
|
// we might become null.
|
||||||
assert!(!ptr.wrapping_sub(512).is_null()); //~inside `MAYBE_NULL`
|
assert!(!ptr.wrapping_sub(512).is_null()); //~inside `MAYBE_NULL`
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ note: inside `std::ptr::const_ptr::<impl *const T>::is_null::compiletime`
|
||||||
note: inside `std::ptr::const_ptr::<impl *const i32>::is_null`
|
note: inside `std::ptr::const_ptr::<impl *const i32>::is_null`
|
||||||
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
||||||
note: inside `MAYBE_NULL`
|
note: inside `MAYBE_NULL`
|
||||||
--> $DIR/const-ptr-is-null.rs:16:14
|
--> $DIR/const-ptr-is-null.rs:22:14
|
||||||
|
|
|
|
||||||
LL | assert!(!ptr.wrapping_sub(512).is_null());
|
LL | assert!(!ptr.wrapping_sub(512).is_null());
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue