Auto merge of #128469 - matthiaskrgr:rollup-00svite, r=matthiaskrgr
Rollup of 6 pull requests Successful merges: - #127567 (std: implement the `once_wait` feature) - #128162 (Cleanup sys module to match house style) - #128296 (Update target-spec metadata for loongarch64 targets) - #128443 (Properly mark loop as diverging if it has no breaks) - #128449 (Temporarily switch `ambiguous_negative_literals` lint to allow) - #128452 (derive(SmartPointer): require pointee to be maybe sized) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
71b2116096
@ -1,5 +1,3 @@
|
|||||||
use std::mem::swap;
|
|
||||||
|
|
||||||
use ast::ptr::P;
|
use ast::ptr::P;
|
||||||
use ast::HasAttrs;
|
use ast::HasAttrs;
|
||||||
use rustc_ast::mut_visit::MutVisitor;
|
use rustc_ast::mut_visit::MutVisitor;
|
||||||
@ -154,13 +152,28 @@ pub fn expand_deriving_smart_ptr(
|
|||||||
{
|
{
|
||||||
let pointee = &mut impl_generics.params[pointee_param_idx];
|
let pointee = &mut impl_generics.params[pointee_param_idx];
|
||||||
self_bounds = pointee.bounds.clone();
|
self_bounds = pointee.bounds.clone();
|
||||||
|
if !contains_maybe_sized_bound(&self_bounds)
|
||||||
|
&& !contains_maybe_sized_bound_on_pointee(
|
||||||
|
&generics.where_clause.predicates,
|
||||||
|
pointee_ty_ident.name,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
cx.dcx()
|
||||||
|
.struct_span_err(
|
||||||
|
pointee_ty_ident.span,
|
||||||
|
format!(
|
||||||
|
"`derive(SmartPointer)` requires {} to be marked `?Sized`",
|
||||||
|
pointee_ty_ident.name
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.emit();
|
||||||
|
return;
|
||||||
|
}
|
||||||
let arg = GenericArg::Type(s_ty.clone());
|
let arg = GenericArg::Type(s_ty.clone());
|
||||||
let unsize = cx.path_all(span, true, path!(span, core::marker::Unsize), vec![arg]);
|
let unsize = cx.path_all(span, true, path!(span, core::marker::Unsize), vec![arg]);
|
||||||
pointee.bounds.push(cx.trait_bound(unsize, false));
|
pointee.bounds.push(cx.trait_bound(unsize, false));
|
||||||
let mut attrs = thin_vec![];
|
|
||||||
swap(&mut pointee.attrs, &mut attrs);
|
|
||||||
// Drop `#[pointee]` attribute since it should not be recognized outside `derive(SmartPointer)`
|
// Drop `#[pointee]` attribute since it should not be recognized outside `derive(SmartPointer)`
|
||||||
pointee.attrs = attrs.into_iter().filter(|attr| !attr.has_name(sym::pointee)).collect();
|
pointee.attrs.retain(|attr| !attr.has_name(sym::pointee));
|
||||||
}
|
}
|
||||||
|
|
||||||
// # Rewrite generic parameter bounds
|
// # Rewrite generic parameter bounds
|
||||||
@ -169,14 +182,14 @@ pub fn expand_deriving_smart_ptr(
|
|||||||
// ```
|
// ```
|
||||||
// struct<
|
// struct<
|
||||||
// U: Trait<T>,
|
// U: Trait<T>,
|
||||||
// #[pointee] T: Trait<T>,
|
// #[pointee] T: Trait<T> + ?Sized,
|
||||||
// V: Trait<T>> ...
|
// V: Trait<T>> ...
|
||||||
// ```
|
// ```
|
||||||
// ... generates this `impl` generic parameters
|
// ... generates this `impl` generic parameters
|
||||||
// ```
|
// ```
|
||||||
// impl<
|
// impl<
|
||||||
// U: Trait<T> + Trait<__S>,
|
// U: Trait<T> + Trait<__S>,
|
||||||
// T: Trait<T> + Unsize<__S>, // (**)
|
// T: Trait<T> + ?Sized + Unsize<__S>, // (**)
|
||||||
// __S: Trait<__S> + ?Sized, // (*)
|
// __S: Trait<__S> + ?Sized, // (*)
|
||||||
// V: Trait<T> + Trait<__S>> ...
|
// V: Trait<T> + Trait<__S>> ...
|
||||||
// ```
|
// ```
|
||||||
@ -218,23 +231,6 @@ pub fn expand_deriving_smart_ptr(
|
|||||||
//
|
//
|
||||||
// We now insert `__S` with the missing bounds marked with (*) above.
|
// We now insert `__S` with the missing bounds marked with (*) above.
|
||||||
// We should also write the bounds from `#[pointee]` to `__S` as required by `Unsize<__S>`.
|
// We should also write the bounds from `#[pointee]` to `__S` as required by `Unsize<__S>`.
|
||||||
let sized = cx.path_global(span, path!(span, core::marker::Sized));
|
|
||||||
// For some reason, we are not allowed to write `?Sized` bound twice like `__S: ?Sized + ?Sized`.
|
|
||||||
if !contains_maybe_sized_bound(&self_bounds)
|
|
||||||
&& !contains_maybe_sized_bound_on_pointee(
|
|
||||||
&generics.where_clause.predicates,
|
|
||||||
pointee_ty_ident.name,
|
|
||||||
)
|
|
||||||
{
|
|
||||||
self_bounds.push(GenericBound::Trait(
|
|
||||||
cx.poly_trait_ref(span, sized),
|
|
||||||
TraitBoundModifiers {
|
|
||||||
polarity: ast::BoundPolarity::Maybe(span),
|
|
||||||
constness: ast::BoundConstness::Never,
|
|
||||||
asyncness: ast::BoundAsyncness::Normal,
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
let mut substitution =
|
let mut substitution =
|
||||||
TypeSubstitution { from_name: pointee_ty_ident.name, to_ty: &s_ty, rewritten: false };
|
TypeSubstitution { from_name: pointee_ty_ident.name, to_ty: &s_ty, rewritten: false };
|
||||||
@ -252,7 +248,7 @@ pub fn expand_deriving_smart_ptr(
|
|||||||
// where
|
// where
|
||||||
// U: Trait<V> + Trait<T>,
|
// U: Trait<V> + Trait<T>,
|
||||||
// Companion<T>: Trait<T>,
|
// Companion<T>: Trait<T>,
|
||||||
// T: Trait<T>,
|
// T: Trait<T> + ?Sized,
|
||||||
// { .. }
|
// { .. }
|
||||||
// ```
|
// ```
|
||||||
// ... will have a impl prelude like so
|
// ... will have a impl prelude like so
|
||||||
@ -263,8 +259,8 @@ pub fn expand_deriving_smart_ptr(
|
|||||||
// U: Trait<__S>,
|
// U: Trait<__S>,
|
||||||
// Companion<T>: Trait<T>,
|
// Companion<T>: Trait<T>,
|
||||||
// Companion<__S>: Trait<__S>,
|
// Companion<__S>: Trait<__S>,
|
||||||
// T: Trait<T>,
|
// T: Trait<T> + ?Sized,
|
||||||
// __S: Trait<__S>,
|
// __S: Trait<__S> + ?Sized,
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// We should also write a few new `where` bounds from `#[pointee] T` to `__S`
|
// We should also write a few new `where` bounds from `#[pointee] T` to `__S`
|
||||||
|
@ -1306,6 +1306,8 @@ fn check_expr_loop(
|
|||||||
// No way to know whether it's diverging because
|
// No way to know whether it's diverging because
|
||||||
// of a `break` or an outer `break` or `return`.
|
// of a `break` or an outer `break` or `return`.
|
||||||
self.diverges.set(Diverges::Maybe);
|
self.diverges.set(Diverges::Maybe);
|
||||||
|
} else {
|
||||||
|
self.diverges.set(self.diverges.get() | Diverges::always(expr.span));
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we permit break with a value, then result type is
|
// If we permit break with a value, then result type is
|
||||||
|
@ -48,30 +48,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
/// Produces warning on the given node, if the current point in the
|
/// Produces warning on the given node, if the current point in the
|
||||||
/// function is unreachable, and there hasn't been another warning.
|
/// function is unreachable, and there hasn't been another warning.
|
||||||
pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) {
|
pub(crate) fn warn_if_unreachable(&self, id: HirId, span: Span, kind: &str) {
|
||||||
// FIXME: Combine these two 'if' expressions into one once
|
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
|
||||||
// let chains are implemented
|
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
|
||||||
if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() {
|
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
|
||||||
// If span arose from a desugaring of `if` or `while`, then it is the condition itself,
|
if span.is_desugaring(DesugaringKind::CondTemporary) {
|
||||||
// which diverges, that we are about to lint on. This gives suboptimal diagnostics.
|
return;
|
||||||
// Instead, stop here so that the `if`- or `while`-expression's block is linted instead.
|
|
||||||
if !span.is_desugaring(DesugaringKind::CondTemporary)
|
|
||||||
&& !span.is_desugaring(DesugaringKind::Async)
|
|
||||||
&& !orig_span.is_desugaring(DesugaringKind::Await)
|
|
||||||
{
|
|
||||||
self.diverges.set(Diverges::WarnedAlways);
|
|
||||||
|
|
||||||
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
|
|
||||||
|
|
||||||
let msg = format!("unreachable {kind}");
|
|
||||||
self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
|
|
||||||
lint.primary_message(msg.clone());
|
|
||||||
lint.span_label(span, msg).span_label(
|
|
||||||
orig_span,
|
|
||||||
custom_note.unwrap_or("any code following this expression is unreachable"),
|
|
||||||
);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't lint if the result of an async block or async function is `!`.
|
||||||
|
// This does not affect the unreachable lints *within* the body.
|
||||||
|
if span.is_desugaring(DesugaringKind::Async) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't lint *within* the `.await` operator, since that's all just desugaring junk.
|
||||||
|
// We only want to lint if there is a subsequent expression after the `.await`.
|
||||||
|
if span.is_desugaring(DesugaringKind::Await) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Don't warn twice.
|
||||||
|
self.diverges.set(Diverges::WarnedAlways);
|
||||||
|
|
||||||
|
debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind);
|
||||||
|
|
||||||
|
let msg = format!("unreachable {kind}");
|
||||||
|
self.tcx().node_span_lint(lint::builtin::UNREACHABLE_CODE, id, span, |lint| {
|
||||||
|
lint.primary_message(msg.clone());
|
||||||
|
lint.span_label(span, msg).span_label(
|
||||||
|
orig_span,
|
||||||
|
custom_note.unwrap_or("any code following this expression is unreachable"),
|
||||||
|
);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolves type and const variables in `ty` if possible. Unlike the infcx
|
/// Resolves type and const variables in `ty` if possible. Unlike the infcx
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
/// ### Example
|
/// ### Example
|
||||||
///
|
///
|
||||||
/// ```rust,compile_fail
|
/// ```rust,compile_fail
|
||||||
|
/// # #![deny(ambiguous_negative_literals)]
|
||||||
/// # #![allow(unused)]
|
/// # #![allow(unused)]
|
||||||
/// -1i32.abs(); // equals -1, while `(-1i32).abs()` equals 1
|
/// -1i32.abs(); // equals -1, while `(-1i32).abs()` equals 1
|
||||||
/// ```
|
/// ```
|
||||||
@ -27,7 +28,7 @@
|
|||||||
/// Method calls take precedence over unary precedence. Setting the
|
/// Method calls take precedence over unary precedence. Setting the
|
||||||
/// precedence explicitly makes the code clearer and avoid potential bugs.
|
/// precedence explicitly makes the code clearer and avoid potential bugs.
|
||||||
pub AMBIGUOUS_NEGATIVE_LITERALS,
|
pub AMBIGUOUS_NEGATIVE_LITERALS,
|
||||||
Deny,
|
Allow,
|
||||||
"ambiguous negative literals operations",
|
"ambiguous negative literals operations",
|
||||||
report_in_external_macro
|
report_in_external_macro
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,10 @@ pub fn target() -> Target {
|
|||||||
Target {
|
Target {
|
||||||
llvm_target: "loongarch64-unknown-linux-musl".into(),
|
llvm_target: "loongarch64-unknown-linux-musl".into(),
|
||||||
metadata: crate::spec::TargetMetadata {
|
metadata: crate::spec::TargetMetadata {
|
||||||
description: Some("LoongArch64 Linux (LP64D ABI) with musl 1.2.3".into()),
|
description: Some("LoongArch64 Linux (LP64D ABI) with musl 1.2.5".into()),
|
||||||
tier: Some(3),
|
tier: Some(2),
|
||||||
host_tools: Some(false),
|
host_tools: Some(true),
|
||||||
std: None, // ?
|
std: Some(true),
|
||||||
},
|
},
|
||||||
pointer_width: 64,
|
pointer_width: 64,
|
||||||
data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
|
data_layout: "e-m:e-p:64:64-i64:64-i128:128-n32:64-S128".into(),
|
||||||
|
@ -6,7 +6,7 @@ pub fn target() -> Target {
|
|||||||
Target {
|
Target {
|
||||||
llvm_target: "loongarch64-unknown-none".into(),
|
llvm_target: "loongarch64-unknown-none".into(),
|
||||||
metadata: crate::spec::TargetMetadata {
|
metadata: crate::spec::TargetMetadata {
|
||||||
description: None,
|
description: Some("Freestanding/bare-metal LoongArch64".into()),
|
||||||
tier: Some(2),
|
tier: Some(2),
|
||||||
host_tools: Some(false),
|
host_tools: Some(false),
|
||||||
std: Some(false),
|
std: Some(false),
|
||||||
|
@ -6,7 +6,7 @@ pub fn target() -> Target {
|
|||||||
Target {
|
Target {
|
||||||
llvm_target: "loongarch64-unknown-none".into(),
|
llvm_target: "loongarch64-unknown-none".into(),
|
||||||
metadata: crate::spec::TargetMetadata {
|
metadata: crate::spec::TargetMetadata {
|
||||||
description: None,
|
description: Some("Freestanding/bare-metal LoongArch64 softfloat".into()),
|
||||||
tier: Some(2),
|
tier: Some(2),
|
||||||
host_tools: Some(false),
|
host_tools: Some(false),
|
||||||
std: Some(false),
|
std: Some(false),
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
use crate::pipe::pipe;
|
use crate::pipe::pipe;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[cfg(all(windows, unix, not(miri)))]
|
||||||
fn pipe_creation_clone_and_rw() {
|
fn pipe_creation_clone_and_rw() {
|
||||||
let (rx, tx) = pipe().unwrap();
|
let (rx, tx) = pipe().unwrap();
|
||||||
|
|
@ -264,6 +264,47 @@ pub fn is_completed(&self) -> bool {
|
|||||||
self.inner.is_completed()
|
self.inner.is_completed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocks the current thread until initialization has completed.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rust
|
||||||
|
/// #![feature(once_wait)]
|
||||||
|
///
|
||||||
|
/// use std::sync::Once;
|
||||||
|
/// use std::thread;
|
||||||
|
///
|
||||||
|
/// static READY: Once = Once::new();
|
||||||
|
///
|
||||||
|
/// let thread = thread::spawn(|| {
|
||||||
|
/// READY.wait();
|
||||||
|
/// println!("everything is ready");
|
||||||
|
/// });
|
||||||
|
///
|
||||||
|
/// READY.call_once(|| println!("performing setup"));
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// If this [`Once`] has been poisoned because an initialization closure has
|
||||||
|
/// panicked, this method will also panic. Use [`wait_force`](Self::wait_force)
|
||||||
|
/// if this behaviour is not desired.
|
||||||
|
#[unstable(feature = "once_wait", issue = "127527")]
|
||||||
|
pub fn wait(&self) {
|
||||||
|
if !self.inner.is_completed() {
|
||||||
|
self.inner.wait(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Blocks the current thread until initialization has completed, ignoring
|
||||||
|
/// poisoning.
|
||||||
|
#[unstable(feature = "once_wait", issue = "127527")]
|
||||||
|
pub fn wait_force(&self) {
|
||||||
|
if !self.inner.is_completed() {
|
||||||
|
self.inner.wait(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the current state of the `Once` instance.
|
/// Returns the current state of the `Once` instance.
|
||||||
///
|
///
|
||||||
/// Since this takes a mutable reference, no initialization can currently
|
/// Since this takes a mutable reference, no initialization can currently
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use super::Once;
|
use super::Once;
|
||||||
|
use crate::sync::atomic::AtomicBool;
|
||||||
|
use crate::sync::atomic::Ordering::Relaxed;
|
||||||
use crate::sync::mpsc::channel;
|
use crate::sync::mpsc::channel;
|
||||||
|
use crate::time::Duration;
|
||||||
use crate::{panic, thread};
|
use crate::{panic, thread};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -113,3 +116,47 @@ fn wait_for_force_to_finish() {
|
|||||||
assert!(t1.join().is_ok());
|
assert!(t1.join().is_ok());
|
||||||
assert!(t2.join().is_ok());
|
assert!(t2.join().is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wait() {
|
||||||
|
for _ in 0..50 {
|
||||||
|
let val = AtomicBool::new(false);
|
||||||
|
let once = Once::new();
|
||||||
|
|
||||||
|
thread::scope(|s| {
|
||||||
|
for _ in 0..4 {
|
||||||
|
s.spawn(|| {
|
||||||
|
once.wait();
|
||||||
|
assert!(val.load(Relaxed));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
once.call_once(|| val.store(true, Relaxed));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wait_on_poisoned() {
|
||||||
|
let once = Once::new();
|
||||||
|
|
||||||
|
panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err();
|
||||||
|
panic::catch_unwind(|| once.wait()).unwrap_err();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn wait_force_on_poisoned() {
|
||||||
|
let once = Once::new();
|
||||||
|
|
||||||
|
thread::scope(|s| {
|
||||||
|
panic::catch_unwind(|| once.call_once(|| panic!())).unwrap_err();
|
||||||
|
|
||||||
|
s.spawn(|| {
|
||||||
|
thread::sleep(Duration::from_millis(100));
|
||||||
|
|
||||||
|
once.call_once_force(|_| {});
|
||||||
|
});
|
||||||
|
|
||||||
|
once.wait_force();
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -167,6 +167,34 @@ pub fn get_mut(&mut self) -> Option<&mut T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Blocks the current thread until the cell is initialized.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// Waiting for a computation on another thread to finish:
|
||||||
|
/// ```rust
|
||||||
|
/// #![feature(once_wait)]
|
||||||
|
///
|
||||||
|
/// use std::thread;
|
||||||
|
/// use std::sync::OnceLock;
|
||||||
|
///
|
||||||
|
/// let value = OnceLock::new();
|
||||||
|
///
|
||||||
|
/// thread::scope(|s| {
|
||||||
|
/// s.spawn(|| value.set(1 + 1));
|
||||||
|
///
|
||||||
|
/// let result = value.wait();
|
||||||
|
/// assert_eq!(result, &2);
|
||||||
|
/// })
|
||||||
|
/// ```
|
||||||
|
#[inline]
|
||||||
|
#[unstable(feature = "once_wait", issue = "127527")]
|
||||||
|
pub fn wait(&self) -> &T {
|
||||||
|
self.once.wait_force();
|
||||||
|
|
||||||
|
unsafe { self.get_unchecked() }
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the contents of this cell to `value`.
|
/// Sets the contents of this cell to `value`.
|
||||||
///
|
///
|
||||||
/// May block if another thread is currently attempting to initialize the cell. The cell is
|
/// May block if another thread is currently attempting to initialize the cell. The cell is
|
||||||
|
@ -1,18 +1,14 @@
|
|||||||
|
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(unix)] {
|
if #[cfg(unix)] {
|
||||||
mod unix;
|
mod unix;
|
||||||
pub(crate) use unix::{AnonPipe, pipe};
|
pub use unix::{AnonPipe, pipe};
|
||||||
|
|
||||||
#[cfg(all(test, not(miri)))]
|
|
||||||
mod tests;
|
|
||||||
} else if #[cfg(windows)] {
|
} else if #[cfg(windows)] {
|
||||||
mod windows;
|
mod windows;
|
||||||
pub(crate) use windows::{AnonPipe, pipe};
|
pub use windows::{AnonPipe, pipe};
|
||||||
|
|
||||||
#[cfg(all(test, not(miri)))]
|
|
||||||
mod tests;
|
|
||||||
} else {
|
} else {
|
||||||
mod unsupported;
|
mod unsupported;
|
||||||
pub(crate) use unsupported::{AnonPipe, pipe};
|
pub use unsupported::{AnonPipe, pipe};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,10 +6,10 @@
|
|||||||
use crate::sys::pipe::anon_pipe;
|
use crate::sys::pipe::anon_pipe;
|
||||||
use crate::sys_common::{FromInner, IntoInner};
|
use crate::sys_common::{FromInner, IntoInner};
|
||||||
|
|
||||||
pub(crate) type AnonPipe = FileDesc;
|
pub type AnonPipe = FileDesc;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> {
|
pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> {
|
||||||
anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner()))
|
anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ fn from(pipe: PipeReader) -> Self {
|
|||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
impl FromRawFd for PipeReader {
|
impl FromRawFd for PipeReader {
|
||||||
unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
|
unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
|
||||||
Self(FileDesc::from_raw_fd(raw_fd))
|
unsafe { Self(FileDesc::from_raw_fd(raw_fd)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
@ -71,7 +71,7 @@ fn from(pipe: PipeWriter) -> Self {
|
|||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
impl FromRawFd for PipeWriter {
|
impl FromRawFd for PipeWriter {
|
||||||
unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
|
unsafe fn from_raw_fd(raw_fd: RawFd) -> Self {
|
||||||
Self(FileDesc::from_raw_fd(raw_fd))
|
unsafe { Self(FileDesc::from_raw_fd(raw_fd)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
use crate::io;
|
use crate::io;
|
||||||
use crate::pipe::{PipeReader, PipeWriter};
|
use crate::pipe::{PipeReader, PipeWriter};
|
||||||
use crate::process::Stdio;
|
use crate::process::Stdio;
|
||||||
pub(crate) use crate::sys::pipe::AnonPipe;
|
pub use crate::sys::pipe::AnonPipe;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> {
|
pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> {
|
||||||
Err(io::Error::UNSUPPORTED_PLATFORM)
|
Err(io::Error::UNSUPPORTED_PLATFORM)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,18 +1,26 @@
|
|||||||
use crate::io;
|
|
||||||
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::pipe::{PipeReader, PipeWriter};
|
||||||
use crate::process::Stdio;
|
use crate::process::Stdio;
|
||||||
|
use crate::sys::c;
|
||||||
use crate::sys::handle::Handle;
|
use crate::sys::handle::Handle;
|
||||||
use crate::sys::pipe::unnamed_anon_pipe;
|
|
||||||
use crate::sys_common::{FromInner, IntoInner};
|
use crate::sys_common::{FromInner, IntoInner};
|
||||||
|
use crate::{io, ptr};
|
||||||
|
|
||||||
pub(crate) type AnonPipe = Handle;
|
pub type AnonPipe = Handle;
|
||||||
|
|
||||||
#[inline]
|
pub fn pipe() -> io::Result<(AnonPipe, AnonPipe)> {
|
||||||
pub(crate) fn pipe() -> io::Result<(AnonPipe, AnonPipe)> {
|
let mut read_pipe = c::INVALID_HANDLE_VALUE;
|
||||||
unnamed_anon_pipe().map(|(rx, wx)| (rx.into_inner(), wx.into_inner()))
|
let mut write_pipe = c::INVALID_HANDLE_VALUE;
|
||||||
|
|
||||||
|
let ret = unsafe { c::CreatePipe(&mut read_pipe, &mut write_pipe, ptr::null_mut(), 0) };
|
||||||
|
|
||||||
|
if ret == 0 {
|
||||||
|
Err(io::Error::last_os_error())
|
||||||
|
} else {
|
||||||
|
unsafe { Ok((Handle::from_raw_handle(read_pipe), Handle::from_raw_handle(write_pipe))) }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
@ -31,7 +39,7 @@ fn as_raw_handle(&self) -> RawHandle {
|
|||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
impl FromRawHandle for PipeReader {
|
impl FromRawHandle for PipeReader {
|
||||||
unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
|
unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
|
||||||
Self(Handle::from_raw_handle(raw_handle))
|
unsafe { Self(Handle::from_raw_handle(raw_handle)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
@ -70,7 +78,7 @@ fn as_raw_handle(&self) -> RawHandle {
|
|||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
impl FromRawHandle for PipeWriter {
|
impl FromRawHandle for PipeWriter {
|
||||||
unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
|
unsafe fn from_raw_handle(raw_handle: RawHandle) -> Self {
|
||||||
Self(Handle::from_raw_handle(raw_handle))
|
unsafe { Self(Handle::from_raw_handle(raw_handle)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
||||||
|
@ -7,7 +7,6 @@
|
|||||||
|
|
||||||
mod personality;
|
mod personality;
|
||||||
|
|
||||||
#[unstable(feature = "anonymous_pipe", issue = "127154")]
|
|
||||||
pub mod anonymous_pipe;
|
pub mod anonymous_pipe;
|
||||||
pub mod backtrace;
|
pub mod backtrace;
|
||||||
pub mod cmath;
|
pub mod cmath;
|
||||||
|
@ -36,23 +36,6 @@ pub struct Pipes {
|
|||||||
pub theirs: AnonPipe,
|
pub theirs: AnonPipe,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create true unnamed anonymous pipe.
|
|
||||||
pub fn unnamed_anon_pipe() -> io::Result<(AnonPipe, AnonPipe)> {
|
|
||||||
let mut read_pipe = c::INVALID_HANDLE_VALUE;
|
|
||||||
let mut write_pipe = c::INVALID_HANDLE_VALUE;
|
|
||||||
|
|
||||||
let ret = unsafe { c::CreatePipe(&mut read_pipe, &mut write_pipe, ptr::null_mut(), 0) };
|
|
||||||
|
|
||||||
if ret == 0 {
|
|
||||||
Err(io::Error::last_os_error())
|
|
||||||
} else {
|
|
||||||
Ok((
|
|
||||||
AnonPipe::from_inner(unsafe { Handle::from_raw_handle(read_pipe) }),
|
|
||||||
AnonPipe::from_inner(unsafe { Handle::from_raw_handle(write_pipe) }),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Although this looks similar to `anon_pipe` in the Unix module it's actually
|
/// Although this looks similar to `anon_pipe` in the Unix module it's actually
|
||||||
/// subtly different. Here we'll return two pipes in the `Pipes` return value,
|
/// subtly different. Here we'll return two pipes in the `Pipes` return value,
|
||||||
/// but one is intended for "us" where as the other is intended for "someone
|
/// but one is intended for "us" where as the other is intended for "someone
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
use crate::sys::futex::{futex_wait, futex_wake_all};
|
use crate::sys::futex::{futex_wait, futex_wake_all};
|
||||||
|
|
||||||
// On some platforms, the OS is very nice and handles the waiter queue for us.
|
// On some platforms, the OS is very nice and handles the waiter queue for us.
|
||||||
// This means we only need one atomic value with 5 states:
|
// This means we only need one atomic value with 4 states:
|
||||||
|
|
||||||
/// No initialization has run yet, and no thread is currently using the Once.
|
/// No initialization has run yet, and no thread is currently using the Once.
|
||||||
const INCOMPLETE: u32 = 0;
|
const INCOMPLETE: u32 = 0;
|
||||||
@ -17,16 +17,20 @@
|
|||||||
/// Some thread is currently attempting to run initialization. It may succeed,
|
/// Some thread is currently attempting to run initialization. It may succeed,
|
||||||
/// so all future threads need to wait for it to finish.
|
/// so all future threads need to wait for it to finish.
|
||||||
const RUNNING: u32 = 2;
|
const RUNNING: u32 = 2;
|
||||||
/// Some thread is currently attempting to run initialization and there are threads
|
|
||||||
/// waiting for it to finish.
|
|
||||||
const QUEUED: u32 = 3;
|
|
||||||
/// Initialization has completed and all future calls should finish immediately.
|
/// Initialization has completed and all future calls should finish immediately.
|
||||||
const COMPLETE: u32 = 4;
|
const COMPLETE: u32 = 3;
|
||||||
|
|
||||||
// Threads wait by setting the state to QUEUED and calling `futex_wait` on the state
|
// An additional bit indicates whether there are waiting threads:
|
||||||
|
|
||||||
|
/// May only be set if the state is not COMPLETE.
|
||||||
|
const QUEUED: u32 = 4;
|
||||||
|
|
||||||
|
// Threads wait by setting the QUEUED bit and calling `futex_wait` on the state
|
||||||
// variable. When the running thread finishes, it will wake all waiting threads using
|
// variable. When the running thread finishes, it will wake all waiting threads using
|
||||||
// `futex_wake_all`.
|
// `futex_wake_all`.
|
||||||
|
|
||||||
|
const STATE_MASK: u32 = 0b11;
|
||||||
|
|
||||||
pub struct OnceState {
|
pub struct OnceState {
|
||||||
poisoned: bool,
|
poisoned: bool,
|
||||||
set_state_to: Cell<u32>,
|
set_state_to: Cell<u32>,
|
||||||
@ -45,7 +49,7 @@ pub fn poison(&self) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct CompletionGuard<'a> {
|
struct CompletionGuard<'a> {
|
||||||
state: &'a AtomicU32,
|
state_and_queued: &'a AtomicU32,
|
||||||
set_state_on_drop_to: u32,
|
set_state_on_drop_to: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,32 +58,32 @@ fn drop(&mut self) {
|
|||||||
// Use release ordering to propagate changes to all threads checking
|
// Use release ordering to propagate changes to all threads checking
|
||||||
// up on the Once. `futex_wake_all` does its own synchronization, hence
|
// up on the Once. `futex_wake_all` does its own synchronization, hence
|
||||||
// we do not need `AcqRel`.
|
// we do not need `AcqRel`.
|
||||||
if self.state.swap(self.set_state_on_drop_to, Release) == QUEUED {
|
if self.state_and_queued.swap(self.set_state_on_drop_to, Release) & QUEUED != 0 {
|
||||||
futex_wake_all(self.state);
|
futex_wake_all(self.state_and_queued);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Once {
|
pub struct Once {
|
||||||
state: AtomicU32,
|
state_and_queued: AtomicU32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Once {
|
impl Once {
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn new() -> Once {
|
pub const fn new() -> Once {
|
||||||
Once { state: AtomicU32::new(INCOMPLETE) }
|
Once { state_and_queued: AtomicU32::new(INCOMPLETE) }
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn is_completed(&self) -> bool {
|
pub fn is_completed(&self) -> bool {
|
||||||
// Use acquire ordering to make all initialization changes visible to the
|
// Use acquire ordering to make all initialization changes visible to the
|
||||||
// current thread.
|
// current thread.
|
||||||
self.state.load(Acquire) == COMPLETE
|
self.state_and_queued.load(Acquire) == COMPLETE
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn state(&mut self) -> ExclusiveState {
|
pub(crate) fn state(&mut self) -> ExclusiveState {
|
||||||
match *self.state.get_mut() {
|
match *self.state_and_queued.get_mut() {
|
||||||
INCOMPLETE => ExclusiveState::Incomplete,
|
INCOMPLETE => ExclusiveState::Incomplete,
|
||||||
POISONED => ExclusiveState::Poisoned,
|
POISONED => ExclusiveState::Poisoned,
|
||||||
COMPLETE => ExclusiveState::Complete,
|
COMPLETE => ExclusiveState::Complete,
|
||||||
@ -87,31 +91,73 @@ pub(crate) fn state(&mut self) -> ExclusiveState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// This uses FnMut to match the API of the generic implementation. As this
|
|
||||||
// implementation is quite light-weight, it is generic over the closure and
|
|
||||||
// so avoids the cost of dynamic dispatch.
|
|
||||||
#[cold]
|
#[cold]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) {
|
pub fn wait(&self, ignore_poisoning: bool) {
|
||||||
let mut state = self.state.load(Acquire);
|
let mut state_and_queued = self.state_and_queued.load(Acquire);
|
||||||
loop {
|
loop {
|
||||||
|
let state = state_and_queued & STATE_MASK;
|
||||||
|
let queued = state_and_queued & QUEUED != 0;
|
||||||
match state {
|
match state {
|
||||||
|
COMPLETE => return,
|
||||||
|
POISONED if !ignore_poisoning => {
|
||||||
|
// Panic to propagate the poison.
|
||||||
|
panic!("Once instance has previously been poisoned");
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// Set the QUEUED bit if it has not already been set.
|
||||||
|
if !queued {
|
||||||
|
state_and_queued += QUEUED;
|
||||||
|
if let Err(new) = self.state_and_queued.compare_exchange_weak(
|
||||||
|
state,
|
||||||
|
state_and_queued,
|
||||||
|
Relaxed,
|
||||||
|
Acquire,
|
||||||
|
) {
|
||||||
|
state_and_queued = new;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
futex_wait(&self.state_and_queued, state_and_queued, None);
|
||||||
|
state_and_queued = self.state_and_queued.load(Acquire);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
#[track_caller]
|
||||||
|
pub fn call(&self, ignore_poisoning: bool, f: &mut dyn FnMut(&public::OnceState)) {
|
||||||
|
let mut state_and_queued = self.state_and_queued.load(Acquire);
|
||||||
|
loop {
|
||||||
|
let state = state_and_queued & STATE_MASK;
|
||||||
|
let queued = state_and_queued & QUEUED != 0;
|
||||||
|
match state {
|
||||||
|
COMPLETE => return,
|
||||||
POISONED if !ignore_poisoning => {
|
POISONED if !ignore_poisoning => {
|
||||||
// Panic to propagate the poison.
|
// Panic to propagate the poison.
|
||||||
panic!("Once instance has previously been poisoned");
|
panic!("Once instance has previously been poisoned");
|
||||||
}
|
}
|
||||||
INCOMPLETE | POISONED => {
|
INCOMPLETE | POISONED => {
|
||||||
// Try to register the current thread as the one running.
|
// Try to register the current thread as the one running.
|
||||||
if let Err(new) =
|
let next = RUNNING + if queued { QUEUED } else { 0 };
|
||||||
self.state.compare_exchange_weak(state, RUNNING, Acquire, Acquire)
|
if let Err(new) = self.state_and_queued.compare_exchange_weak(
|
||||||
{
|
state_and_queued,
|
||||||
state = new;
|
next,
|
||||||
|
Acquire,
|
||||||
|
Acquire,
|
||||||
|
) {
|
||||||
|
state_and_queued = new;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// `waiter_queue` will manage other waiting threads, and
|
// `waiter_queue` will manage other waiting threads, and
|
||||||
// wake them up on drop.
|
// wake them up on drop.
|
||||||
let mut waiter_queue =
|
let mut waiter_queue = CompletionGuard {
|
||||||
CompletionGuard { state: &self.state, set_state_on_drop_to: POISONED };
|
state_and_queued: &self.state_and_queued,
|
||||||
|
set_state_on_drop_to: POISONED,
|
||||||
|
};
|
||||||
// Run the function, letting it know if we're poisoned or not.
|
// Run the function, letting it know if we're poisoned or not.
|
||||||
let f_state = public::OnceState {
|
let f_state = public::OnceState {
|
||||||
inner: OnceState {
|
inner: OnceState {
|
||||||
@ -123,21 +169,27 @@ pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState
|
|||||||
waiter_queue.set_state_on_drop_to = f_state.inner.set_state_to.get();
|
waiter_queue.set_state_on_drop_to = f_state.inner.set_state_to.get();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
RUNNING | QUEUED => {
|
_ => {
|
||||||
// Set the state to QUEUED if it is not already.
|
// All other values must be RUNNING.
|
||||||
if state == RUNNING
|
assert!(state == RUNNING);
|
||||||
&& let Err(new) =
|
|
||||||
self.state.compare_exchange_weak(RUNNING, QUEUED, Relaxed, Acquire)
|
// Set the QUEUED bit if it is not already set.
|
||||||
{
|
if !queued {
|
||||||
state = new;
|
state_and_queued += QUEUED;
|
||||||
continue;
|
if let Err(new) = self.state_and_queued.compare_exchange_weak(
|
||||||
|
state,
|
||||||
|
state_and_queued,
|
||||||
|
Relaxed,
|
||||||
|
Acquire,
|
||||||
|
) {
|
||||||
|
state_and_queued = new;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
futex_wait(&self.state, QUEUED, None);
|
futex_wait(&self.state_and_queued, state_and_queued, None);
|
||||||
state = self.state.load(Acquire);
|
state_and_queued = self.state_and_queued.load(Acquire);
|
||||||
}
|
}
|
||||||
COMPLETE => return,
|
|
||||||
_ => unreachable!("state is never set to invalid values"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,12 @@ pub(crate) fn state(&mut self) -> ExclusiveState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
#[track_caller]
|
||||||
|
pub fn wait(&self, _ignore_poisoning: bool) {
|
||||||
|
panic!("not implementable on this target");
|
||||||
|
}
|
||||||
|
|
||||||
#[cold]
|
#[cold]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) {
|
pub fn call(&self, ignore_poisoning: bool, f: &mut impl FnMut(&public::OnceState)) {
|
||||||
|
@ -56,20 +56,21 @@
|
|||||||
// allowed, so no need for `SeqCst`.
|
// allowed, so no need for `SeqCst`.
|
||||||
|
|
||||||
use crate::cell::Cell;
|
use crate::cell::Cell;
|
||||||
use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering};
|
use crate::sync::atomic::Ordering::{AcqRel, Acquire, Release};
|
||||||
|
use crate::sync::atomic::{AtomicBool, AtomicPtr};
|
||||||
use crate::sync::once::ExclusiveState;
|
use crate::sync::once::ExclusiveState;
|
||||||
use crate::thread::{self, Thread};
|
use crate::thread::{self, Thread};
|
||||||
use crate::{fmt, ptr, sync as public};
|
use crate::{fmt, ptr, sync as public};
|
||||||
|
|
||||||
type Masked = ();
|
type StateAndQueue = *mut ();
|
||||||
|
|
||||||
pub struct Once {
|
pub struct Once {
|
||||||
state_and_queue: AtomicPtr<Masked>,
|
state_and_queue: AtomicPtr<()>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct OnceState {
|
pub struct OnceState {
|
||||||
poisoned: bool,
|
poisoned: bool,
|
||||||
set_state_on_drop_to: Cell<*mut Masked>,
|
set_state_on_drop_to: Cell<StateAndQueue>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Four states that a Once can be in, encoded into the lower bits of
|
// Four states that a Once can be in, encoded into the lower bits of
|
||||||
@ -81,7 +82,8 @@ pub struct OnceState {
|
|||||||
|
|
||||||
// Mask to learn about the state. All other bits are the queue of waiters if
|
// Mask to learn about the state. All other bits are the queue of waiters if
|
||||||
// this is in the RUNNING state.
|
// this is in the RUNNING state.
|
||||||
const STATE_MASK: usize = 0x3;
|
const STATE_MASK: usize = 0b11;
|
||||||
|
const QUEUE_MASK: usize = !STATE_MASK;
|
||||||
|
|
||||||
// Representation of a node in the linked list of waiters, used while in the
|
// Representation of a node in the linked list of waiters, used while in the
|
||||||
// RUNNING state.
|
// RUNNING state.
|
||||||
@ -93,15 +95,23 @@ pub struct OnceState {
|
|||||||
struct Waiter {
|
struct Waiter {
|
||||||
thread: Cell<Option<Thread>>,
|
thread: Cell<Option<Thread>>,
|
||||||
signaled: AtomicBool,
|
signaled: AtomicBool,
|
||||||
next: *const Waiter,
|
next: Cell<*const Waiter>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Head of a linked list of waiters.
|
// Head of a linked list of waiters.
|
||||||
// Every node is a struct on the stack of a waiting thread.
|
// Every node is a struct on the stack of a waiting thread.
|
||||||
// Will wake up the waiters when it gets dropped, i.e. also on panic.
|
// Will wake up the waiters when it gets dropped, i.e. also on panic.
|
||||||
struct WaiterQueue<'a> {
|
struct WaiterQueue<'a> {
|
||||||
state_and_queue: &'a AtomicPtr<Masked>,
|
state_and_queue: &'a AtomicPtr<()>,
|
||||||
set_state_on_drop_to: *mut Masked,
|
set_state_on_drop_to: StateAndQueue,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_queue(current: StateAndQueue) -> *const Waiter {
|
||||||
|
current.mask(QUEUE_MASK).cast()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_state(current: StateAndQueue) -> usize {
|
||||||
|
current.addr() & STATE_MASK
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Once {
|
impl Once {
|
||||||
@ -117,7 +127,7 @@ pub fn is_completed(&self) -> bool {
|
|||||||
// operations visible to us, and, this being a fast path, weaker
|
// operations visible to us, and, this being a fast path, weaker
|
||||||
// ordering helps with performance. This `Acquire` synchronizes with
|
// ordering helps with performance. This `Acquire` synchronizes with
|
||||||
// `Release` operations on the slow path.
|
// `Release` operations on the slow path.
|
||||||
self.state_and_queue.load(Ordering::Acquire).addr() == COMPLETE
|
self.state_and_queue.load(Acquire).addr() == COMPLETE
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -130,6 +140,25 @@ pub(crate) fn state(&mut self) -> ExclusiveState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cold]
|
||||||
|
#[track_caller]
|
||||||
|
pub fn wait(&self, ignore_poisoning: bool) {
|
||||||
|
let mut current = self.state_and_queue.load(Acquire);
|
||||||
|
loop {
|
||||||
|
let state = to_state(current);
|
||||||
|
match state {
|
||||||
|
COMPLETE => return,
|
||||||
|
POISONED if !ignore_poisoning => {
|
||||||
|
// Panic to propagate the poison.
|
||||||
|
panic!("Once instance has previously been poisoned");
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
current = wait(&self.state_and_queue, current, !ignore_poisoning);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// This is a non-generic function to reduce the monomorphization cost of
|
// This is a non-generic function to reduce the monomorphization cost of
|
||||||
// using `call_once` (this isn't exactly a trivial or small implementation).
|
// using `call_once` (this isn't exactly a trivial or small implementation).
|
||||||
//
|
//
|
||||||
@ -144,9 +173,10 @@ pub(crate) fn state(&mut self) -> ExclusiveState {
|
|||||||
#[cold]
|
#[cold]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn call(&self, ignore_poisoning: bool, init: &mut dyn FnMut(&public::OnceState)) {
|
pub fn call(&self, ignore_poisoning: bool, init: &mut dyn FnMut(&public::OnceState)) {
|
||||||
let mut state_and_queue = self.state_and_queue.load(Ordering::Acquire);
|
let mut current = self.state_and_queue.load(Acquire);
|
||||||
loop {
|
loop {
|
||||||
match state_and_queue.addr() {
|
let state = to_state(current);
|
||||||
|
match state {
|
||||||
COMPLETE => break,
|
COMPLETE => break,
|
||||||
POISONED if !ignore_poisoning => {
|
POISONED if !ignore_poisoning => {
|
||||||
// Panic to propagate the poison.
|
// Panic to propagate the poison.
|
||||||
@ -154,16 +184,16 @@ pub fn call(&self, ignore_poisoning: bool, init: &mut dyn FnMut(&public::OnceSta
|
|||||||
}
|
}
|
||||||
POISONED | INCOMPLETE => {
|
POISONED | INCOMPLETE => {
|
||||||
// Try to register this thread as the one RUNNING.
|
// Try to register this thread as the one RUNNING.
|
||||||
let exchange_result = self.state_and_queue.compare_exchange(
|
if let Err(new) = self.state_and_queue.compare_exchange_weak(
|
||||||
state_and_queue,
|
current,
|
||||||
ptr::without_provenance_mut(RUNNING),
|
current.mask(QUEUE_MASK).wrapping_byte_add(RUNNING),
|
||||||
Ordering::Acquire,
|
Acquire,
|
||||||
Ordering::Acquire,
|
Acquire,
|
||||||
);
|
) {
|
||||||
if let Err(old) = exchange_result {
|
current = new;
|
||||||
state_and_queue = old;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// `waiter_queue` will manage other waiting threads, and
|
// `waiter_queue` will manage other waiting threads, and
|
||||||
// wake them up on drop.
|
// wake them up on drop.
|
||||||
let mut waiter_queue = WaiterQueue {
|
let mut waiter_queue = WaiterQueue {
|
||||||
@ -174,54 +204,57 @@ pub fn call(&self, ignore_poisoning: bool, init: &mut dyn FnMut(&public::OnceSta
|
|||||||
// poisoned or not.
|
// poisoned or not.
|
||||||
let init_state = public::OnceState {
|
let init_state = public::OnceState {
|
||||||
inner: OnceState {
|
inner: OnceState {
|
||||||
poisoned: state_and_queue.addr() == POISONED,
|
poisoned: state == POISONED,
|
||||||
set_state_on_drop_to: Cell::new(ptr::without_provenance_mut(COMPLETE)),
|
set_state_on_drop_to: Cell::new(ptr::without_provenance_mut(COMPLETE)),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
init(&init_state);
|
init(&init_state);
|
||||||
waiter_queue.set_state_on_drop_to = init_state.inner.set_state_on_drop_to.get();
|
waiter_queue.set_state_on_drop_to = init_state.inner.set_state_on_drop_to.get();
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// All other values must be RUNNING with possibly a
|
// All other values must be RUNNING with possibly a
|
||||||
// pointer to the waiter queue in the more significant bits.
|
// pointer to the waiter queue in the more significant bits.
|
||||||
assert!(state_and_queue.addr() & STATE_MASK == RUNNING);
|
assert!(state == RUNNING);
|
||||||
wait(&self.state_and_queue, state_and_queue);
|
current = wait(&self.state_and_queue, current, true);
|
||||||
state_and_queue = self.state_and_queue.load(Ordering::Acquire);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn wait(state_and_queue: &AtomicPtr<Masked>, mut current_state: *mut Masked) {
|
fn wait(
|
||||||
// Note: the following code was carefully written to avoid creating a
|
state_and_queue: &AtomicPtr<()>,
|
||||||
// mutable reference to `node` that gets aliased.
|
mut current: StateAndQueue,
|
||||||
|
return_on_poisoned: bool,
|
||||||
|
) -> StateAndQueue {
|
||||||
|
let node = &Waiter {
|
||||||
|
thread: Cell::new(Some(thread::current())),
|
||||||
|
signaled: AtomicBool::new(false),
|
||||||
|
next: Cell::new(ptr::null()),
|
||||||
|
};
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Don't queue this thread if the status is no longer running,
|
let state = to_state(current);
|
||||||
// otherwise we will not be woken up.
|
let queue = to_queue(current);
|
||||||
if current_state.addr() & STATE_MASK != RUNNING {
|
|
||||||
return;
|
// If initialization has finished, return.
|
||||||
|
if state == COMPLETE || (return_on_poisoned && state == POISONED) {
|
||||||
|
return current;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the node for our current thread.
|
// Update the node for our current thread.
|
||||||
let node = Waiter {
|
node.next.set(queue);
|
||||||
thread: Cell::new(Some(thread::current())),
|
|
||||||
signaled: AtomicBool::new(false),
|
|
||||||
next: current_state.with_addr(current_state.addr() & !STATE_MASK) as *const Waiter,
|
|
||||||
};
|
|
||||||
let me = core::ptr::addr_of!(node) as *const Masked as *mut Masked;
|
|
||||||
|
|
||||||
// Try to slide in the node at the head of the linked list, making sure
|
// Try to slide in the node at the head of the linked list, making sure
|
||||||
// that another thread didn't just replace the head of the linked list.
|
// that another thread didn't just replace the head of the linked list.
|
||||||
let exchange_result = state_and_queue.compare_exchange(
|
if let Err(new) = state_and_queue.compare_exchange_weak(
|
||||||
current_state,
|
current,
|
||||||
me.with_addr(me.addr() | RUNNING),
|
ptr::from_ref(node).wrapping_byte_add(state) as StateAndQueue,
|
||||||
Ordering::Release,
|
Release,
|
||||||
Ordering::Relaxed,
|
Acquire,
|
||||||
);
|
) {
|
||||||
if let Err(old) = exchange_result {
|
current = new;
|
||||||
current_state = old;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -230,14 +263,15 @@ fn wait(state_and_queue: &AtomicPtr<Masked>, mut current_state: *mut Masked) {
|
|||||||
// would drop our `Waiter` node and leave a hole in the linked list
|
// would drop our `Waiter` node and leave a hole in the linked list
|
||||||
// (and a dangling reference). Guard against spurious wakeups by
|
// (and a dangling reference). Guard against spurious wakeups by
|
||||||
// reparking ourselves until we are signaled.
|
// reparking ourselves until we are signaled.
|
||||||
while !node.signaled.load(Ordering::Acquire) {
|
while !node.signaled.load(Acquire) {
|
||||||
// If the managing thread happens to signal and unpark us before we
|
// If the managing thread happens to signal and unpark us before we
|
||||||
// can park ourselves, the result could be this thread never gets
|
// can park ourselves, the result could be this thread never gets
|
||||||
// unparked. Luckily `park` comes with the guarantee that if it got
|
// unparked. Luckily `park` comes with the guarantee that if it got
|
||||||
// an `unpark` just before on an unparked thread it does not park.
|
// an `unpark` just before on an unparked thread it does not park.
|
||||||
thread::park();
|
thread::park();
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
|
return state_and_queue.load(Acquire);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -251,11 +285,10 @@ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|||||||
impl Drop for WaiterQueue<'_> {
|
impl Drop for WaiterQueue<'_> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
// Swap out our state with however we finished.
|
// Swap out our state with however we finished.
|
||||||
let state_and_queue =
|
let current = self.state_and_queue.swap(self.set_state_on_drop_to, AcqRel);
|
||||||
self.state_and_queue.swap(self.set_state_on_drop_to, Ordering::AcqRel);
|
|
||||||
|
|
||||||
// We should only ever see an old state which was RUNNING.
|
// We should only ever see an old state which was RUNNING.
|
||||||
assert_eq!(state_and_queue.addr() & STATE_MASK, RUNNING);
|
assert_eq!(current.addr() & STATE_MASK, RUNNING);
|
||||||
|
|
||||||
// Walk the entire linked list of waiters and wake them up (in lifo
|
// Walk the entire linked list of waiters and wake them up (in lifo
|
||||||
// order, last to register is first to wake up).
|
// order, last to register is first to wake up).
|
||||||
@ -264,16 +297,13 @@ fn drop(&mut self) {
|
|||||||
// free `node` if there happens to be has a spurious wakeup.
|
// free `node` if there happens to be has a spurious wakeup.
|
||||||
// So we have to take out the `thread` field and copy the pointer to
|
// So we have to take out the `thread` field and copy the pointer to
|
||||||
// `next` first.
|
// `next` first.
|
||||||
let mut queue =
|
let mut queue = to_queue(current);
|
||||||
state_and_queue.with_addr(state_and_queue.addr() & !STATE_MASK) as *const Waiter;
|
|
||||||
while !queue.is_null() {
|
while !queue.is_null() {
|
||||||
let next = (*queue).next;
|
let next = (*queue).next.get();
|
||||||
let thread = (*queue).thread.take().unwrap();
|
let thread = (*queue).thread.take().unwrap();
|
||||||
(*queue).signaled.store(true, Ordering::Release);
|
(*queue).signaled.store(true, Release);
|
||||||
// ^- FIXME (maybe): This is another case of issue #55005
|
|
||||||
// `store()` has a potentially dangling ref to `signaled`.
|
|
||||||
queue = next;
|
|
||||||
thread.unpark();
|
thread.unpark();
|
||||||
|
queue = next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
15
tests/ui/async-await/unreachable-lint-2.rs
Normal file
15
tests/ui/async-await/unreachable-lint-2.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//@ edition:2018
|
||||||
|
|
||||||
|
#![deny(unreachable_code)]
|
||||||
|
|
||||||
|
async fn foo() {
|
||||||
|
endless().await;
|
||||||
|
println!("this is unreachable!");
|
||||||
|
//~^ ERROR unreachable statement
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn endless() -> ! {
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() { }
|
17
tests/ui/async-await/unreachable-lint-2.stderr
Normal file
17
tests/ui/async-await/unreachable-lint-2.stderr
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
error: unreachable statement
|
||||||
|
--> $DIR/unreachable-lint-2.rs:7:5
|
||||||
|
|
|
||||||
|
LL | endless().await;
|
||||||
|
| ----- any code following this expression is unreachable
|
||||||
|
LL | println!("this is unreachable!");
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ unreachable statement
|
||||||
|
|
|
||||||
|
note: the lint level is defined here
|
||||||
|
--> $DIR/unreachable-lint-2.rs:3:9
|
||||||
|
|
|
||||||
|
LL | #![deny(unreachable_code)]
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
= note: this error originates in the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: aborting due to 1 previous error
|
||||||
|
|
@ -1,5 +1,6 @@
|
|||||||
#![feature(derive_smart_pointer, arbitrary_self_types)]
|
#![feature(derive_smart_pointer, arbitrary_self_types)]
|
||||||
|
|
||||||
|
extern crate core;
|
||||||
use std::marker::SmartPointer;
|
use std::marker::SmartPointer;
|
||||||
|
|
||||||
#[derive(SmartPointer)]
|
#[derive(SmartPointer)]
|
||||||
@ -35,6 +36,13 @@ struct NotTransparent<'a, #[pointee] T: ?Sized> {
|
|||||||
ptr: &'a T,
|
ptr: &'a T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(SmartPointer)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct NoMaybeSized<'a, #[pointee] T> {
|
||||||
|
//~^ ERROR: `derive(SmartPointer)` requires T to be marked `?Sized`
|
||||||
|
ptr: &'a T,
|
||||||
|
}
|
||||||
|
|
||||||
// However, reordering attributes should work nevertheless.
|
// However, reordering attributes should work nevertheless.
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(SmartPointer)]
|
#[derive(SmartPointer)]
|
||||||
@ -42,4 +50,26 @@ struct ThisIsAPossibleSmartPointer<'a, #[pointee] T: ?Sized> {
|
|||||||
ptr: &'a T,
|
ptr: &'a T,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Also, these paths to Sized should work
|
||||||
|
#[derive(SmartPointer)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct StdSized<'a, #[pointee] T: ?std::marker::Sized> {
|
||||||
|
ptr: &'a T,
|
||||||
|
}
|
||||||
|
#[derive(SmartPointer)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct CoreSized<'a, #[pointee] T: ?core::marker::Sized> {
|
||||||
|
ptr: &'a T,
|
||||||
|
}
|
||||||
|
#[derive(SmartPointer)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct GlobalStdSized<'a, #[pointee] T: ?::std::marker::Sized> {
|
||||||
|
ptr: &'a T,
|
||||||
|
}
|
||||||
|
#[derive(SmartPointer)]
|
||||||
|
#[repr(transparent)]
|
||||||
|
struct GlobalCoreSized<'a, #[pointee] T: ?::core::marker::Sized> {
|
||||||
|
ptr: &'a T,
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
error: `SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`
|
error: `SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`
|
||||||
--> $DIR/deriving-smart-pointer-neg.rs:5:10
|
--> $DIR/deriving-smart-pointer-neg.rs:6:10
|
||||||
|
|
|
|
||||||
LL | #[derive(SmartPointer)]
|
LL | #[derive(SmartPointer)]
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
@ -7,7 +7,7 @@ LL | #[derive(SmartPointer)]
|
|||||||
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits
|
error: At least one generic type should be designated as `#[pointee]` in order to derive `SmartPointer` traits
|
||||||
--> $DIR/deriving-smart-pointer-neg.rs:11:10
|
--> $DIR/deriving-smart-pointer-neg.rs:12:10
|
||||||
|
|
|
|
||||||
LL | #[derive(SmartPointer)]
|
LL | #[derive(SmartPointer)]
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
@ -15,7 +15,7 @@ LL | #[derive(SmartPointer)]
|
|||||||
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: `SmartPointer` can only be derived on `struct`s with at least one field
|
error: `SmartPointer` can only be derived on `struct`s with at least one field
|
||||||
--> $DIR/deriving-smart-pointer-neg.rs:18:10
|
--> $DIR/deriving-smart-pointer-neg.rs:19:10
|
||||||
|
|
|
|
||||||
LL | #[derive(SmartPointer)]
|
LL | #[derive(SmartPointer)]
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
@ -23,7 +23,7 @@ LL | #[derive(SmartPointer)]
|
|||||||
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: `SmartPointer` can only be derived on `struct`s with at least one field
|
error: `SmartPointer` can only be derived on `struct`s with at least one field
|
||||||
--> $DIR/deriving-smart-pointer-neg.rs:25:10
|
--> $DIR/deriving-smart-pointer-neg.rs:26:10
|
||||||
|
|
|
|
||||||
LL | #[derive(SmartPointer)]
|
LL | #[derive(SmartPointer)]
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
@ -31,15 +31,21 @@ LL | #[derive(SmartPointer)]
|
|||||||
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error: `SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`
|
error: `SmartPointer` can only be derived on `struct`s with `#[repr(transparent)]`
|
||||||
--> $DIR/deriving-smart-pointer-neg.rs:32:10
|
--> $DIR/deriving-smart-pointer-neg.rs:33:10
|
||||||
|
|
|
|
||||||
LL | #[derive(SmartPointer)]
|
LL | #[derive(SmartPointer)]
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in the derive macro `SmartPointer` (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: `derive(SmartPointer)` requires T to be marked `?Sized`
|
||||||
|
--> $DIR/deriving-smart-pointer-neg.rs:41:36
|
||||||
|
|
|
||||||
|
LL | struct NoMaybeSized<'a, #[pointee] T> {
|
||||||
|
| ^
|
||||||
|
|
||||||
error[E0392]: lifetime parameter `'a` is never used
|
error[E0392]: lifetime parameter `'a` is never used
|
||||||
--> $DIR/deriving-smart-pointer-neg.rs:21:16
|
--> $DIR/deriving-smart-pointer-neg.rs:22:16
|
||||||
|
|
|
|
||||||
LL | struct NoField<'a, #[pointee] T: ?Sized> {}
|
LL | struct NoField<'a, #[pointee] T: ?Sized> {}
|
||||||
| ^^ unused lifetime parameter
|
| ^^ unused lifetime parameter
|
||||||
@ -47,7 +53,7 @@ LL | struct NoField<'a, #[pointee] T: ?Sized> {}
|
|||||||
= help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
|
= help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
|
||||||
|
|
||||||
error[E0392]: type parameter `T` is never used
|
error[E0392]: type parameter `T` is never used
|
||||||
--> $DIR/deriving-smart-pointer-neg.rs:21:31
|
--> $DIR/deriving-smart-pointer-neg.rs:22:31
|
||||||
|
|
|
|
||||||
LL | struct NoField<'a, #[pointee] T: ?Sized> {}
|
LL | struct NoField<'a, #[pointee] T: ?Sized> {}
|
||||||
| ^ unused type parameter
|
| ^ unused type parameter
|
||||||
@ -55,7 +61,7 @@ LL | struct NoField<'a, #[pointee] T: ?Sized> {}
|
|||||||
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
|
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
|
||||||
|
|
||||||
error[E0392]: lifetime parameter `'a` is never used
|
error[E0392]: lifetime parameter `'a` is never used
|
||||||
--> $DIR/deriving-smart-pointer-neg.rs:28:20
|
--> $DIR/deriving-smart-pointer-neg.rs:29:20
|
||||||
|
|
|
|
||||||
LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
|
LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
|
||||||
| ^^ unused lifetime parameter
|
| ^^ unused lifetime parameter
|
||||||
@ -63,13 +69,13 @@ LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
|
|||||||
= help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
|
= help: consider removing `'a`, referring to it in a field, or using a marker such as `PhantomData`
|
||||||
|
|
||||||
error[E0392]: type parameter `T` is never used
|
error[E0392]: type parameter `T` is never used
|
||||||
--> $DIR/deriving-smart-pointer-neg.rs:28:35
|
--> $DIR/deriving-smart-pointer-neg.rs:29:35
|
||||||
|
|
|
|
||||||
LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
|
LL | struct NoFieldUnit<'a, #[pointee] T: ?Sized>();
|
||||||
| ^ unused type parameter
|
| ^ unused type parameter
|
||||||
|
|
|
|
||||||
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
|
= help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData`
|
||||||
|
|
||||||
error: aborting due to 9 previous errors
|
error: aborting due to 10 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0392`.
|
For more information about this error, try `rustc --explain E0392`.
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
//@ check-fail
|
//@ check-fail
|
||||||
|
|
||||||
|
#![deny(ambiguous_negative_literals)]
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let _ = -1i32.abs();
|
let _ = -1i32.abs();
|
||||||
//~^ ERROR `-` has lower precedence than method calls
|
//~^ ERROR `-` has lower precedence than method calls
|
||||||
|
@ -1,11 +1,15 @@
|
|||||||
error: `-` has lower precedence than method calls, which might be unexpected
|
error: `-` has lower precedence than method calls, which might be unexpected
|
||||||
--> $DIR/negative_literals.rs:4:13
|
--> $DIR/negative_literals.rs:6:13
|
||||||
|
|
|
|
||||||
LL | let _ = -1i32.abs();
|
LL | let _ = -1i32.abs();
|
||||||
| ^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4`
|
= note: e.g. `-4.abs()` equals `-4`; while `(-4).abs()` equals `4`
|
||||||
= note: `#[deny(ambiguous_negative_literals)]` on by default
|
note: the lint level is defined here
|
||||||
|
--> $DIR/negative_literals.rs:3:9
|
||||||
|
|
|
||||||
|
LL | #![deny(ambiguous_negative_literals)]
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
help: add parentheses around the `-` and the literal to call the method on a negative literal
|
help: add parentheses around the `-` and the literal to call the method on a negative literal
|
||||||
|
|
|
|
||||||
LL | let _ = (-1i32).abs();
|
LL | let _ = (-1i32).abs();
|
||||||
@ -16,7 +20,7 @@ LL | let _ = -(1i32.abs());
|
|||||||
| + +
|
| + +
|
||||||
|
|
||||||
error: `-` has lower precedence than method calls, which might be unexpected
|
error: `-` has lower precedence than method calls, which might be unexpected
|
||||||
--> $DIR/negative_literals.rs:6:13
|
--> $DIR/negative_literals.rs:8:13
|
||||||
|
|
|
|
||||||
LL | let _ = -1f32.abs();
|
LL | let _ = -1f32.abs();
|
||||||
| ^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
@ -32,7 +36,7 @@ LL | let _ = -(1f32.abs());
|
|||||||
| + +
|
| + +
|
||||||
|
|
||||||
error: `-` has lower precedence than method calls, which might be unexpected
|
error: `-` has lower precedence than method calls, which might be unexpected
|
||||||
--> $DIR/negative_literals.rs:8:13
|
--> $DIR/negative_literals.rs:10:13
|
||||||
|
|
|
|
||||||
LL | let _ = -1f64.asin();
|
LL | let _ = -1f64.asin();
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
@ -48,7 +52,7 @@ LL | let _ = -(1f64.asin());
|
|||||||
| + +
|
| + +
|
||||||
|
|
||||||
error: `-` has lower precedence than method calls, which might be unexpected
|
error: `-` has lower precedence than method calls, which might be unexpected
|
||||||
--> $DIR/negative_literals.rs:10:13
|
--> $DIR/negative_literals.rs:12:13
|
||||||
|
|
|
|
||||||
LL | let _ = -1f64.asinh();
|
LL | let _ = -1f64.asinh();
|
||||||
| ^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^
|
||||||
@ -64,7 +68,7 @@ LL | let _ = -(1f64.asinh());
|
|||||||
| + +
|
| + +
|
||||||
|
|
||||||
error: `-` has lower precedence than method calls, which might be unexpected
|
error: `-` has lower precedence than method calls, which might be unexpected
|
||||||
--> $DIR/negative_literals.rs:12:13
|
--> $DIR/negative_literals.rs:14:13
|
||||||
|
|
|
|
||||||
LL | let _ = -1f64.tan();
|
LL | let _ = -1f64.tan();
|
||||||
| ^^^^^^^^^^^
|
| ^^^^^^^^^^^
|
||||||
@ -80,7 +84,7 @@ LL | let _ = -(1f64.tan());
|
|||||||
| + +
|
| + +
|
||||||
|
|
||||||
error: `-` has lower precedence than method calls, which might be unexpected
|
error: `-` has lower precedence than method calls, which might be unexpected
|
||||||
--> $DIR/negative_literals.rs:14:13
|
--> $DIR/negative_literals.rs:16:13
|
||||||
|
|
|
|
||||||
LL | let _ = -1f64.tanh();
|
LL | let _ = -1f64.tanh();
|
||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
@ -96,7 +100,7 @@ LL | let _ = -(1f64.tanh());
|
|||||||
| + +
|
| + +
|
||||||
|
|
||||||
error: `-` has lower precedence than method calls, which might be unexpected
|
error: `-` has lower precedence than method calls, which might be unexpected
|
||||||
--> $DIR/negative_literals.rs:16:13
|
--> $DIR/negative_literals.rs:18:13
|
||||||
|
|
|
|
||||||
LL | let _ = -1.0_f64.cos().cos();
|
LL | let _ = -1.0_f64.cos().cos();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -112,7 +116,7 @@ LL | let _ = -(1.0_f64.cos().cos());
|
|||||||
| + +
|
| + +
|
||||||
|
|
||||||
error: `-` has lower precedence than method calls, which might be unexpected
|
error: `-` has lower precedence than method calls, which might be unexpected
|
||||||
--> $DIR/negative_literals.rs:18:13
|
--> $DIR/negative_literals.rs:20:13
|
||||||
|
|
|
|
||||||
LL | let _ = -1.0_f64.cos().sin();
|
LL | let _ = -1.0_f64.cos().sin();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -128,7 +132,7 @@ LL | let _ = -(1.0_f64.cos().sin());
|
|||||||
| + +
|
| + +
|
||||||
|
|
||||||
error: `-` has lower precedence than method calls, which might be unexpected
|
error: `-` has lower precedence than method calls, which might be unexpected
|
||||||
--> $DIR/negative_literals.rs:20:13
|
--> $DIR/negative_literals.rs:22:13
|
||||||
|
|
|
|
||||||
LL | let _ = -1.0_f64.sin().cos();
|
LL | let _ = -1.0_f64.sin().cos();
|
||||||
| ^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^
|
||||||
@ -144,7 +148,7 @@ LL | let _ = -(1.0_f64.sin().cos());
|
|||||||
| + +
|
| + +
|
||||||
|
|
||||||
error: `-` has lower precedence than method calls, which might be unexpected
|
error: `-` has lower precedence than method calls, which might be unexpected
|
||||||
--> $DIR/negative_literals.rs:22:13
|
--> $DIR/negative_literals.rs:24:13
|
||||||
|
|
|
|
||||||
LL | let _ = -1f64.sin().sin();
|
LL | let _ = -1f64.sin().sin();
|
||||||
| ^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^
|
||||||
@ -160,7 +164,7 @@ LL | let _ = -(1f64.sin().sin());
|
|||||||
| + +
|
| + +
|
||||||
|
|
||||||
error: `-` has lower precedence than method calls, which might be unexpected
|
error: `-` has lower precedence than method calls, which might be unexpected
|
||||||
--> $DIR/negative_literals.rs:25:11
|
--> $DIR/negative_literals.rs:27:11
|
||||||
|
|
|
|
||||||
LL | dbg!( -1.0_f32.cos() );
|
LL | dbg!( -1.0_f32.cos() );
|
||||||
| ^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^
|
||||||
|
Loading…
Reference in New Issue
Block a user