Support #[unix_sigpipe = "inherit|sig_dfl|sig_ign"]
on fn main()
This makes it possible to instruct libstd to never touch the signal handler for `SIGPIPE`, which makes programs pipeable by default (e.g. with `./your-program | head -n 1`) without `ErrorKind::BrokenPipe` errors.
This commit is contained in:
parent
ee285eab69
commit
ddee45e1d7
@ -1,7 +1,7 @@
|
|||||||
use rustc_hir::LangItem;
|
use rustc_hir::LangItem;
|
||||||
use rustc_middle::ty::subst::GenericArg;
|
use rustc_middle::ty::subst::GenericArg;
|
||||||
use rustc_middle::ty::AssocKind;
|
use rustc_middle::ty::AssocKind;
|
||||||
use rustc_session::config::EntryFnType;
|
use rustc_session::config::{sigpipe, EntryFnType};
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
|
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
@ -15,12 +15,12 @@ pub(crate) fn maybe_create_entry_wrapper(
|
|||||||
is_jit: bool,
|
is_jit: bool,
|
||||||
is_primary_cgu: bool,
|
is_primary_cgu: bool,
|
||||||
) {
|
) {
|
||||||
let (main_def_id, is_main_fn) = match tcx.entry_fn(()) {
|
let (main_def_id, (is_main_fn, sigpipe)) = match tcx.entry_fn(()) {
|
||||||
Some((def_id, entry_ty)) => (
|
Some((def_id, entry_ty)) => (
|
||||||
def_id,
|
def_id,
|
||||||
match entry_ty {
|
match entry_ty {
|
||||||
EntryFnType::Main => true,
|
EntryFnType::Main { sigpipe } => (true, sigpipe),
|
||||||
EntryFnType::Start => false,
|
EntryFnType::Start => (false, sigpipe::DEFAULT),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
None => return,
|
None => return,
|
||||||
@ -35,7 +35,7 @@ pub(crate) fn maybe_create_entry_wrapper(
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
create_entry_fn(tcx, module, unwind_context, main_def_id, is_jit, is_main_fn);
|
create_entry_fn(tcx, module, unwind_context, main_def_id, is_jit, is_main_fn, sigpipe);
|
||||||
|
|
||||||
fn create_entry_fn(
|
fn create_entry_fn(
|
||||||
tcx: TyCtxt<'_>,
|
tcx: TyCtxt<'_>,
|
||||||
@ -44,6 +44,7 @@ pub(crate) fn maybe_create_entry_wrapper(
|
|||||||
rust_main_def_id: DefId,
|
rust_main_def_id: DefId,
|
||||||
ignore_lang_start_wrapper: bool,
|
ignore_lang_start_wrapper: bool,
|
||||||
is_main_fn: bool,
|
is_main_fn: bool,
|
||||||
|
sigpipe: u8,
|
||||||
) {
|
) {
|
||||||
let main_ret_ty = tcx.fn_sig(rust_main_def_id).output();
|
let main_ret_ty = tcx.fn_sig(rust_main_def_id).output();
|
||||||
// Given that `main()` has no arguments,
|
// Given that `main()` has no arguments,
|
||||||
@ -83,6 +84,7 @@ pub(crate) fn maybe_create_entry_wrapper(
|
|||||||
bcx.switch_to_block(block);
|
bcx.switch_to_block(block);
|
||||||
let arg_argc = bcx.append_block_param(block, m.target_config().pointer_type());
|
let arg_argc = bcx.append_block_param(block, m.target_config().pointer_type());
|
||||||
let arg_argv = bcx.append_block_param(block, m.target_config().pointer_type());
|
let arg_argv = bcx.append_block_param(block, m.target_config().pointer_type());
|
||||||
|
let arg_sigpipe = bcx.ins().iconst(types::I8, sigpipe as i64);
|
||||||
|
|
||||||
let main_func_ref = m.declare_func_in_func(main_func_id, &mut bcx.func);
|
let main_func_ref = m.declare_func_in_func(main_func_id, &mut bcx.func);
|
||||||
|
|
||||||
@ -143,7 +145,8 @@ pub(crate) fn maybe_create_entry_wrapper(
|
|||||||
let main_val = bcx.ins().func_addr(m.target_config().pointer_type(), main_func_ref);
|
let main_val = bcx.ins().func_addr(m.target_config().pointer_type(), main_func_ref);
|
||||||
|
|
||||||
let func_ref = m.declare_func_in_func(start_func_id, &mut bcx.func);
|
let func_ref = m.declare_func_in_func(start_func_id, &mut bcx.func);
|
||||||
let call_inst = bcx.ins().call(func_ref, &[main_val, arg_argc, arg_argv]);
|
let call_inst =
|
||||||
|
bcx.ins().call(func_ref, &[main_val, arg_argc, arg_argv, arg_sigpipe]);
|
||||||
bcx.inst_results(call_inst)[0]
|
bcx.inst_results(call_inst)[0]
|
||||||
} else {
|
} else {
|
||||||
// using user-defined start fn
|
// using user-defined start fn
|
||||||
|
@ -389,15 +389,14 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||||||
|
|
||||||
let main_llfn = cx.get_fn_addr(instance);
|
let main_llfn = cx.get_fn_addr(instance);
|
||||||
|
|
||||||
let use_start_lang_item = EntryFnType::Start != entry_type;
|
let entry_fn = create_entry_fn::<Bx>(cx, main_llfn, main_def_id, entry_type);
|
||||||
let entry_fn = create_entry_fn::<Bx>(cx, main_llfn, main_def_id, use_start_lang_item);
|
|
||||||
return Some(entry_fn);
|
return Some(entry_fn);
|
||||||
|
|
||||||
fn create_entry_fn<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
fn create_entry_fn<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
||||||
cx: &'a Bx::CodegenCx,
|
cx: &'a Bx::CodegenCx,
|
||||||
rust_main: Bx::Value,
|
rust_main: Bx::Value,
|
||||||
rust_main_def_id: DefId,
|
rust_main_def_id: DefId,
|
||||||
use_start_lang_item: bool,
|
entry_type: EntryFnType,
|
||||||
) -> Bx::Function {
|
) -> Bx::Function {
|
||||||
// The entry function is either `int main(void)` or `int main(int argc, char **argv)`,
|
// The entry function is either `int main(void)` or `int main(int argc, char **argv)`,
|
||||||
// depending on whether the target needs `argc` and `argv` to be passed in.
|
// depending on whether the target needs `argc` and `argv` to be passed in.
|
||||||
@ -442,7 +441,7 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||||||
let i8pp_ty = cx.type_ptr_to(cx.type_i8p());
|
let i8pp_ty = cx.type_ptr_to(cx.type_i8p());
|
||||||
let (arg_argc, arg_argv) = get_argc_argv(cx, &mut bx);
|
let (arg_argc, arg_argv) = get_argc_argv(cx, &mut bx);
|
||||||
|
|
||||||
let (start_fn, start_ty, args) = if use_start_lang_item {
|
let (start_fn, start_ty, args) = if let EntryFnType::Main { sigpipe } = entry_type {
|
||||||
let start_def_id = cx.tcx().require_lang_item(LangItem::Start, None);
|
let start_def_id = cx.tcx().require_lang_item(LangItem::Start, None);
|
||||||
let start_fn = cx.get_fn_addr(
|
let start_fn = cx.get_fn_addr(
|
||||||
ty::Instance::resolve(
|
ty::Instance::resolve(
|
||||||
@ -454,8 +453,13 @@ pub fn maybe_create_entry_wrapper<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
);
|
);
|
||||||
let start_ty = cx.type_func(&[cx.val_ty(rust_main), isize_ty, i8pp_ty], isize_ty);
|
|
||||||
(start_fn, start_ty, vec![rust_main, arg_argc, arg_argv])
|
let i8_ty = cx.type_i8();
|
||||||
|
let arg_sigpipe = bx.const_u8(sigpipe);
|
||||||
|
|
||||||
|
let start_ty =
|
||||||
|
cx.type_func(&[cx.val_ty(rust_main), isize_ty, i8pp_ty, i8_ty], isize_ty);
|
||||||
|
(start_fn, start_ty, vec![rust_main, arg_argc, arg_argv, arg_sigpipe])
|
||||||
} else {
|
} else {
|
||||||
debug!("using user-defined start fn");
|
debug!("using user-defined start fn");
|
||||||
let start_ty = cx.type_func(&[isize_ty, i8pp_ty], isize_ty);
|
let start_ty = cx.type_func(&[isize_ty, i8pp_ty], isize_ty);
|
||||||
|
@ -519,6 +519,8 @@ declare_features! (
|
|||||||
/// Allows creation of instances of a struct by moving fields that have
|
/// Allows creation of instances of a struct by moving fields that have
|
||||||
/// not changed from prior instances of the same struct (RFC #2528)
|
/// not changed from prior instances of the same struct (RFC #2528)
|
||||||
(active, type_changing_struct_update, "1.58.0", Some(86555), None),
|
(active, type_changing_struct_update, "1.58.0", Some(86555), None),
|
||||||
|
/// Enables rustc to generate code that instructs libstd to NOT ignore SIGPIPE.
|
||||||
|
(active, unix_sigpipe, "CURRENT_RUSTC_VERSION", Some(97889), None),
|
||||||
/// Allows unsized fn parameters.
|
/// Allows unsized fn parameters.
|
||||||
(active, unsized_fn_params, "1.49.0", Some(48055), None),
|
(active, unsized_fn_params, "1.49.0", Some(48055), None),
|
||||||
/// Allows unsized rvalues at arguments and parameters.
|
/// Allows unsized rvalues at arguments and parameters.
|
||||||
|
@ -359,6 +359,7 @@ pub const BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
|
|||||||
),
|
),
|
||||||
|
|
||||||
// Entry point:
|
// Entry point:
|
||||||
|
gated!(unix_sigpipe, Normal, template!(Word, NameValueStr: "inherit|sig_ign|sig_dfl"), ErrorFollowing, experimental!(unix_sigpipe)),
|
||||||
ungated!(start, Normal, template!(Word), WarnFollowing),
|
ungated!(start, Normal, template!(Word), WarnFollowing),
|
||||||
ungated!(no_start, CrateLevel, template!(Word), WarnFollowing),
|
ungated!(no_start, CrateLevel, template!(Word), WarnFollowing),
|
||||||
ungated!(no_main, CrateLevel, template!(Word), WarnFollowing),
|
ungated!(no_main, CrateLevel, template!(Word), WarnFollowing),
|
||||||
|
@ -1315,7 +1315,7 @@ impl<'v> RootCollector<'_, 'v> {
|
|||||||
/// the return type of `main`. This is not needed when
|
/// the return type of `main`. This is not needed when
|
||||||
/// the user writes their own `start` manually.
|
/// the user writes their own `start` manually.
|
||||||
fn push_extra_entry_roots(&mut self) {
|
fn push_extra_entry_roots(&mut self) {
|
||||||
let Some((main_def_id, EntryFnType::Main)) = self.entry_fn else {
|
let Some((main_def_id, EntryFnType::Main { .. })) = self.entry_fn else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -2117,6 +2117,7 @@ fn check_invalid_crate_level_attr(tcx: TyCtxt<'_>, attrs: &[Attribute]) {
|
|||||||
sym::automatically_derived,
|
sym::automatically_derived,
|
||||||
sym::start,
|
sym::start,
|
||||||
sym::rustc_main,
|
sym::rustc_main,
|
||||||
|
sym::unix_sigpipe,
|
||||||
sym::derive,
|
sym::derive,
|
||||||
sym::test,
|
sym::test,
|
||||||
sym::test_case,
|
sym::test_case,
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use rustc_ast::{entry::EntryPointType, Attribute};
|
use rustc_ast::entry::EntryPointType;
|
||||||
use rustc_errors::struct_span_err;
|
use rustc_errors::struct_span_err;
|
||||||
use rustc_hir::def::DefKind;
|
use rustc_hir::def::DefKind;
|
||||||
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
|
use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
|
||||||
use rustc_hir::{ItemId, Node, CRATE_HIR_ID};
|
use rustc_hir::{ItemId, Node, CRATE_HIR_ID};
|
||||||
use rustc_middle::ty::query::Providers;
|
use rustc_middle::ty::query::Providers;
|
||||||
use rustc_middle::ty::{DefIdTree, TyCtxt};
|
use rustc_middle::ty::{DefIdTree, TyCtxt};
|
||||||
use rustc_session::config::{CrateType, EntryFnType};
|
use rustc_session::config::{sigpipe, CrateType, EntryFnType};
|
||||||
use rustc_session::parse::feature_err;
|
use rustc_session::parse::feature_err;
|
||||||
use rustc_span::symbol::sym;
|
use rustc_span::symbol::sym;
|
||||||
use rustc_span::{Span, Symbol, DUMMY_SP};
|
use rustc_span::{Span, Symbol, DUMMY_SP};
|
||||||
@ -71,14 +71,12 @@ fn entry_point_type(ctxt: &EntryContext<'_>, id: ItemId, at_root: bool) -> Entry
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn err_if_attr_found(ctxt: &EntryContext<'_>, attrs: &[Attribute], sym: Symbol) {
|
fn err_if_attr_found(ctxt: &EntryContext<'_>, id: ItemId, sym: Symbol, details: &str) {
|
||||||
|
let attrs = ctxt.tcx.hir().attrs(id.hir_id());
|
||||||
if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym) {
|
if let Some(attr) = ctxt.tcx.sess.find_by_name(attrs, sym) {
|
||||||
ctxt.tcx
|
ctxt.tcx
|
||||||
.sess
|
.sess
|
||||||
.struct_span_err(
|
.struct_span_err(attr.span, &format!("`{}` attribute {}", sym, details))
|
||||||
attr.span,
|
|
||||||
&format!("`{}` attribute can only be used on functions", sym),
|
|
||||||
)
|
|
||||||
.emit();
|
.emit();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -87,14 +85,16 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
|
|||||||
let at_root = ctxt.tcx.opt_local_parent(id.def_id) == Some(CRATE_DEF_ID);
|
let at_root = ctxt.tcx.opt_local_parent(id.def_id) == Some(CRATE_DEF_ID);
|
||||||
|
|
||||||
match entry_point_type(ctxt, id, at_root) {
|
match entry_point_type(ctxt, id, at_root) {
|
||||||
EntryPointType::None => (),
|
EntryPointType::None => {
|
||||||
|
err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on `fn main()`");
|
||||||
|
}
|
||||||
_ if !matches!(ctxt.tcx.def_kind(id.def_id), DefKind::Fn) => {
|
_ if !matches!(ctxt.tcx.def_kind(id.def_id), DefKind::Fn) => {
|
||||||
let attrs = ctxt.tcx.hir().attrs(id.hir_id());
|
err_if_attr_found(ctxt, id, sym::start, "can only be used on functions");
|
||||||
err_if_attr_found(ctxt, attrs, sym::start);
|
err_if_attr_found(ctxt, id, sym::rustc_main, "can only be used on functions");
|
||||||
err_if_attr_found(ctxt, attrs, sym::rustc_main);
|
|
||||||
}
|
}
|
||||||
EntryPointType::MainNamed => (),
|
EntryPointType::MainNamed => (),
|
||||||
EntryPointType::OtherMain => {
|
EntryPointType::OtherMain => {
|
||||||
|
err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on root `fn main()`");
|
||||||
ctxt.non_main_fns.push(ctxt.tcx.def_span(id.def_id));
|
ctxt.non_main_fns.push(ctxt.tcx.def_span(id.def_id));
|
||||||
}
|
}
|
||||||
EntryPointType::RustcMainAttr => {
|
EntryPointType::RustcMainAttr => {
|
||||||
@ -116,6 +116,7 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
EntryPointType::Start => {
|
EntryPointType::Start => {
|
||||||
|
err_if_attr_found(ctxt, id, sym::unix_sigpipe, "can only be used on `fn main()`");
|
||||||
if ctxt.start_fn.is_none() {
|
if ctxt.start_fn.is_none() {
|
||||||
ctxt.start_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id())));
|
ctxt.start_fn = Some((id.def_id, ctxt.tcx.def_span(id.def_id.to_def_id())));
|
||||||
} else {
|
} else {
|
||||||
@ -136,8 +137,9 @@ fn find_item(id: ItemId, ctxt: &mut EntryContext<'_>) {
|
|||||||
fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> {
|
fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId, EntryFnType)> {
|
||||||
if let Some((def_id, _)) = visitor.start_fn {
|
if let Some((def_id, _)) = visitor.start_fn {
|
||||||
Some((def_id.to_def_id(), EntryFnType::Start))
|
Some((def_id.to_def_id(), EntryFnType::Start))
|
||||||
} else if let Some((def_id, _)) = visitor.attr_main_fn {
|
} else if let Some((local_def_id, _)) = visitor.attr_main_fn {
|
||||||
Some((def_id.to_def_id(), EntryFnType::Main))
|
let def_id = local_def_id.to_def_id();
|
||||||
|
Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) }))
|
||||||
} else {
|
} else {
|
||||||
if let Some(main_def) = tcx.resolutions(()).main_def && let Some(def_id) = main_def.opt_fn_def_id() {
|
if let Some(main_def) = tcx.resolutions(()).main_def && let Some(def_id) = main_def.opt_fn_def_id() {
|
||||||
// non-local main imports are handled below
|
// non-local main imports are handled below
|
||||||
@ -161,13 +163,39 @@ fn configure_main(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) -> Option<(DefId,
|
|||||||
)
|
)
|
||||||
.emit();
|
.emit();
|
||||||
}
|
}
|
||||||
return Some((def_id, EntryFnType::Main));
|
return Some((def_id, EntryFnType::Main { sigpipe: sigpipe(tcx, def_id) }));
|
||||||
}
|
}
|
||||||
no_main_err(tcx, visitor);
|
no_main_err(tcx, visitor);
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn sigpipe(tcx: TyCtxt<'_>, def_id: DefId) -> u8 {
|
||||||
|
if let Some(attr) = tcx.get_attr(def_id, sym::unix_sigpipe) {
|
||||||
|
match (attr.value_str(), attr.meta_item_list()) {
|
||||||
|
(Some(sym::inherit), None) => sigpipe::INHERIT,
|
||||||
|
(Some(sym::sig_ign), None) => sigpipe::SIG_IGN,
|
||||||
|
(Some(sym::sig_dfl), None) => sigpipe::SIG_DFL,
|
||||||
|
(_, Some(_)) => {
|
||||||
|
// Keep going so that `fn emit_malformed_attribute()` can print
|
||||||
|
// an excellent error message
|
||||||
|
sigpipe::DEFAULT
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
tcx.sess
|
||||||
|
.struct_span_err(
|
||||||
|
attr.span,
|
||||||
|
"valid values for `#[unix_sigpipe = \"...\"]` are `inherit`, `sig_ign`, or `sig_dfl`",
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
sigpipe::DEFAULT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
sigpipe::DEFAULT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) {
|
fn no_main_err(tcx: TyCtxt<'_>, visitor: &EntryContext<'_>) {
|
||||||
let sp = tcx.def_span(CRATE_DEF_ID);
|
let sp = tcx.def_span(CRATE_DEF_ID);
|
||||||
if *tcx.sess.parse_sess.reached_eof.borrow() {
|
if *tcx.sess.parse_sess.reached_eof.borrow() {
|
||||||
|
@ -36,6 +36,8 @@ use std::iter::{self, FromIterator};
|
|||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::str::{self, FromStr};
|
use std::str::{self, FromStr};
|
||||||
|
|
||||||
|
pub mod sigpipe;
|
||||||
|
|
||||||
/// The different settings that the `-C strip` flag can have.
|
/// The different settings that the `-C strip` flag can have.
|
||||||
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
|
#[derive(Clone, Copy, PartialEq, Hash, Debug)]
|
||||||
pub enum Strip {
|
pub enum Strip {
|
||||||
@ -798,7 +800,15 @@ impl UnstableOptions {
|
|||||||
// The type of entry function, so users can have their own entry functions
|
// The type of entry function, so users can have their own entry functions
|
||||||
#[derive(Copy, Clone, PartialEq, Hash, Debug, HashStable_Generic)]
|
#[derive(Copy, Clone, PartialEq, Hash, Debug, HashStable_Generic)]
|
||||||
pub enum EntryFnType {
|
pub enum EntryFnType {
|
||||||
Main,
|
Main {
|
||||||
|
/// Specifies what to do with `SIGPIPE` before calling `fn main()`.
|
||||||
|
///
|
||||||
|
/// What values that are valid and what they mean must be in sync
|
||||||
|
/// across rustc and libstd, but we don't want it public in libstd,
|
||||||
|
/// so we take a bit of an unusual approach with simple constants
|
||||||
|
/// and an `include!()`.
|
||||||
|
sigpipe: u8,
|
||||||
|
},
|
||||||
Start,
|
Start,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
20
compiler/rustc_session/src/config/sigpipe.rs
Normal file
20
compiler/rustc_session/src/config/sigpipe.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/// Do not touch `SIGPIPE`. Use whatever the parent process uses.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub const INHERIT: u8 = 1;
|
||||||
|
|
||||||
|
/// Change `SIGPIPE` to `SIG_IGN` so that failed writes results in `EPIPE`
|
||||||
|
/// that are eventually converted to `ErrorKind::BrokenPipe`.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub const SIG_IGN: u8 = 2;
|
||||||
|
|
||||||
|
/// Change `SIGPIPE` to `SIG_DFL` so that the process is killed when trying
|
||||||
|
/// to write to a closed pipe. This is usually the desired behavior for CLI
|
||||||
|
/// apps that produce textual output that you want to pipe to other programs
|
||||||
|
/// such as `head -n 1`.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub const SIG_DFL: u8 = 3;
|
||||||
|
|
||||||
|
/// `SIG_IGN` has been the Rust default since 2014. See
|
||||||
|
/// <https://github.com/rust-lang/rust/issues/62569>.
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub const DEFAULT: u8 = SIG_IGN;
|
@ -819,6 +819,7 @@ symbols! {
|
|||||||
infer_outlives_requirements,
|
infer_outlives_requirements,
|
||||||
infer_static_outlives_requirements,
|
infer_static_outlives_requirements,
|
||||||
inherent_associated_types,
|
inherent_associated_types,
|
||||||
|
inherit,
|
||||||
inlateout,
|
inlateout,
|
||||||
inline,
|
inline,
|
||||||
inline_const,
|
inline_const,
|
||||||
@ -1301,6 +1302,8 @@ symbols! {
|
|||||||
should_panic,
|
should_panic,
|
||||||
shr,
|
shr,
|
||||||
shr_assign,
|
shr_assign,
|
||||||
|
sig_dfl,
|
||||||
|
sig_ign,
|
||||||
simd,
|
simd,
|
||||||
simd_add,
|
simd_add,
|
||||||
simd_and,
|
simd_and,
|
||||||
@ -1519,6 +1522,7 @@ symbols! {
|
|||||||
unit,
|
unit,
|
||||||
universal_impl_trait,
|
universal_impl_trait,
|
||||||
unix,
|
unix,
|
||||||
|
unix_sigpipe,
|
||||||
unlikely,
|
unlikely,
|
||||||
unmarked_api,
|
unmarked_api,
|
||||||
unpin,
|
unpin,
|
||||||
|
@ -443,7 +443,7 @@ fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
|
|||||||
|
|
||||||
fn check_for_entry_fn(tcx: TyCtxt<'_>) {
|
fn check_for_entry_fn(tcx: TyCtxt<'_>) {
|
||||||
match tcx.entry_fn(()) {
|
match tcx.entry_fn(()) {
|
||||||
Some((def_id, EntryFnType::Main)) => check_main_fn_ty(tcx, def_id),
|
Some((def_id, EntryFnType::Main { .. })) => check_main_fn_ty(tcx, def_id),
|
||||||
Some((def_id, EntryFnType::Start)) => check_start_fn_ty(tcx, def_id),
|
Some((def_id, EntryFnType::Start)) => check_start_fn_ty(tcx, def_id),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
@ -73,9 +73,9 @@ macro_rules! rtunwrap {
|
|||||||
// SAFETY: must be called only once during runtime initialization.
|
// SAFETY: must be called only once during runtime initialization.
|
||||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||||
#[cfg_attr(test, allow(dead_code))]
|
#[cfg_attr(test, allow(dead_code))]
|
||||||
unsafe fn init(argc: isize, argv: *const *const u8) {
|
unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
|
||||||
unsafe {
|
unsafe {
|
||||||
sys::init(argc, argv);
|
sys::init(argc, argv, sigpipe);
|
||||||
|
|
||||||
let main_guard = sys::thread::guard::init();
|
let main_guard = sys::thread::guard::init();
|
||||||
// Next, set up the current Thread with the guard information we just
|
// Next, set up the current Thread with the guard information we just
|
||||||
@ -107,6 +107,7 @@ fn lang_start_internal(
|
|||||||
main: &(dyn Fn() -> i32 + Sync + crate::panic::RefUnwindSafe),
|
main: &(dyn Fn() -> i32 + Sync + crate::panic::RefUnwindSafe),
|
||||||
argc: isize,
|
argc: isize,
|
||||||
argv: *const *const u8,
|
argv: *const *const u8,
|
||||||
|
sigpipe: u8,
|
||||||
) -> Result<isize, !> {
|
) -> Result<isize, !> {
|
||||||
use crate::{mem, panic};
|
use crate::{mem, panic};
|
||||||
let rt_abort = move |e| {
|
let rt_abort = move |e| {
|
||||||
@ -124,7 +125,7 @@ fn lang_start_internal(
|
|||||||
// prevent libstd from accidentally introducing a panic to these functions. Another is from
|
// prevent libstd from accidentally introducing a panic to these functions. Another is from
|
||||||
// user code from `main` or, more nefariously, as described in e.g. issue #86030.
|
// user code from `main` or, more nefariously, as described in e.g. issue #86030.
|
||||||
// SAFETY: Only called once during runtime initialization.
|
// SAFETY: Only called once during runtime initialization.
|
||||||
panic::catch_unwind(move || unsafe { init(argc, argv) }).map_err(rt_abort)?;
|
panic::catch_unwind(move || unsafe { init(argc, argv, sigpipe) }).map_err(rt_abort)?;
|
||||||
let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
|
let ret_code = panic::catch_unwind(move || panic::catch_unwind(main).unwrap_or(101) as isize)
|
||||||
.map_err(move |e| {
|
.map_err(move |e| {
|
||||||
mem::forget(e);
|
mem::forget(e);
|
||||||
@ -140,11 +141,16 @@ fn lang_start<T: crate::process::Termination + 'static>(
|
|||||||
main: fn() -> T,
|
main: fn() -> T,
|
||||||
argc: isize,
|
argc: isize,
|
||||||
argv: *const *const u8,
|
argv: *const *const u8,
|
||||||
|
#[cfg(not(bootstrap))] sigpipe: u8,
|
||||||
) -> isize {
|
) -> isize {
|
||||||
let Ok(v) = lang_start_internal(
|
let Ok(v) = lang_start_internal(
|
||||||
&move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report().to_i32(),
|
&move || crate::sys_common::backtrace::__rust_begin_short_backtrace(main).report().to_i32(),
|
||||||
argc,
|
argc,
|
||||||
argv,
|
argv,
|
||||||
|
#[cfg(bootstrap)]
|
||||||
|
2, // Temporary inlining of sigpipe::DEFAULT until bootstrap stops being special
|
||||||
|
#[cfg(not(bootstrap))]
|
||||||
|
sigpipe,
|
||||||
);
|
);
|
||||||
v
|
v
|
||||||
}
|
}
|
||||||
|
@ -98,7 +98,7 @@ pub extern "C" fn __rust_abort() {
|
|||||||
|
|
||||||
// SAFETY: must be called only once during runtime initialization.
|
// SAFETY: must be called only once during runtime initialization.
|
||||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||||
pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {
|
||||||
let _ = net::init();
|
let _ = net::init();
|
||||||
args::init(argc, argv);
|
args::init(argc, argv);
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ pub mod locks {
|
|||||||
|
|
||||||
// SAFETY: must be called only once during runtime initialization.
|
// SAFETY: must be called only once during runtime initialization.
|
||||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||||
pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
pub unsafe fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {
|
||||||
unsafe {
|
unsafe {
|
||||||
args::init(argc, argv);
|
args::init(argc, argv);
|
||||||
}
|
}
|
||||||
|
@ -56,7 +56,7 @@ pub mod locks {
|
|||||||
|
|
||||||
// SAFETY: must be called only once during runtime initialization.
|
// SAFETY: must be called only once during runtime initialization.
|
||||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||||
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
|
pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
|
||||||
|
|
||||||
// SAFETY: must be called only once during runtime cleanup.
|
// SAFETY: must be called only once during runtime cleanup.
|
||||||
pub unsafe fn cleanup() {}
|
pub unsafe fn cleanup() {}
|
||||||
|
@ -44,12 +44,12 @@ pub mod thread_parker;
|
|||||||
pub mod time;
|
pub mod time;
|
||||||
|
|
||||||
#[cfg(target_os = "espidf")]
|
#[cfg(target_os = "espidf")]
|
||||||
pub fn init(argc: isize, argv: *const *const u8) {}
|
pub fn init(argc: isize, argv: *const *const u8, _sigpipe: u8) {}
|
||||||
|
|
||||||
#[cfg(not(target_os = "espidf"))]
|
#[cfg(not(target_os = "espidf"))]
|
||||||
// SAFETY: must be called only once during runtime initialization.
|
// SAFETY: must be called only once during runtime initialization.
|
||||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||||
pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) {
|
||||||
// The standard streams might be closed on application startup. To prevent
|
// The standard streams might be closed on application startup. To prevent
|
||||||
// std::io::{stdin, stdout,stderr} objects from using other unrelated file
|
// std::io::{stdin, stdout,stderr} objects from using other unrelated file
|
||||||
// resources opened later, we reopen standards streams when they are closed.
|
// resources opened later, we reopen standards streams when they are closed.
|
||||||
@ -61,8 +61,9 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
|||||||
// want!
|
// want!
|
||||||
//
|
//
|
||||||
// Hence, we set SIGPIPE to ignore when the program starts up in order
|
// Hence, we set SIGPIPE to ignore when the program starts up in order
|
||||||
// to prevent this problem.
|
// to prevent this problem. Add `#[unix_sigpipe = "..."]` above `fn main()` to
|
||||||
reset_sigpipe();
|
// alter this behavior.
|
||||||
|
reset_sigpipe(sigpipe);
|
||||||
|
|
||||||
stack_overflow::init();
|
stack_overflow::init();
|
||||||
args::init(argc, argv);
|
args::init(argc, argv);
|
||||||
@ -151,9 +152,25 @@ pub unsafe fn init(argc: isize, argv: *const *const u8) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe fn reset_sigpipe() {
|
unsafe fn reset_sigpipe(#[allow(unused_variables)] sigpipe: u8) {
|
||||||
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))]
|
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))]
|
||||||
rtassert!(signal(libc::SIGPIPE, libc::SIG_IGN) != libc::SIG_ERR);
|
{
|
||||||
|
// We don't want to add this as a public type to libstd, nor do we want to
|
||||||
|
// duplicate the code, so we choose to include this compiler file like this.
|
||||||
|
mod sigpipe {
|
||||||
|
include!("../../../../../compiler/rustc_session/src/config/sigpipe.rs");
|
||||||
|
}
|
||||||
|
|
||||||
|
let handler = match sigpipe {
|
||||||
|
sigpipe::INHERIT => None,
|
||||||
|
sigpipe::SIG_IGN => Some(libc::SIG_IGN),
|
||||||
|
sigpipe::SIG_DFL => Some(libc::SIG_DFL),
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
if let Some(handler) = handler {
|
||||||
|
rtassert!(signal(libc::SIGPIPE, handler) != libc::SIG_ERR);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ pub mod memchr {
|
|||||||
|
|
||||||
// SAFETY: must be called only once during runtime initialization.
|
// SAFETY: must be called only once during runtime initialization.
|
||||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||||
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {}
|
pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {}
|
||||||
|
|
||||||
// SAFETY: must be called only once during runtime cleanup.
|
// SAFETY: must be called only once during runtime cleanup.
|
||||||
// NOTE: this is not guaranteed to run, for example when the program aborts.
|
// NOTE: this is not guaranteed to run, for example when the program aborts.
|
||||||
|
@ -48,7 +48,7 @@ cfg_if::cfg_if! {
|
|||||||
|
|
||||||
// SAFETY: must be called only once during runtime initialization.
|
// SAFETY: must be called only once during runtime initialization.
|
||||||
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
// NOTE: this is not guaranteed to run, for example when Rust code is called externally.
|
||||||
pub unsafe fn init(_argc: isize, _argv: *const *const u8) {
|
pub unsafe fn init(_argc: isize, _argv: *const *const u8, _sigpipe: u8) {
|
||||||
stack_overflow::init();
|
stack_overflow::init();
|
||||||
|
|
||||||
// Normally, `thread::spawn` will call `Thread::set_name` but since this thread already
|
// Normally, `thread::spawn` will call `Thread::set_name` but since this thread already
|
||||||
|
54
src/doc/unstable-book/src/language-features/unix-sigpipe.md
Normal file
54
src/doc/unstable-book/src/language-features/unix-sigpipe.md
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# `unix_sigpipe`
|
||||||
|
|
||||||
|
The tracking issue for this feature is: [#97889]
|
||||||
|
|
||||||
|
[#97889]: https://github.com/rust-lang/rust/issues/97889
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
The `#[unix_sigpipe = "..."]` attribute on `fn main()` can be used to specify how libstd shall setup `SIGPIPE` on Unix platforms before invoking `fn main()`. This attribute is ignored on non-Unix targets. There are three variants:
|
||||||
|
* `#[unix_sigpipe = "inherit"]`
|
||||||
|
* `#[unix_sigpipe = "sig_dfl"]`
|
||||||
|
* `#[unix_sigpipe = "sig_ign"]`
|
||||||
|
|
||||||
|
## `#[unix_sigpipe = "inherit"]`
|
||||||
|
|
||||||
|
Leave `SIGPIPE` untouched before entering `fn main()`. Unless the parent process has changed the default `SIGPIPE` handler from `SIG_DFL` to something else, this will behave the same as `#[unix_sigpipe = "sig_dfl"]`.
|
||||||
|
|
||||||
|
## `#[unix_sigpipe = "sig_dfl"]`
|
||||||
|
|
||||||
|
Set the `SIGPIPE` handler to `SIG_DFL`. This will result in your program getting killed if it tries to write to a closed pipe. This is normally what you want if your program produces textual output.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
#[unix_sigpipe = "sig_dfl"]
|
||||||
|
fn main() { loop { println!("hello world"); } }
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
% ./main | head -n 1
|
||||||
|
hello world
|
||||||
|
```
|
||||||
|
|
||||||
|
## `#[unix_sigpipe = "sig_ign"]`
|
||||||
|
|
||||||
|
Set the `SIGPIPE` handler to `SIG_IGN` before invoking `fn main()`. This will result in `ErrorKind::BrokenPipe` errors if you program tries to write to a closed pipe. This is normally what you want if you for example write socket servers, socket clients, or pipe peers.
|
||||||
|
|
||||||
|
This is what libstd has done by default since 2014. Omitting `#[unix_sigpipe = "..."]` is the same as having `#[unix_sigpipe = "sig_ign"]`.
|
||||||
|
|
||||||
|
### Example
|
||||||
|
|
||||||
|
```rust,no_run
|
||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
#[unix_sigpipe = "sig_ign"]
|
||||||
|
fn main() { loop { println!("hello world"); } }
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
% ./main | head -n 1
|
||||||
|
hello world
|
||||||
|
thread 'main' panicked at 'failed printing to stdout: Broken pipe (os error 32)', library/std/src/io/stdio.rs:1016:9
|
||||||
|
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||||
|
```
|
@ -0,0 +1,26 @@
|
|||||||
|
#![feature(rustc_private)]
|
||||||
|
extern crate libc;
|
||||||
|
|
||||||
|
/// So tests don't have to bring libc in scope themselves
|
||||||
|
pub enum SignalHandler {
|
||||||
|
Ignore,
|
||||||
|
Default,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper to assert that [`libc::SIGPIPE`] has the expected signal handler.
|
||||||
|
pub fn assert_sigpipe_handler(expected_handler: SignalHandler) {
|
||||||
|
#[cfg(unix)]
|
||||||
|
#[cfg(not(any(target_os = "emscripten", target_os = "fuchsia", target_os = "horizon")))]
|
||||||
|
{
|
||||||
|
let prev = unsafe { libc::signal(libc::SIGPIPE, libc::SIG_IGN) };
|
||||||
|
|
||||||
|
let expected = match expected_handler {
|
||||||
|
SignalHandler::Ignore => libc::SIG_IGN,
|
||||||
|
SignalHandler::Default => libc::SIG_DFL,
|
||||||
|
};
|
||||||
|
assert_eq!(prev, expected);
|
||||||
|
|
||||||
|
// Unlikely to matter, but restore the old value anyway
|
||||||
|
unsafe { libc::signal(libc::SIGPIPE, prev); };
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
#![unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute cannot be used at crate level
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,13 @@
|
|||||||
|
error: `unix_sigpipe` attribute cannot be used at crate level
|
||||||
|
--> $DIR/unix_sigpipe-crate.rs:2:1
|
||||||
|
|
|
||||||
|
LL | #![unix_sigpipe = "inherit"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: perhaps you meant to use an outer attribute
|
||||||
|
|
|
||||||
|
LL | #[unix_sigpipe = "inherit"]
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
@ -0,0 +1,5 @@
|
|||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
|
||||||
|
#[unix_sigpipe = "sig_ign"]
|
||||||
|
#[unix_sigpipe = "inherit"] //~ error: multiple `unix_sigpipe` attributes
|
||||||
|
fn main() {}
|
@ -0,0 +1,14 @@
|
|||||||
|
error: multiple `unix_sigpipe` attributes
|
||||||
|
--> $DIR/unix_sigpipe-duplicates.rs:4:1
|
||||||
|
|
|
||||||
|
LL | #[unix_sigpipe = "inherit"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute
|
||||||
|
|
|
||||||
|
note: attribute also specified here
|
||||||
|
--> $DIR/unix_sigpipe-duplicates.rs:3:1
|
||||||
|
|
|
||||||
|
LL | #[unix_sigpipe = "sig_ign"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
13
src/test/ui/attributes/unix_sigpipe/unix_sigpipe-error.rs
Normal file
13
src/test/ui/attributes/unix_sigpipe/unix_sigpipe-error.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// run-pass
|
||||||
|
// aux-build:sigpipe-utils.rs
|
||||||
|
|
||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
|
||||||
|
#[unix_sigpipe = "sig_ign"]
|
||||||
|
fn main() {
|
||||||
|
extern crate sigpipe_utils;
|
||||||
|
|
||||||
|
// #[unix_sigpipe = "sig_ign"] is active, so the legacy behavior of ignoring
|
||||||
|
// SIGPIPE shall be in effect
|
||||||
|
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore);
|
||||||
|
}
|
14
src/test/ui/attributes/unix_sigpipe/unix_sigpipe-inherit.rs
Normal file
14
src/test/ui/attributes/unix_sigpipe/unix_sigpipe-inherit.rs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// run-pass
|
||||||
|
// aux-build:sigpipe-utils.rs
|
||||||
|
|
||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
|
||||||
|
#[unix_sigpipe = "inherit"]
|
||||||
|
fn main() {
|
||||||
|
extern crate sigpipe_utils;
|
||||||
|
|
||||||
|
// #[unix_sigpipe = "inherit"] is active, so SIGPIPE shall NOT be ignored,
|
||||||
|
// instead the default handler shall be installed. (We assume that the
|
||||||
|
// process that runs these tests have the default handler.)
|
||||||
|
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default);
|
||||||
|
}
|
4
src/test/ui/attributes/unix_sigpipe/unix_sigpipe-list.rs
Normal file
4
src/test/ui/attributes/unix_sigpipe/unix_sigpipe-list.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
|
||||||
|
#[unix_sigpipe(inherit)] //~ error: malformed `unix_sigpipe` attribute input
|
||||||
|
fn main() {}
|
15
src/test/ui/attributes/unix_sigpipe/unix_sigpipe-list.stderr
Normal file
15
src/test/ui/attributes/unix_sigpipe/unix_sigpipe-list.stderr
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
error: malformed `unix_sigpipe` attribute input
|
||||||
|
--> $DIR/unix_sigpipe-list.rs:3:1
|
||||||
|
|
|
||||||
|
LL | #[unix_sigpipe(inherit)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
help: the following are the possible correct uses
|
||||||
|
|
|
||||||
|
LL | #[unix_sigpipe = "inherit|sig_ign|sig_dfl"]
|
||||||
|
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
LL | #[unix_sigpipe]
|
||||||
|
| ~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
@ -0,0 +1,6 @@
|
|||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
|
||||||
|
#[unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute can only be used on `fn main()`
|
||||||
|
fn f() {}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,8 @@
|
|||||||
|
error: `unix_sigpipe` attribute can only be used on `fn main()`
|
||||||
|
--> $DIR/unix_sigpipe-non-main-fn.rs:3:1
|
||||||
|
|
|
||||||
|
LL | #[unix_sigpipe = "inherit"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
@ -0,0 +1,8 @@
|
|||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
|
||||||
|
mod m {
|
||||||
|
#[unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute can only be used on root `fn main()`
|
||||||
|
fn main() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,8 @@
|
|||||||
|
error: `unix_sigpipe` attribute can only be used on root `fn main()`
|
||||||
|
--> $DIR/unix_sigpipe-non-root-main.rs:4:5
|
||||||
|
|
|
||||||
|
LL | #[unix_sigpipe = "inherit"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
@ -0,0 +1,9 @@
|
|||||||
|
// run-pass
|
||||||
|
// aux-build:sigpipe-utils.rs
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
extern crate sigpipe_utils;
|
||||||
|
|
||||||
|
// SIGPIPE shall be ignored since #[unix_sigpipe = "..."] is not used
|
||||||
|
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore);
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
// run-pass
|
||||||
|
// aux-build:sigpipe-utils.rs
|
||||||
|
|
||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
extern crate sigpipe_utils;
|
||||||
|
|
||||||
|
// Only #![feature(unix_sigpipe)] is enabled, not #[unix_sigpipe = "..."].
|
||||||
|
// This shall not change any behavior, so we still expect SIGPIPE to be
|
||||||
|
// ignored
|
||||||
|
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Ignore);
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
// run-pass
|
||||||
|
// aux-build:sigpipe-utils.rs
|
||||||
|
|
||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
#![feature(rustc_attrs)]
|
||||||
|
|
||||||
|
#[unix_sigpipe = "sig_dfl"]
|
||||||
|
#[rustc_main]
|
||||||
|
fn rustc_main() {
|
||||||
|
extern crate sigpipe_utils;
|
||||||
|
|
||||||
|
// #[unix_sigpipe = "sig_dfl"] is active, so SIGPIPE handler shall be
|
||||||
|
// SIG_DFL. Note that we have a #[rustc_main], but it should still work.
|
||||||
|
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default);
|
||||||
|
}
|
13
src/test/ui/attributes/unix_sigpipe/unix_sigpipe-sig_dfl.rs
Normal file
13
src/test/ui/attributes/unix_sigpipe/unix_sigpipe-sig_dfl.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
// run-pass
|
||||||
|
// aux-build:sigpipe-utils.rs
|
||||||
|
|
||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
|
||||||
|
#[unix_sigpipe = "sig_dfl"]
|
||||||
|
fn main() {
|
||||||
|
extern crate sigpipe_utils;
|
||||||
|
|
||||||
|
// #[unix_sigpipe = "sig_dfl"] is active, so SIGPIPE shall NOT be ignored, instead
|
||||||
|
// the default handler shall be installed
|
||||||
|
sigpipe_utils::assert_sigpipe_handler(sigpipe_utils::SignalHandler::Default);
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
#![feature(start)]
|
||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
|
||||||
|
#[start]
|
||||||
|
#[unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute can only be used on `fn main()`
|
||||||
|
fn custom_start(argc: isize, argv: *const *const u8) -> isize { 0 }
|
@ -0,0 +1,8 @@
|
|||||||
|
error: `unix_sigpipe` attribute can only be used on `fn main()`
|
||||||
|
--> $DIR/unix_sigpipe-start.rs:5:1
|
||||||
|
|
|
||||||
|
LL | #[unix_sigpipe = "inherit"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
@ -0,0 +1,6 @@
|
|||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
|
||||||
|
#[unix_sigpipe = "inherit"] //~ error: `unix_sigpipe` attribute can only be used on `fn main()`
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
fn main() {}
|
@ -0,0 +1,8 @@
|
|||||||
|
error: `unix_sigpipe` attribute can only be used on `fn main()`
|
||||||
|
--> $DIR/unix_sigpipe-struct.rs:3:1
|
||||||
|
|
|
||||||
|
LL | #[unix_sigpipe = "inherit"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
@ -0,0 +1,4 @@
|
|||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
|
||||||
|
#[unix_sigpipe = "wrong"] //~ error: valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl`
|
||||||
|
fn main() {}
|
@ -0,0 +1,8 @@
|
|||||||
|
error: valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl`
|
||||||
|
--> $DIR/unix_sigpipe-wrong.rs:3:1
|
||||||
|
|
|
||||||
|
LL | #[unix_sigpipe = "wrong"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
4
src/test/ui/attributes/unix_sigpipe/unix_sigpipe.rs
Normal file
4
src/test/ui/attributes/unix_sigpipe/unix_sigpipe.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#![feature(unix_sigpipe)]
|
||||||
|
|
||||||
|
#[unix_sigpipe] //~ error: valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl`
|
||||||
|
fn main() {}
|
8
src/test/ui/attributes/unix_sigpipe/unix_sigpipe.stderr
Normal file
8
src/test/ui/attributes/unix_sigpipe/unix_sigpipe.stderr
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
error: valid values for `#[unix_sigpipe = "..."]` are `inherit`, `sig_ign`, or `sig_dfl`
|
||||||
|
--> $DIR/unix_sigpipe.rs:3:1
|
||||||
|
|
|
||||||
|
LL | #[unix_sigpipe]
|
||||||
|
| ^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
4
src/test/ui/feature-gates/feature-gate-unix_sigpipe.rs
Normal file
4
src/test/ui/feature-gates/feature-gate-unix_sigpipe.rs
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
#![crate_type = "bin"]
|
||||||
|
|
||||||
|
#[unix_sigpipe = "inherit"] //~ the `#[unix_sigpipe]` attribute is an experimental feature
|
||||||
|
fn main () {}
|
12
src/test/ui/feature-gates/feature-gate-unix_sigpipe.stderr
Normal file
12
src/test/ui/feature-gates/feature-gate-unix_sigpipe.stderr
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
error[E0658]: the `#[unix_sigpipe]` attribute is an experimental feature
|
||||||
|
--> $DIR/feature-gate-unix_sigpipe.rs:3:1
|
||||||
|
|
|
||||||
|
LL | #[unix_sigpipe = "inherit"]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: see issue #97889 <https://github.com/rust-lang/rust/issues/97889> for more information
|
||||||
|
= help: add `#![feature(unix_sigpipe)]` to the crate attributes to enable
|
||||||
|
|
||||||
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0658`.
|
Loading…
x
Reference in New Issue
Block a user