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
|
||||
run: |
|
||||
# DockerHub image we want to mirror
|
||||
image="ubuntu:22.04"
|
||||
# List of DockerHub images to mirror to ghcr.io
|
||||
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
|
||||
./crane copy \
|
||||
docker.io/${image} \
|
||||
ghcr.io/${{ github.repository_owner }}/${image}
|
||||
# Mirror each image from DockerHub to ghcr.io
|
||||
for img in "${images[@]}"; do
|
||||
echo "Mirroring ${img}..."
|
||||
# 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 super::{LiveLoans, LocalizedOutlivesConstraintSet};
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::dataflow::BorrowIndex;
|
||||
use crate::region_infer::values::LivenessValues;
|
||||
use crate::type_check::Locations;
|
||||
use crate::{BorrowSet, PlaceConflictBias, places_conflict};
|
||||
|
||||
/// With the full graph of constraints, we can compute loan reachability, stop at kills, and trace
|
||||
/// loan liveness throughout the CFG.
|
||||
/// Compute loan reachability, stop at kills, and trace loan liveness throughout the CFG, by
|
||||
/// 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>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
liveness: &LivenessValues,
|
||||
outlives_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
localized_outlives_constraints: &LocalizedOutlivesConstraintSet,
|
||||
) -> LiveLoans {
|
||||
|
@ -29,7 +34,11 @@ pub(super) fn compute_loan_liveness<'tcx>(
|
|||
// edges when visualizing the constraint graph anyways.
|
||||
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 stack = Vec::new();
|
||||
|
||||
|
@ -108,7 +117,7 @@ pub(super) fn compute_loan_liveness<'tcx>(
|
|||
let is_loan_killed =
|
||||
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
|
||||
// forward traversal.
|
||||
if is_loan_killed {
|
||||
|
@ -125,9 +134,17 @@ pub(super) fn compute_loan_liveness<'tcx>(
|
|||
live_loans
|
||||
}
|
||||
|
||||
/// The localized constraint graph is currently the per-node map of its physical edges. In the
|
||||
/// future, we'll add logical edges to model constraints that hold at all points in the CFG.
|
||||
type LocalizedConstraintGraph = FxHashMap<LocalizedNode, FxIndexSet<LocalizedNode>>;
|
||||
/// The localized constraint graph indexes the physical and logical edges to compute a given node's
|
||||
/// successors during traversal.
|
||||
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.
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
|
@ -136,24 +153,44 @@ struct LocalizedNode {
|
|||
point: PointIndex,
|
||||
}
|
||||
|
||||
/// Traverses the constraints and returns the indexable graph of edges per node.
|
||||
fn index_constraints(constraints: &LocalizedOutlivesConstraintSet) -> LocalizedConstraintGraph {
|
||||
let mut edges = LocalizedConstraintGraph::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);
|
||||
impl LocalizedConstraintGraph {
|
||||
/// Traverses the constraints and returns the indexed graph of edges per node.
|
||||
fn new<'tcx>(
|
||||
constraints: &LocalizedOutlivesConstraintSet,
|
||||
logical_constraints: impl Iterator<Item = OutlivesConstraint<'tcx>>,
|
||||
) -> Self {
|
||||
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(
|
||||
graph: &LocalizedConstraintGraph,
|
||||
node: LocalizedNode,
|
||||
) -> impl Iterator<Item = LocalizedNode> + use<'_> {
|
||||
graph.get(&node).into_iter().flat_map(|edges| edges.iter().copied())
|
||||
/// 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:
|
||||
// - the physical edges present at this node,
|
||||
// - the materialized logical edges that exist virtually at all points for this node's
|
||||
// region, localized at this point.
|
||||
let physical_edges =
|
||||
self.edges.get(&node).into_iter().flat_map(|targets| targets.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.
|
||||
|
|
|
@ -130,6 +130,7 @@ impl PoloniusContext {
|
|||
tcx,
|
||||
body,
|
||||
regioncx.liveness_constraints(),
|
||||
regioncx.outlives_constraints(),
|
||||
borrow_set,
|
||||
&localized_outlives_constraints,
|
||||
);
|
||||
|
|
|
@ -22,23 +22,11 @@ pub(super) fn convert_typeck_constraints<'tcx>(
|
|||
for outlives_constraint in outlives_constraints {
|
||||
match outlives_constraint.locations {
|
||||
Locations::All(_) => {
|
||||
// For now, turn logical constraints holding at all points into physical edges at
|
||||
// every point in the graph.
|
||||
// FIXME: encode this into *traversal* instead.
|
||||
for (block, bb) in body.basic_blocks.iter_enumerated() {
|
||||
let statement_count = bb.statements.len();
|
||||
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,
|
||||
});
|
||||
}
|
||||
}
|
||||
// We don't turn constraints holding at all points into physical edges at every
|
||||
// point in the graph. They are encoded into *traversal* instead: a given node's
|
||||
// successors will combine these logical edges with the regular, physical, localized
|
||||
// edges.
|
||||
continue;
|
||||
}
|
||||
|
||||
Locations::Single(location) => {
|
||||
|
|
|
@ -1481,22 +1481,31 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
|||
/// Test if this value might be null.
|
||||
/// 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> {
|
||||
interp_ok(match scalar.try_to_scalar_int() {
|
||||
Ok(int) => int.is_null(),
|
||||
match scalar.try_to_scalar_int() {
|
||||
Ok(int) => interp_ok(int.is_null()),
|
||||
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)?;
|
||||
match self.ptr_try_get_alloc_id(ptr, 0) {
|
||||
Ok((alloc_id, offset, _)) => {
|
||||
let size = self.get_alloc_info(alloc_id).size;
|
||||
// If the pointer is out-of-bounds, it may be null.
|
||||
// Note that one-past-the-end (offset == size) is still inbounds, and never null.
|
||||
offset > size
|
||||
let info = self.get_alloc_info(alloc_id);
|
||||
// If the pointer is in-bounds (including "at the end"), it is definitely not null.
|
||||
if offset <= info.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"),
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Turning a "maybe pointer" into a proper pointer (and some information
|
||||
|
|
|
@ -504,7 +504,6 @@ impl f128 {
|
|||
///
|
||||
/// ```rust
|
||||
/// #![feature(f128)]
|
||||
/// #![feature(float_next_up_down)]
|
||||
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
||||
/// # #[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
|
||||
/// [`INFINITY`]: Self::INFINITY
|
||||
/// [`MIN`]: Self::MIN
|
||||
/// [`MAX`]: Self::MAX
|
||||
#[inline]
|
||||
#[doc(alias = "nextUp")]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
// #[unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
pub const fn next_up(self) -> Self {
|
||||
// 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
|
||||
|
@ -558,7 +559,6 @@ impl f128 {
|
|||
///
|
||||
/// ```rust
|
||||
/// #![feature(f128)]
|
||||
/// #![feature(float_next_up_down)]
|
||||
/// # // FIXME(f16_f128): remove when `eqtf2` is available
|
||||
/// # #[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
|
||||
/// [`INFINITY`]: Self::INFINITY
|
||||
/// [`MIN`]: Self::MIN
|
||||
/// [`MAX`]: Self::MAX
|
||||
#[inline]
|
||||
#[doc(alias = "nextDown")]
|
||||
#[unstable(feature = "f128", issue = "116909")]
|
||||
// #[unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
pub const fn next_down(self) -> Self {
|
||||
// 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
|
||||
|
|
|
@ -497,7 +497,6 @@ impl f16 {
|
|||
///
|
||||
/// ```rust
|
||||
/// #![feature(f16)]
|
||||
/// #![feature(float_next_up_down)]
|
||||
/// # // FIXME(f16_f128): ABI issues on MSVC
|
||||
/// # #[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
|
||||
/// [`INFINITY`]: Self::INFINITY
|
||||
/// [`MIN`]: Self::MIN
|
||||
/// [`MAX`]: Self::MAX
|
||||
#[inline]
|
||||
#[doc(alias = "nextUp")]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
// #[unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
pub const fn next_up(self) -> Self {
|
||||
// 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
|
||||
|
@ -551,7 +552,6 @@ impl f16 {
|
|||
///
|
||||
/// ```rust
|
||||
/// #![feature(f16)]
|
||||
/// #![feature(float_next_up_down)]
|
||||
/// # // FIXME(f16_f128): ABI issues on MSVC
|
||||
/// # #[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
|
||||
/// [`INFINITY`]: Self::INFINITY
|
||||
/// [`MIN`]: Self::MIN
|
||||
/// [`MAX`]: Self::MAX
|
||||
#[inline]
|
||||
#[doc(alias = "nextDown")]
|
||||
#[unstable(feature = "f16", issue = "116909")]
|
||||
// #[unstable(feature = "float_next_up_down", issue = "91399")]
|
||||
pub const fn next_down(self) -> Self {
|
||||
// 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
|
||||
|
|
|
@ -726,7 +726,6 @@ impl f32 {
|
|||
/// is finite `x == x.next_up().next_down()` also holds.
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(float_next_up_down)]
|
||||
/// // f32::EPSILON is the difference between 1.0 and the next number up.
|
||||
/// assert_eq!(1.0f32.next_up(), 1.0 + f32::EPSILON);
|
||||
/// // But not for most numbers.
|
||||
|
@ -734,12 +733,16 @@ impl f32 {
|
|||
/// assert_eq!(16777216f32.next_up(), 16777218.0);
|
||||
/// ```
|
||||
///
|
||||
/// This operation corresponds to IEEE-754 `nextUp`.
|
||||
///
|
||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||
/// [`INFINITY`]: Self::INFINITY
|
||||
/// [`MIN`]: Self::MIN
|
||||
/// [`MAX`]: Self::MAX
|
||||
#[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 {
|
||||
// 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
|
||||
|
@ -774,7 +777,6 @@ impl f32 {
|
|||
/// is finite `x == x.next_down().next_up()` also holds.
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(float_next_up_down)]
|
||||
/// let x = 1.0f32;
|
||||
/// // Clamp value into range [0, 1).
|
||||
/// let clamped = x.clamp(0.0, 1.0f32.next_down());
|
||||
|
@ -782,12 +784,16 @@ impl f32 {
|
|||
/// assert_eq!(clamped.next_up(), 1.0);
|
||||
/// ```
|
||||
///
|
||||
/// This operation corresponds to IEEE-754 `nextDown`.
|
||||
///
|
||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||
/// [`INFINITY`]: Self::INFINITY
|
||||
/// [`MIN`]: Self::MIN
|
||||
/// [`MAX`]: Self::MAX
|
||||
#[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 {
|
||||
// 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
|
||||
|
|
|
@ -743,7 +743,6 @@ impl f64 {
|
|||
/// is finite `x == x.next_up().next_down()` also holds.
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(float_next_up_down)]
|
||||
/// // f64::EPSILON is the difference between 1.0 and the next number up.
|
||||
/// assert_eq!(1.0f64.next_up(), 1.0 + f64::EPSILON);
|
||||
/// // But not for most numbers.
|
||||
|
@ -751,12 +750,16 @@ impl f64 {
|
|||
/// assert_eq!(9007199254740992f64.next_up(), 9007199254740994.0);
|
||||
/// ```
|
||||
///
|
||||
/// This operation corresponds to IEEE-754 `nextUp`.
|
||||
///
|
||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||
/// [`INFINITY`]: Self::INFINITY
|
||||
/// [`MIN`]: Self::MIN
|
||||
/// [`MAX`]: Self::MAX
|
||||
#[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 {
|
||||
// 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
|
||||
|
@ -791,7 +794,6 @@ impl f64 {
|
|||
/// is finite `x == x.next_down().next_up()` also holds.
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(float_next_up_down)]
|
||||
/// let x = 1.0f64;
|
||||
/// // Clamp value into range [0, 1).
|
||||
/// let clamped = x.clamp(0.0, 1.0f64.next_down());
|
||||
|
@ -799,12 +801,16 @@ impl f64 {
|
|||
/// assert_eq!(clamped.next_up(), 1.0);
|
||||
/// ```
|
||||
///
|
||||
/// This operation corresponds to IEEE-754 `nextDown`.
|
||||
///
|
||||
/// [`NEG_INFINITY`]: Self::NEG_INFINITY
|
||||
/// [`INFINITY`]: Self::INFINITY
|
||||
/// [`MIN`]: Self::MIN
|
||||
/// [`MAX`]: Self::MAX
|
||||
#[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 {
|
||||
// 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
|
||||
|
|
|
@ -330,6 +330,7 @@ pub use self::{
|
|||
};
|
||||
use crate::mem::take;
|
||||
use crate::ops::{Deref, DerefMut};
|
||||
use crate::sys::anonymous_pipe::{AnonPipe, pipe as pipe_inner};
|
||||
use crate::{cmp, fmt, slice, str, sys};
|
||||
|
||||
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);
|
||||
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(float_gamma)]
|
||||
#![feature(float_minimum_maximum)]
|
||||
#![feature(float_next_up_down)]
|
||||
#![feature(fmt_internals)]
|
||||
#![feature(hasher_prefixfree_extras)]
|
||||
#![feature(hashmap_internals)]
|
||||
|
@ -596,8 +595,6 @@ pub mod panic;
|
|||
#[unstable(feature = "pattern_type_macro", issue = "123646")]
|
||||
pub mod pat;
|
||||
pub mod path;
|
||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||
pub mod pipe;
|
||||
pub mod process;
|
||||
#[unstable(feature = "random", issue = "130703")]
|
||||
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::pipe::{PipeReader, PipeWriter};
|
||||
use crate::process::Stdio;
|
||||
use crate::sys::fd::FileDesc;
|
||||
use crate::sys::pipe::anon_pipe;
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
use crate::io;
|
||||
use crate::pipe::{PipeReader, PipeWriter};
|
||||
use crate::io::{self, PipeReader, PipeWriter};
|
||||
use crate::process::Stdio;
|
||||
pub use crate::sys::pipe::AnonPipe;
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use crate::io::{self, PipeReader, PipeWriter};
|
||||
use crate::os::windows::io::{
|
||||
AsHandle, AsRawHandle, BorrowedHandle, FromRawHandle, IntoRawHandle, OwnedHandle, RawHandle,
|
||||
};
|
||||
use crate::pipe::{PipeReader, PipeWriter};
|
||||
use crate::process::Stdio;
|
||||
use crate::ptr;
|
||||
use crate::sys::c;
|
||||
use crate::sys::handle::Handle;
|
||||
use crate::sys_common::{FromInner, IntoInner};
|
||||
use crate::{io, ptr};
|
||||
|
||||
pub type AnonPipe = Handle;
|
||||
|
||||
|
|
|
@ -52,15 +52,14 @@ use crate::cmp::min;
|
|||
use crate::fs::{File, Metadata};
|
||||
use crate::io::copy::generic_copy;
|
||||
use crate::io::{
|
||||
BufRead, BufReader, BufWriter, Error, Read, Result, StderrLock, StdinLock, StdoutLock, Take,
|
||||
Write,
|
||||
BufRead, BufReader, BufWriter, Error, PipeReader, PipeWriter, Read, Result, StderrLock,
|
||||
StdinLock, StdoutLock, Take, Write,
|
||||
};
|
||||
use crate::mem::ManuallyDrop;
|
||||
use crate::net::TcpStream;
|
||||
use crate::os::unix::fs::FileTypeExt;
|
||||
use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use crate::os::unix::net::UnixStream;
|
||||
use crate::pipe::{PipeReader, PipeWriter};
|
||||
use crate::process::{ChildStderr, ChildStdin, ChildStdout};
|
||||
use crate::ptr;
|
||||
use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering};
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
fn main() {
|
||||
#[cfg(all(not(miri), any(unix, windows)))]
|
||||
{
|
||||
use std::io::Read;
|
||||
use std::pipe::pipe;
|
||||
use std::io::{Read, pipe};
|
||||
use std::{env, process};
|
||||
|
||||
if env::var("I_AM_THE_CHILD").is_ok() {
|
||||
|
|
|
@ -334,6 +334,7 @@ lint_any!(
|
|||
CargoMiri, "src/tools/miri/cargo-miri", "cargo-miri";
|
||||
Clippy, "src/tools/clippy", "clippy";
|
||||
CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
|
||||
CodegenGcc, "compiler/rustc_codegen_gcc", "rustc-codegen-gcc";
|
||||
Compiletest, "src/tools/compiletest", "compiletest";
|
||||
CoverageDump, "src/tools/coverage-dump", "coverage-dump";
|
||||
Jsondocck, "src/tools/jsondocck", "jsondocck";
|
||||
|
@ -400,6 +401,12 @@ impl Step for CI {
|
|||
],
|
||||
forbid: vec![],
|
||||
};
|
||||
builder.ensure(Std {
|
||||
target: self.target,
|
||||
config: self.config.merge(&library_clippy_cfg),
|
||||
crates: vec![],
|
||||
});
|
||||
|
||||
let compiler_clippy_cfg = LintConfig {
|
||||
allow: vec!["clippy::all".into()],
|
||||
warn: vec![],
|
||||
|
@ -419,16 +426,21 @@ impl Step for CI {
|
|||
],
|
||||
forbid: vec![],
|
||||
};
|
||||
|
||||
builder.ensure(Std {
|
||||
target: self.target,
|
||||
config: self.config.merge(&library_clippy_cfg),
|
||||
crates: vec![],
|
||||
});
|
||||
builder.ensure(Rustc {
|
||||
target: self.target,
|
||||
config: self.config.merge(&compiler_clippy_cfg),
|
||||
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
|
||||
let src_files = [
|
||||
// tidy-alphabetical-start
|
||||
".gitmodules",
|
||||
"Cargo.lock",
|
||||
"Cargo.toml",
|
||||
"config.example.toml",
|
||||
"configure",
|
||||
"CONTRIBUTING.md",
|
||||
"COPYRIGHT",
|
||||
"LICENSE-APACHE",
|
||||
"license-metadata.json",
|
||||
"LICENSE-MIT",
|
||||
"CONTRIBUTING.md",
|
||||
"README.md",
|
||||
"RELEASES.md",
|
||||
"REUSE.toml",
|
||||
"license-metadata.json",
|
||||
"configure",
|
||||
"x",
|
||||
"x.ps1",
|
||||
"x.py",
|
||||
"config.example.toml",
|
||||
"Cargo.toml",
|
||||
"Cargo.lock",
|
||||
".gitmodules",
|
||||
// tidy-alphabetical-end
|
||||
];
|
||||
let src_dirs = ["src", "compiler", "library", "tests", "LICENSES"];
|
||||
|
||||
|
|
|
@ -900,6 +900,7 @@ impl<'a> Builder<'a> {
|
|||
clippy::BuildManifest,
|
||||
clippy::CargoMiri,
|
||||
clippy::Clippy,
|
||||
clippy::CodegenGcc,
|
||||
clippy::CollectLicenseMetadata,
|
||||
clippy::Compiletest,
|
||||
clippy::CoverageDump,
|
||||
|
|
|
@ -25,7 +25,7 @@ enum Binary {
|
|||
}
|
||||
|
||||
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
|
||||
cmd.stdout(writer).stderr(Stdio::piped());
|
||||
|
||||
|
|
|
@ -12,7 +12,13 @@ const MAYBE_NULL: () = {
|
|||
let ptr = &x as *const i32;
|
||||
// This one is still unambiguous...
|
||||
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`
|
||||
};
|
||||
|
||||
|
|
|
@ -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`
|
||||
--> $SRC_DIR/core/src/ptr/const_ptr.rs:LL:COL
|
||||
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());
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue