diff --git a/RELEASES.md b/RELEASES.md index 08040f4815836..0965e37574d07 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,158 @@ +Version 1.60.0 (2022-04-07) +========================== + +Language +-------- +- [Stabilize `#[cfg(panic = "...")]` for either `"unwind"` or `"abort"`.][93658] +- [Stabilize `#[cfg(target_has_atomic = "...")]` for each integer size and `"ptr"`.][93824] + +Compiler +-------- +- [Enable combining `+crt-static` and `relocation-model=pic` on `x86_64-unknown-linux-gnu`][86374] +- [Fixes wrong `unreachable_pub` lints on nested and glob public reexport][87487] +- [Stabilize `-Z instrument-coverage` as `-C instrument-coverage`][90132] +- [Stabilize `-Z print-link-args` as `--print link-args`][91606] +- [Add new Tier 3 target `mips64-openwrt-linux-musl`\*][92300] +- [Add new Tier 3 target `armv7-unknown-linux-uclibceabi` (softfloat)\*][92383] +- [Fix invalid removal of newlines from doc comments][92357] +- [Add kernel target for RustyHermit][92670] +- [Deny mixing bin crate type with lib crate types][92933] +- [Make rustc use `RUST_BACKTRACE=full` by default][93566] +- [Upgrade to LLVM 14][93577] + +\* Refer to Rust's [platform support page][platform-support-doc] for more + information on Rust's tiered platform support. + +Libraries +--------- +- [Guarantee call order for `sort_by_cached_key`][89621] +- [Improve `Duration::try_from_secs_f32`/`f64` accuracy by directly processing exponent and mantissa][90247] +- [Make `Instant::{duration_since, elapsed, sub}` saturating][89926] +- [Remove non-monotonic clocks workarounds in `Instant::now`][89926] +- [Make `BuildHasherDefault`, `iter::Empty` and `future::Pending` covariant][92630] + +Stabilized APIs +--------------- +- [`Arc::new_cyclic`][arc_new_cyclic] +- [`Rc::new_cyclic`][rc_new_cyclic] +- [`slice::EscapeAscii`][slice_escape_ascii] +- [`<[u8]>::escape_ascii`][slice_u8_escape_ascii] +- [`u8::escape_ascii`][u8_escape_ascii] +- [`Vec::spare_capacity_mut`][vec_spare_capacity_mut] +- [`MaybeUninit::assume_init_drop`][assume_init_drop] +- [`MaybeUninit::assume_init_read`][assume_init_read] +- [`i8::abs_diff`][i8_abs_diff] +- [`i16::abs_diff`][i16_abs_diff] +- [`i32::abs_diff`][i32_abs_diff] +- [`i64::abs_diff`][i64_abs_diff] +- [`i128::abs_diff`][i128_abs_diff] +- [`isize::abs_diff`][isize_abs_diff] +- [`u8::abs_diff`][u8_abs_diff] +- [`u16::abs_diff`][u16_abs_diff] +- [`u32::abs_diff`][u32_abs_diff] +- [`u64::abs_diff`][u64_abs_diff] +- [`u128::abs_diff`][u128_abs_diff] +- [`usize::abs_diff`][usize_abs_diff] +- [`Display for io::ErrorKind`][display_error_kind] +- [`From for ExitCode`][from_u8_exit_code] +- [`Not for !` (the "never" type)][not_never] +- [_Op_`Assign<$t> for Wrapping<$t>`][wrapping_assign_ops] +- [`arch::is_aarch64_feature_detected!`][is_aarch64_feature_detected] + +Cargo +----- +- [Port cargo from `toml-rs` to `toml_edit`][cargo/10086] +- [Stabilize `-Ztimings` as `--timings`][cargo/10245] +- [Stabilize namespaced and weak dependency features.][cargo/10269] +- [Accept more `cargo:rustc-link-arg-*` types from build script output.][cargo/10274] +- [cargo-new should not add ignore rule on Cargo.lock inside subdirs][cargo/10379] + +Misc +---- +- [Ship docs on Tier 2 platforms by reusing the closest Tier 1 platform docs][92800] +- [Drop rustc-docs from complete profile][93742] +- [bootstrap: tidy up flag handling for llvm build][93918] + +Compatibility Notes +------------------- +- [Remove compiler-rt linking hack on Android][83822] +- [Mitigations for platforms with non-monotonic clocks have been removed from + `Instant::now`][89926]. On platforms that don't provide monotonic clocks, an + instant is not guaranteed to be greater than an earlier instant anymore. +- [`Instant::{duration_since, elapsed, sub}` do not panic anymore on underflow, + saturating to `0` instead][89926]. In the real world the panic happened mostly + on platforms with buggy monotonic clock implementations rather than catching + programming errors like reversing the start and end times. Such programming + errors will now results in `0` rather than a panic. +- In a future release we're planning to increase the baseline requirements for + the Linux kernel to version 3.2, and for glibc to version 2.17. We'd love + your feedback in [PR #95026][95026]. + +Internal Changes +---------------- + +These changes provide no direct user facing benefits, but represent significant +improvements to the internals and overall performance of rustc +and related tools. + +- [Switch all libraries to the 2021 edition][92068] + +[83822]: https://github.com/rust-lang/rust/pull/83822 +[86374]: https://github.com/rust-lang/rust/pull/86374 +[87487]: https://github.com/rust-lang/rust/pull/87487 +[89621]: https://github.com/rust-lang/rust/pull/89621 +[89926]: https://github.com/rust-lang/rust/pull/89926 +[90132]: https://github.com/rust-lang/rust/pull/90132 +[90247]: https://github.com/rust-lang/rust/pull/90247 +[91606]: https://github.com/rust-lang/rust/pull/91606 +[92068]: https://github.com/rust-lang/rust/pull/92068 +[92300]: https://github.com/rust-lang/rust/pull/92300 +[92357]: https://github.com/rust-lang/rust/pull/92357 +[92383]: https://github.com/rust-lang/rust/pull/92383 +[92630]: https://github.com/rust-lang/rust/pull/92630 +[92670]: https://github.com/rust-lang/rust/pull/92670 +[92800]: https://github.com/rust-lang/rust/pull/92800 +[92933]: https://github.com/rust-lang/rust/pull/92933 +[93566]: https://github.com/rust-lang/rust/pull/93566 +[93577]: https://github.com/rust-lang/rust/pull/93577 +[93658]: https://github.com/rust-lang/rust/pull/93658 +[93742]: https://github.com/rust-lang/rust/pull/93742 +[93824]: https://github.com/rust-lang/rust/pull/93824 +[93918]: https://github.com/rust-lang/rust/pull/93918 +[95026]: https://github.com/rust-lang/rust/pull/95026 + +[cargo/10086]: https://github.com/rust-lang/cargo/pull/10086 +[cargo/10245]: https://github.com/rust-lang/cargo/pull/10245 +[cargo/10269]: https://github.com/rust-lang/cargo/pull/10269 +[cargo/10274]: https://github.com/rust-lang/cargo/pull/10274 +[cargo/10379]: https://github.com/rust-lang/cargo/pull/10379 + +[arc_new_cyclic]: https://doc.rust-lang.org/stable/std/sync/struct.Arc.html#method.new_cyclic +[rc_new_cyclic]: https://doc.rust-lang.org/stable/std/rc/struct.Rc.html#method.new_cyclic +[slice_escape_ascii]: https://doc.rust-lang.org/stable/std/slice/struct.EscapeAscii.html +[slice_u8_escape_ascii]: https://doc.rust-lang.org/stable/std/primitive.slice.html#method.escape_ascii +[u8_escape_ascii]: https://doc.rust-lang.org/stable/std/primitive.u8.html#method.escape_ascii +[vec_spare_capacity_mut]: https://doc.rust-lang.org/stable/std/vec/struct.Vec.html#method.spare_capacity_mut +[assume_init_drop]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.assume_init_drop +[assume_init_read]: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.html#method.assume_init_read +[i8_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.i8.html#method.abs_diff +[i16_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.i16.html#method.abs_diff +[i32_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.i32.html#method.abs_diff +[i64_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.i64.html#method.abs_diff +[i128_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.i128.html#method.abs_diff +[isize_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.isize.html#method.abs_diff +[u8_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.u8.html#method.abs_diff +[u16_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.u16.html#method.abs_diff +[u32_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.u32.html#method.abs_diff +[u64_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.u64.html#method.abs_diff +[u128_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.u128.html#method.abs_diff +[usize_abs_diff]: https://doc.rust-lang.org/stable/std/primitive.usize.html#method.abs_diff +[display_error_kind]: https://doc.rust-lang.org/stable/std/io/enum.ErrorKind.html#impl-Display +[from_u8_exit_code]: https://doc.rust-lang.org/stable/std/process/struct.ExitCode.html#impl-From%3Cu8%3E +[not_never]: https://doc.rust-lang.org/stable/std/primitive.never.html#impl-Not +[wrapping_assign_ops]: https://doc.rust-lang.org/stable/std/num/struct.Wrapping.html#trait-implementations +[is_aarch64_feature_detected]: https://doc.rust-lang.org/stable/std/arch/macro.is_aarch64_feature_detected.html + Version 1.59.0 (2022-02-24) ========================== diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index b49a5f6578f75..ac17b0d68657e 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1045,8 +1045,8 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } let hir = self.tcx.hir(); - let parent_node = hir.get_parent_node(obligation.cause.body_id); - let node = hir.find(parent_node); + let fn_hir_id = hir.get_parent_node(obligation.cause.body_id); + let node = hir.find(fn_hir_id); let Some(hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(sig, _, body_id), .. @@ -1084,16 +1084,17 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { visitor.visit_body(&body); let typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()).unwrap(); + let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id) else { return false; }; - let mut ret_types = visitor + let ret_types = visitor .returns .iter() - .filter_map(|expr| typeck_results.node_type_opt(expr.hir_id)) - .map(|ty| self.resolve_vars_if_possible(ty)); + .filter_map(|expr| Some((expr.span, typeck_results.node_type_opt(expr.hir_id)?))) + .map(|(expr_span, ty)| (expr_span, self.resolve_vars_if_possible(ty))); let (last_ty, all_returns_have_same_type, only_never_return) = ret_types.clone().fold( (None, true, true), |(last_ty, mut same, only_never_return): (std::option::Option>, bool, bool), - ty| { + (_, ty)| { let ty = self.resolve_vars_if_possible(ty); same &= !matches!(ty.kind(), ty::Error(_)) @@ -1114,39 +1115,60 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { (Some(ty), same, only_never_return && matches!(ty.kind(), ty::Never)) }, ); - let all_returns_conform_to_trait = - if let Some(ty_ret_ty) = typeck_results.node_type_opt(ret_ty.hir_id) { - match ty_ret_ty.kind() { - ty::Dynamic(predicates, _) => { - let cause = ObligationCause::misc(ret_ty.span, ret_ty.hir_id); - let param_env = ty::ParamEnv::empty(); - only_never_return - || ret_types.all(|returned_ty| { - predicates.iter().all(|predicate| { - let pred = predicate.with_self_ty(self.tcx, returned_ty); - let obl = Obligation::new(cause.clone(), param_env, pred); - self.predicate_may_hold(&obl) - }) + let mut spans_and_needs_box = vec![]; + + match liberated_sig.output().kind() { + ty::Dynamic(predicates, _) => { + let cause = ObligationCause::misc(ret_ty.span, fn_hir_id); + let param_env = ty::ParamEnv::empty(); + + if !only_never_return { + for (expr_span, return_ty) in ret_types { + let self_ty_satisfies_dyn_predicates = |self_ty| { + predicates.iter().all(|predicate| { + let pred = predicate.with_self_ty(self.tcx, self_ty); + let obl = Obligation::new(cause.clone(), param_env, pred); + self.predicate_may_hold(&obl) }) + }; + + if let ty::Adt(def, substs) = return_ty.kind() + && def.is_box() + && self_ty_satisfies_dyn_predicates(substs.type_at(0)) + { + spans_and_needs_box.push((expr_span, false)); + } else if self_ty_satisfies_dyn_predicates(return_ty) { + spans_and_needs_box.push((expr_span, true)); + } else { + return false; + } } - _ => false, } - } else { - true - }; + } + _ => return false, + }; let sm = self.tcx.sess.source_map(); - let (true, hir::TyKind::TraitObject(..), Ok(snippet), true) = ( - // Verify that we're dealing with a return `dyn Trait` - ret_ty.span.overlaps(span), - &ret_ty.kind, - sm.span_to_snippet(ret_ty.span), - // If any of the return types does not conform to the trait, then we can't - // suggest `impl Trait` nor trait objects: it is a type mismatch error. - all_returns_conform_to_trait, - ) else { + if !ret_ty.span.overlaps(span) { return false; + } + let snippet = if let hir::TyKind::TraitObject(..) = ret_ty.kind { + if let Ok(snippet) = sm.span_to_snippet(ret_ty.span) { + snippet + } else { + return false; + } + } else { + // Substitute the type, so we can print a fixup given `type Alias = dyn Trait` + let name = liberated_sig.output().to_string(); + let name = + name.strip_prefix('(').and_then(|name| name.strip_suffix(')')).unwrap_or(&name); + if !name.starts_with("dyn ") { + return false; + } + name.to_owned() }; + err.code(error_code!(E0746)); err.set_primary_message("return type cannot have an unboxed trait object"); err.children.clear(); @@ -1156,6 +1178,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let trait_obj_msg = "for information on trait objects, see \ "; + let has_dyn = snippet.split_whitespace().next().map_or(false, |s| s == "dyn"); let trait_obj = if has_dyn { &snippet[4..] } else { &snippet }; if only_never_return { @@ -1183,26 +1206,25 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { } else { if is_object_safe { // Suggest `-> Box` and `Box::new(returned_value)`. - // Get all the return values and collect their span and suggestion. - let mut suggestions: Vec<_> = visitor - .returns - .iter() - .flat_map(|expr| { - [ - (expr.span.shrink_to_lo(), "Box::new(".to_string()), - (expr.span.shrink_to_hi(), ")".to_string()), - ] - .into_iter() - }) - .collect(); - if !suggestions.is_empty() { - // Add the suggestion for the return type. - suggestions.push((ret_ty.span, format!("Box", trait_obj))); - err.multipart_suggestion( - "return a boxed trait object instead", - suggestions, - Applicability::MaybeIncorrect, - ); + err.multipart_suggestion( + "return a boxed trait object instead", + vec![ + (ret_ty.span.shrink_to_lo(), "Box<".to_string()), + (span.shrink_to_hi(), ">".to_string()), + ], + Applicability::MaybeIncorrect, + ); + for (span, needs_box) in spans_and_needs_box { + if needs_box { + err.multipart_suggestion( + "... and box this value", + vec![ + (span.shrink_to_lo(), "Box::new(".to_string()), + (span.shrink_to_hi(), ")".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } } } else { // This is currently not possible to trigger because E0038 takes precedence, but @@ -2677,13 +2699,15 @@ fn suggest_trait_object_return_type_alternatives( Applicability::MaybeIncorrect, ); if is_object_safe { - err.span_suggestion( - ret_ty, + err.multipart_suggestion( &format!( "use a boxed trait object if all return paths implement trait `{}`", trait_obj, ), - format!("Box", trait_obj), + vec![ + (ret_ty.shrink_to_lo(), "Box<".to_string()), + (ret_ty.shrink_to_hi(), ">".to_string()), + ], Applicability::MaybeIncorrect, ); } diff --git a/library/core/src/cell.rs b/library/core/src/cell.rs index 9dbb5eecd469b..2a49017de3cc8 100644 --- a/library/core/src/cell.rs +++ b/library/core/src/cell.rs @@ -1990,9 +1990,106 @@ impl const From for UnsafeCell { #[unstable(feature = "coerce_unsized", issue = "27732")] impl, U> CoerceUnsized> for UnsafeCell {} +/// [`UnsafeCell`], but [`Sync`]. +/// +/// This is just an `UnsafeCell`, except it implements `Sync` +/// if `T` implements `Sync`. +/// +/// `UnsafeCell` doesn't implement `Sync`, to prevent accidental mis-use. +/// You can use `SyncUnsafeCell` instead of `UnsafeCell` to allow it to be +/// shared between threads, if that's intentional. +/// Providing proper synchronization is still the task of the user, +/// making this type just as unsafe to use. +/// +/// See [`UnsafeCell`] for details. +#[unstable(feature = "sync_unsafe_cell", issue = "95439")] +#[repr(transparent)] +pub struct SyncUnsafeCell { + value: UnsafeCell, +} + +#[unstable(feature = "sync_unsafe_cell", issue = "95439")] +unsafe impl Sync for SyncUnsafeCell {} + +#[unstable(feature = "sync_unsafe_cell", issue = "95439")] +impl SyncUnsafeCell { + /// Constructs a new instance of `SyncUnsafeCell` which will wrap the specified value. + #[inline] + pub const fn new(value: T) -> Self { + Self { value: UnsafeCell { value } } + } + + /// Unwraps the value. + #[inline] + pub const fn into_inner(self) -> T { + self.value.into_inner() + } +} + +#[unstable(feature = "sync_unsafe_cell", issue = "95439")] +impl SyncUnsafeCell { + /// Gets a mutable pointer to the wrapped value. + /// + /// This can be cast to a pointer of any kind. + /// Ensure that the access is unique (no active references, mutable or not) + /// when casting to `&mut T`, and ensure that there are no mutations + /// or mutable aliases going on when casting to `&T` + #[inline] + pub const fn get(&self) -> *mut T { + self.value.get() + } + + /// Returns a mutable reference to the underlying data. + /// + /// This call borrows the `SyncUnsafeCell` mutably (at compile-time) which + /// guarantees that we possess the only reference. + #[inline] + pub const fn get_mut(&mut self) -> &mut T { + self.value.get_mut() + } + + /// Gets a mutable pointer to the wrapped value. + /// + /// See [`UnsafeCell::get`] for details. + #[inline] + pub const fn raw_get(this: *const Self) -> *mut T { + // We can just cast the pointer from `SyncUnsafeCell` to `T` because + // of #[repr(transparent)] on both SyncUnsafeCell and UnsafeCell. + // See UnsafeCell::raw_get. + this as *const T as *mut T + } +} + +#[unstable(feature = "sync_unsafe_cell", issue = "95439")] +impl Default for SyncUnsafeCell { + /// Creates an `SyncUnsafeCell`, with the `Default` value for T. + fn default() -> SyncUnsafeCell { + SyncUnsafeCell::new(Default::default()) + } +} + +#[unstable(feature = "sync_unsafe_cell", issue = "95439")] +#[rustc_const_unstable(feature = "const_convert", issue = "88674")] +impl const From for SyncUnsafeCell { + /// Creates a new `SyncUnsafeCell` containing the given value. + fn from(t: T) -> SyncUnsafeCell { + SyncUnsafeCell::new(t) + } +} + +#[unstable(feature = "coerce_unsized", issue = "27732")] +//#[unstable(feature = "sync_unsafe_cell", issue = "95439")] +impl, U> CoerceUnsized> for SyncUnsafeCell {} + #[allow(unused)] -fn assert_coerce_unsized(a: UnsafeCell<&i32>, b: Cell<&i32>, c: RefCell<&i32>) { +fn assert_coerce_unsized( + a: UnsafeCell<&i32>, + b: SyncUnsafeCell<&i32>, + c: Cell<&i32>, + d: RefCell<&i32>, +) { let _: UnsafeCell<&dyn Send> = a; - let _: Cell<&dyn Send> = b; - let _: RefCell<&dyn Send> = c; + let _: SyncUnsafeCell<&dyn Send> = b; + let _: Cell<&dyn Send> = c; + let _: RefCell<&dyn Send> = d; } diff --git a/library/core/src/fmt/mod.rs b/library/core/src/fmt/mod.rs index 6c1d20f36e2f6..fac959ac7347d 100644 --- a/library/core/src/fmt/mod.rs +++ b/library/core/src/fmt/mod.rs @@ -2,7 +2,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use crate::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell}; +use crate::cell::{Cell, Ref, RefCell, RefMut, SyncUnsafeCell, UnsafeCell}; use crate::char::EscapeDebugExtArgs; use crate::marker::PhantomData; use crate::mem; @@ -2400,6 +2400,13 @@ impl Debug for UnsafeCell { } } +#[unstable(feature = "sync_unsafe_cell", issue = "95439")] +impl Debug for SyncUnsafeCell { + fn fmt(&self, f: &mut Formatter<'_>) -> Result { + f.debug_struct("SyncUnsafeCell").finish_non_exhaustive() + } +} + // If you expected tests to be here, look instead at the core/tests/fmt.rs file, // it's a lot easier than creating all of the rt::Piece structures here. // There are also tests in the alloc crate, for those that need allocations. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 7dfcc36ce9391..e13f50b0d7a53 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -139,6 +139,7 @@ #![feature(const_type_id)] #![feature(const_type_name)] #![feature(const_default_impls)] +#![feature(const_unsafecell_get_mut)] #![feature(core_panic)] #![feature(duration_consts_float)] #![feature(maybe_uninit_uninit_array)] diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 17ca854768546..e56e602a662c1 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -1040,7 +1040,6 @@ impl f32 { /// # Example /// /// ``` - /// #![feature(total_cmp)] /// struct GoodBoy { /// name: String, /// weight: f32, @@ -1060,7 +1059,7 @@ impl f32 { /// # .zip([-5.0, 0.1, 10.0, 99.0, f32::INFINITY, f32::NAN].iter()) /// # .all(|(a, b)| a.to_bits() == b.to_bits())) /// ``` - #[unstable(feature = "total_cmp", issue = "72599")] + #[stable(feature = "total_cmp", since = "1.62.0")] #[must_use] #[inline] pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 350d8529de57a..8304caf649cc2 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -1056,7 +1056,6 @@ impl f64 { /// # Example /// /// ``` - /// #![feature(total_cmp)] /// struct GoodBoy { /// name: String, /// weight: f64, @@ -1076,7 +1075,7 @@ impl f64 { /// # .zip([-5.0, 0.1, 10.0, 99.0, f64::INFINITY, f64::NAN].iter()) /// # .all(|(a, b)| a.to_bits() == b.to_bits())) /// ``` - #[unstable(feature = "total_cmp", issue = "72599")] + #[stable(feature = "total_cmp", since = "1.62.0")] #[must_use] #[inline] pub fn total_cmp(&self, other: &Self) -> crate::cmp::Ordering { diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 209ea5cb0439b..410880e4f3244 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -152,9 +152,17 @@ impl *const T { /// Gets the "address" portion of the pointer. /// - /// This is equivalent to `self as usize`, which semantically discards - /// *provenance* and *address-space* information. To properly restore that information, - /// use [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr]. + /// This is similar to `self as usize`, which semantically discards *provenance* and + /// *address-space* information. However, unlike `self as usize`, it is not possible to cast the + /// returned address back to a dereferencable pointer via `as *const T`. To properly restore the + /// lost information and obtain a dereferencable pointer, use [`with_addr`][pointer::with_addr] + /// or [`map_addr`][pointer::map_addr]. + /// + /// If using those APIs is not possible because there is no way to preserve a pointer with the + /// required provenance, use [`expose_addr`][pointer::expose_addr] and + /// [`from_exposed_addr`][from_exposed_addr] instead. However, note that this makes + /// your code less portable and less amenable to tools that check for compliance with the Rust + /// memory model. /// /// On most platforms this will produce a value with the same bytes as the original /// pointer, because all the bytes are dedicated to describing the address. @@ -162,8 +170,9 @@ impl *const T { /// perform a change of representation to produce a value containing only the address /// portion of the pointer. What that means is up to the platform to define. /// - /// This API and its claimed semantics are part of the Strict Provenance experiment, - /// see the [module documentation][crate::ptr] for details. + /// This API and its claimed semantics are part of the Strict Provenance experiment, and as such + /// might change in the future (including possibly weakening this so it becomes wholly + /// equivalent to `self as usize`). See the [module documentation][crate::ptr] for details. #[must_use] #[inline] #[unstable(feature = "strict_provenance", issue = "95228")] @@ -175,6 +184,41 @@ impl *const T { self as usize } + /// Gets the "address" portion of the pointer, and 'exposes' the "provenance" part for future + /// use in [`from_exposed_addr`][]. + /// + /// This is equivalent to `self as usize`, which semantically discards *provenance* and + /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit + /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can + /// later call [`from_exposed_addr`][] to reconstitute the original pointer including its + /// provenance. (Reconstructing address space information, if required, is your responsibility.) + /// + /// Using this method means that code is *not* following Strict Provenance rules. Supporting + /// [`from_exposed_addr`][] complicates specification and reasoning and may not be supported by + /// tools that help you to stay conformant with the Rust memory model, so it is recommended to + /// use [`addr`][pointer::addr] wherever possible. + /// + /// On most platforms this will produce a value with the same bytes as the original pointer, + /// because all the bytes are dedicated to describing the address. Platforms which need to store + /// additional information in the pointer may not support this operation, since the 'expose' + /// side-effect which is required for [`from_exposed_addr`][] to work is typically not + /// available. + /// + /// This API and its claimed semantics are part of the Strict Provenance experiment, see the + /// [module documentation][crate::ptr] for details. + /// + /// [`from_exposed_addr`]: from_exposed_addr + #[must_use] + #[inline] + #[unstable(feature = "strict_provenance", issue = "95228")] + pub fn expose_addr(self) -> usize + where + T: Sized, + { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + self as usize + } + /// Creates a new pointer with the given address. /// /// This performs the same operation as an `addr as ptr` cast, but copies diff --git a/library/core/src/ptr/mod.rs b/library/core/src/ptr/mod.rs index b93c2ea961565..ba5d5700eb481 100644 --- a/library/core/src/ptr/mod.rs +++ b/library/core/src/ptr/mod.rs @@ -70,7 +70,7 @@ //! interpretation of provenance. It's ok if your code doesn't strictly conform to it.** //! //! [Strict Provenance][] is an experimental set of APIs that help tools that try -//! to validate the memory-safety of your program's execution. Notably this includes [miri][] +//! to validate the memory-safety of your program's execution. Notably this includes [Miri][] //! and [CHERI][], which can detect when you access out of bounds memory or otherwise violate //! Rust's memory model. //! @@ -136,7 +136,7 @@ //! //! The strict provenance experiment is mostly only interested in exploring stricter *spatial* //! provenance. In this sense it can be thought of as a subset of the more ambitious and -//! formal [Stacked Borrows][] research project, which is what tools like [miri][] are based on. +//! formal [Stacked Borrows][] research project, which is what tools like [Miri][] are based on. //! In particular, Stacked Borrows is necessary to properly describe what borrows are allowed //! to do and when they become invalidated. This necessarily involves much more complex //! *temporal* reasoning than simply identifying allocations. Adjusting APIs and code @@ -170,7 +170,8 @@ //! Under Strict Provenance, a usize *cannot* accurately represent a pointer, and converting from //! a pointer to a usize is generally an operation which *only* extracts the address. It is //! therefore *impossible* to construct a valid pointer from a usize because there is no way -//! to restore the address-space and provenance. +//! to restore the address-space and provenance. In other words, pointer-integer-pointer +//! roundtrips are not possible (in the sense that the resulting pointer is not dereferencable). //! //! The key insight to making this model *at all* viable is the [`with_addr`][] method: //! @@ -194,10 +195,10 @@ //! and then immediately converting back to a pointer. To make this use case more ergonomic, //! we provide the [`map_addr`][] method. //! -//! To help make it clear that code is "following" Strict Provenance semantics, we also -//! provide an [`addr`][] method which is currently equivalent to `ptr as usize`. In the -//! future we may provide a lint for pointer<->integer casts to help you audit if your -//! code conforms to strict provenance. +//! To help make it clear that code is "following" Strict Provenance semantics, we also provide an +//! [`addr`][] method which promises that the returned address is not part of a +//! pointer-usize-pointer roundtrip. In the future we may provide a lint for pointer<->integer +//! casts to help you audit if your code conforms to strict provenance. //! //! //! ## Using Strict Provenance @@ -310,6 +311,41 @@ //! For instance, ARM explicitly supports high-bit tagging, and so CHERI on ARM inherits //! that and should support it. //! +//! ## Pointer-usize-pointer roundtrips and 'exposed' provenance +//! +//! **This section is *non-normative* and is part of the [Strict Provenance] experiment.** +//! +//! As discussed above, pointer-usize-pointer roundtrips are not possible under [Strict Provenance]. +//! However, there exists legacy Rust code that is full of such roundtrips, and legacy platform APIs +//! regularly assume that `usize` can capture all the information that makes up a pointer. There +//! also might be code that cannot be ported to Strict Provenance (which is something we would [like +//! to hear about][Strict Provenance]). +//! +//! For situations like this, there is a fallback plan, a way to 'opt out' of Strict Provenance. +//! However, note that this makes your code a lot harder to specify, and the code will not work +//! (well) with tools like [Miri] and [CHERI]. +//! +//! This fallback plan is provided by the [`expose_addr`] and [`from_exposed_addr`] methods (which +//! are equivalent to `as` casts between pointers and integers). [`expose_addr`] is a lot like +//! [`addr`], but additionally adds the provenance of the pointer to a global list of 'exposed' +//! provenances. (This list is purely conceptual, it exists for the purpose of specifcying Rust but +//! is not materialized in actual executions, except in tools like [Miri].) [`from_exposed_addr`] +//! can be used to construct a pointer with one of these previously 'exposed' provenances. +//! [`from_exposed_addr`] takes only `addr: usize` as arguments, so unlike in [`with_addr`] there is +//! no indication of what the correct provenance for the returned pointer is -- and that is exactly +//! what makes pointer-usize-pointer roundtrips so tricky to rigorously specify! There is no +//! algorithm that decides which provenance will be used. You can think of this as "guessing" the +//! right provenance, and the guess will be "maximally in your favor", in the sense that if there is +//! any way to avoid undefined behavior, then that is the guess that will be taken. However, if +//! there is *no* previously 'exposed' provenance that justifies the way the returned pointer will +//! be used, the program has undefined behavior. +//! +//! Using [`expose_addr`] or [`from_exposed_addr`] (or the equivalent `as` casts) means that code is +//! *not* following Strict Provenance rules. The goal of the Strict Provenance experiment is to +//! determine whether it is possible to use Rust without [`expose_addr`] and [`from_exposed_addr`]. +//! If this is successful, it would be a major win for avoiding specification complexity and to +//! facilitate adoption of tools like [CHERI] and [Miri] that can be a big help in increasing the +//! confidence in (unsafe) Rust code. //! //! [aliasing]: ../../nomicon/aliasing.html //! [book]: ../../book/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer @@ -322,7 +358,9 @@ //! [`map_addr`]: pointer::map_addr //! [`addr`]: pointer::addr //! [`ptr::invalid`]: core::ptr::invalid -//! [miri]: https://github.com/rust-lang/miri +//! [`expose_addr`]: pointer::expose_addr +//! [`from_exposed_addr`]: from_exposed_addr +//! [Miri]: https://github.com/rust-lang/miri //! [CHERI]: https://www.cl.cam.ac.uk/research/security/ctsrd/cheri/ //! [Strict Provenance]: https://github.com/rust-lang/rust/issues/95228 //! [Stacked Borrows]: https://plv.mpi-sws.org/rustbelt/stacked-borrows/ @@ -547,6 +585,78 @@ pub const fn invalid_mut(addr: usize) -> *mut T { addr as *mut T } +/// Convert an address back to a pointer, picking up a previously 'exposed' provenance. +/// +/// This is equivalent to `addr as *const T`. The provenance of the returned pointer is that of *any* +/// pointer that was previously passed to [`expose_addr`][pointer::expose_addr] or a `ptr as usize` +/// cast. If there is no previously 'exposed' provenance that justifies the way this pointer will be +/// used, the program has undefined behavior. Note that there is no algorithm that decides which +/// provenance will be used. You can think of this as "guessing" the right provenance, and the guess +/// will be "maximally in your favor", in the sense that if there is any way to avoid undefined +/// behavior, then that is the guess that will be taken. +/// +/// On platforms with multiple address spaces, it is your responsibility to ensure that the +/// address makes sense in the address space that this pointer will be used with. +/// +/// Using this method means that code is *not* following strict provenance rules. "Guessing" a +/// suitable provenance complicates specification and reasoning and may not be supported by +/// tools that help you to stay conformant with the Rust memory model, so it is recommended to +/// use [`with_addr`][pointer::with_addr] wherever possible. +/// +/// On most platforms this will produce a value with the same bytes as the address. Platforms +/// which need to store additional information in a pointer may not support this operation, +/// since it is generally not possible to actually *compute* which provenance the returned +/// pointer has to pick up. +/// +/// This API and its claimed semantics are part of the Strict Provenance experiment, see the +/// [module documentation][crate::ptr] for details. +#[must_use] +#[inline] +#[unstable(feature = "strict_provenance", issue = "95228")] +pub fn from_exposed_addr(addr: usize) -> *const T +where + T: Sized, +{ + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + addr as *const T +} + +/// Convert an address back to a mutable pointer, picking up a previously 'exposed' provenance. +/// +/// This is equivalent to `addr as *mut T`. The provenance of the returned pointer is that of *any* +/// pointer that was previously passed to [`expose_addr`][pointer::expose_addr] or a `ptr as usize` +/// cast. If there is no previously 'exposed' provenance that justifies the way this pointer will be +/// used, the program has undefined behavior. Note that there is no algorithm that decides which +/// provenance will be used. You can think of this as "guessing" the right provenance, and the guess +/// will be "maximally in your favor", in the sense that if there is any way to avoid undefined +/// behavior, then that is the guess that will be taken. +/// +/// On platforms with multiple address spaces, it is your responsibility to ensure that the +/// address makes sense in the address space that this pointer will be used with. +/// +/// Using this method means that code is *not* following strict provenance rules. "Guessing" a +/// suitable provenance complicates specification and reasoning and may not be supported by +/// tools that help you to stay conformant with the Rust memory model, so it is recommended to +/// use [`with_addr`][pointer::with_addr] wherever possible. +/// +/// On most platforms this will produce a value with the same bytes as the address. Platforms +/// which need to store additional information in a pointer may not support this operation, +/// since it is generally not possible to actually *compute* which provenance the returned +/// pointer has to pick up. +/// +/// This API and its claimed semantics are part of the Strict Provenance experiment, see the +/// [module documentation][crate::ptr] for details. +#[must_use] +#[inline] +#[unstable(feature = "strict_provenance", issue = "95228")] +pub fn from_exposed_addr_mut(addr: usize) -> *mut T +where + T: Sized, +{ + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + addr as *mut T +} + /// Forms a raw slice from a pointer and a length. /// /// The `len` argument is the number of **elements**, not the number of bytes. @@ -763,7 +873,7 @@ pub const unsafe fn swap_nonoverlapping(x: *mut T, y: *mut T, count: usize) { ); } - // NOTE(scottmcm) MIRI is disabled here as reading in smaller units is a + // NOTE(scottmcm) Miri is disabled here as reading in smaller units is a // pessimization for it. Also, if the type contains any unaligned pointers, // copying those over multiple reads is difficult to support. #[cfg(not(miri))] diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index bfc89625935d9..fce399f074258 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -156,9 +156,17 @@ impl *mut T { /// Gets the "address" portion of the pointer. /// - /// This is equivalent to `self as usize`, which semantically discards - /// *provenance* and *address-space* information. To properly restore that information, - /// use [`with_addr`][pointer::with_addr] or [`map_addr`][pointer::map_addr]. + /// This is similar to `self as usize`, which semantically discards *provenance* and + /// *address-space* information. However, unlike `self as usize`, it is not possible to cast the + /// returned address back to a dereferencable pointer via `as *mut T`. To properly restore the + /// lost information and obtain a dereferencable pointer, use [`with_addr`][pointer::with_addr] + /// or [`map_addr`][pointer::map_addr]. + /// + /// If using those APIs is not possible because there is no way to preserve a pointer with the + /// required provenance, use [`expose_addr`][pointer::expose_addr] and + /// [`from_exposed_addr_mut`][from_exposed_addr_mut] instead. However, note that this makes + /// your code less portable and less amenable to tools that check for compliance with the Rust + /// memory model. /// /// On most platforms this will produce a value with the same bytes as the original /// pointer, because all the bytes are dedicated to describing the address. @@ -166,8 +174,9 @@ impl *mut T { /// perform a change of representation to produce a value containing only the address /// portion of the pointer. What that means is up to the platform to define. /// - /// This API and its claimed semantics are part of the Strict Provenance experiment, - /// see the [module documentation][crate::ptr] for details. + /// This API and its claimed semantics are part of the Strict Provenance experiment, and as such + /// might change in the future (including possibly weakening this so it becomes wholly + /// equivalent to `self as usize`). See the [module documentation][crate::ptr] for details. #[must_use] #[inline] #[unstable(feature = "strict_provenance", issue = "95228")] @@ -179,6 +188,41 @@ impl *mut T { self as usize } + /// Gets the "address" portion of the pointer, and 'exposes' the "provenance" part for future + /// use in [`from_exposed_addr`][]. + /// + /// This is equivalent to `self as usize`, which semantically discards *provenance* and + /// *address-space* information. Furthermore, this (like the `as` cast) has the implicit + /// side-effect of marking the provenance as 'exposed', so on platforms that support it you can + /// later call [`from_exposed_addr_mut`][] to reconstitute the original pointer including its + /// provenance. (Reconstructing address space information, if required, is your responsibility.) + /// + /// Using this method means that code is *not* following Strict Provenance rules. Supporting + /// [`from_exposed_addr_mut`][] complicates specification and reasoning and may not be supported + /// by tools that help you to stay conformant with the Rust memory model, so it is recommended + /// to use [`addr`][pointer::addr] wherever possible. + /// + /// On most platforms this will produce a value with the same bytes as the original pointer, + /// because all the bytes are dedicated to describing the address. Platforms which need to store + /// additional information in the pointer may not support this operation, since the 'expose' + /// side-effect which is required for [`from_exposed_addr_mut`][] to work is typically not + /// available. + /// + /// This API and its claimed semantics are part of the Strict Provenance experiment, see the + /// [module documentation][crate::ptr] for details. + /// + /// [`from_exposed_addr_mut`]: from_exposed_addr_mut + #[must_use] + #[inline] + #[unstable(feature = "strict_provenance", issue = "95228")] + pub fn expose_addr(self) -> usize + where + T: Sized, + { + // FIXME(strict_provenance_magic): I am magic and should be a compiler intrinsic. + self as usize + } + /// Creates a new pointer with the given address. /// /// This performs the same operation as an `addr as ptr` cast, but copies diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index e1c1800438354..1016fbc99d826 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -285,7 +285,6 @@ #![feature(std_internals)] #![feature(str_internals)] #![feature(strict_provenance)] -#![feature(total_cmp)] // // Library features (alloc): #![feature(alloc_layout_extra)] diff --git a/library/std/src/sys/windows/c.rs b/library/std/src/sys/windows/c.rs index 0edf43e5d9dd5..b59b15f7ddfaf 100644 --- a/library/std/src/sys/windows/c.rs +++ b/library/std/src/sys/windows/c.rs @@ -326,6 +326,12 @@ impl Default for IO_STATUS_BLOCK { } } +pub type LPOVERLAPPED_COMPLETION_ROUTINE = unsafe extern "C" fn( + dwErrorCode: DWORD, + dwNumberOfBytesTransfered: DWORD, + lpOverlapped: *mut OVERLAPPED, +); + #[repr(C)] #[cfg(not(target_pointer_width = "64"))] pub struct WSADATA { @@ -891,6 +897,7 @@ extern "system" { pub fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; pub fn SwitchToThread() -> BOOL; pub fn Sleep(dwMilliseconds: DWORD); + pub fn SleepEx(dwMilliseconds: DWORD, bAlertable: BOOL) -> DWORD; pub fn GetProcessId(handle: HANDLE) -> DWORD; pub fn CopyFileExW( lpExistingFileName: LPCWSTR, @@ -957,6 +964,13 @@ extern "system" { lpNumberOfBytesRead: LPDWORD, lpOverlapped: LPOVERLAPPED, ) -> BOOL; + pub fn ReadFileEx( + hFile: BorrowedHandle<'_>, + lpBuffer: LPVOID, + nNumberOfBytesToRead: DWORD, + lpOverlapped: LPOVERLAPPED, + lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE, + ) -> BOOL; pub fn WriteFile( hFile: BorrowedHandle<'_>, lpBuffer: LPVOID, @@ -964,6 +978,13 @@ extern "system" { lpNumberOfBytesWritten: LPDWORD, lpOverlapped: LPOVERLAPPED, ) -> BOOL; + pub fn WriteFileEx( + hFile: BorrowedHandle<'_>, + lpBuffer: LPVOID, + nNumberOfBytesToWrite: DWORD, + lpOverlapped: LPOVERLAPPED, + lpCompletionRoutine: LPOVERLAPPED_COMPLETION_ROUTINE, + ) -> BOOL; pub fn CloseHandle(hObject: HANDLE) -> BOOL; pub fn MoveFileExW(lpExistingFileName: LPCWSTR, lpNewFileName: LPCWSTR, dwFlags: DWORD) -> BOOL; diff --git a/library/std/src/sys/windows/pipe.rs b/library/std/src/sys/windows/pipe.rs index 63d3d6c5ed42f..1723a331a3223 100644 --- a/library/std/src/sys/windows/pipe.rs +++ b/library/std/src/sys/windows/pipe.rs @@ -173,6 +173,15 @@ fn random_number() -> usize { } } +// Abstracts over `ReadFileEx` and `WriteFileEx` +type AlertableIoFn = unsafe extern "system" fn( + BorrowedHandle<'_>, + c::LPVOID, + c::DWORD, + c::LPOVERLAPPED, + c::LPOVERLAPPED_COMPLETION_ROUTINE, +) -> c::BOOL; + impl AnonPipe { pub fn handle(&self) -> &Handle { &self.inner @@ -182,7 +191,19 @@ impl AnonPipe { } pub fn read(&self, buf: &mut [u8]) -> io::Result { - self.inner.read(buf) + let result = unsafe { + let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD; + self.alertable_io_internal(c::ReadFileEx, buf.as_mut_ptr() as _, len) + }; + + match result { + // The special treatment of BrokenPipe is to deal with Windows + // pipe semantics, which yields this error when *reading* from + // a pipe after the other end has closed; we interpret that as + // EOF on the pipe. + Err(ref e) if e.kind() == io::ErrorKind::BrokenPipe => Ok(0), + _ => result, + } } pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result { @@ -195,7 +216,10 @@ impl AnonPipe { } pub fn write(&self, buf: &[u8]) -> io::Result { - self.inner.write(buf) + unsafe { + let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD; + self.alertable_io_internal(c::WriteFileEx, buf.as_ptr() as _, len) + } } pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result { @@ -206,6 +230,99 @@ impl AnonPipe { pub fn is_write_vectored(&self) -> bool { self.inner.is_write_vectored() } + + /// Synchronizes asynchronous reads or writes using our anonymous pipe. + /// + /// This is a wrapper around [`ReadFileEx`] or [`WriteFileEx`] that uses + /// [Asynchronous Procedure Call] (APC) to synchronize reads or writes. + /// + /// Note: This should not be used for handles we don't create. + /// + /// # Safety + /// + /// `buf` must be a pointer to a buffer that's valid for reads or writes + /// up to `len` bytes. The `AlertableIoFn` must be either `ReadFileEx` or `WriteFileEx` + /// + /// [`ReadFileEx`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-readfileex + /// [`WriteFileEx`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-writefileex + /// [Asynchronous Procedure Call]: https://docs.microsoft.com/en-us/windows/win32/sync/asynchronous-procedure-calls + unsafe fn alertable_io_internal( + &self, + io: AlertableIoFn, + buf: c::LPVOID, + len: c::DWORD, + ) -> io::Result { + // Use "alertable I/O" to synchronize the pipe I/O. + // This has four steps. + // + // STEP 1: Start the asynchronous I/O operation. + // This simply calls either `ReadFileEx` or `WriteFileEx`, + // giving it a pointer to the buffer and callback function. + // + // STEP 2: Enter an alertable state. + // The callback set in step 1 will not be called until the thread + // enters an "alertable" state. This can be done using `SleepEx`. + // + // STEP 3: The callback + // Once the I/O is complete and the thread is in an alertable state, + // the callback will be run on the same thread as the call to + // `ReadFileEx` or `WriteFileEx` done in step 1. + // In the callback we simply set the result of the async operation. + // + // STEP 4: Return the result. + // At this point we'll have a result from the callback function + // and can simply return it. Note that we must not return earlier, + // while the I/O is still in progress. + + // The result that will be set from the asynchronous callback. + let mut async_result: Option = None; + struct AsyncResult { + error: u32, + transfered: u32, + } + + // STEP 3: The callback. + unsafe extern "C" fn callback( + dwErrorCode: u32, + dwNumberOfBytesTransfered: u32, + lpOverlapped: *mut c::OVERLAPPED, + ) { + // Set `async_result` using a pointer smuggled through `hEvent`. + let result = AsyncResult { error: dwErrorCode, transfered: dwNumberOfBytesTransfered }; + *(*lpOverlapped).hEvent.cast::>() = Some(result); + } + + // STEP 1: Start the I/O operation. + let mut overlapped: c::OVERLAPPED = crate::mem::zeroed(); + // `hEvent` is unused by `ReadFileEx` and `WriteFileEx`. + // Therefore the documentation suggests using it to smuggle a pointer to the callback. + overlapped.hEvent = &mut async_result as *mut _ as *mut _; + + // Asynchronous read of the pipe. + // If successful, `callback` will be called once it completes. + let result = io(self.inner.as_handle(), buf, len, &mut overlapped, callback); + if result == c::FALSE { + // We can return here because the call failed. + // After this we must not return until the I/O completes. + return Err(io::Error::last_os_error()); + } + + // Wait indefinitely for the result. + let result = loop { + // STEP 2: Enter an alertable state. + // The second parameter of `SleepEx` is used to make this sleep alertable. + c::SleepEx(c::INFINITE, c::TRUE); + if let Some(result) = async_result { + break result; + } + }; + // STEP 4: Return the result. + // `async_result` is always `Some` at this point + match result.error { + c::ERROR_SUCCESS => Ok(result.transfered as usize), + error => Err(io::Error::from_raw_os_error(error as _)), + } + } } pub fn read2(p1: AnonPipe, v1: &mut Vec, p2: AnonPipe, v2: &mut Vec) -> io::Result<()> { diff --git a/library/test/src/lib.rs b/library/test/src/lib.rs index 889f7cb9db941..0c748da1a59cc 100644 --- a/library/test/src/lib.rs +++ b/library/test/src/lib.rs @@ -21,7 +21,6 @@ #![feature(staged_api)] #![feature(process_exitcode_internals)] #![feature(test)] -#![feature(total_cmp)] // Public reexports pub use self::bench::{black_box, Bencher}; diff --git a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr index 0d4f82bfc153f..f90399b6b9458 100644 --- a/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr +++ b/src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr @@ -81,7 +81,7 @@ LL | fn bak() -> impl Trait { unimplemented!() } help: use a boxed trait object if all return paths implement trait `Trait` | LL | fn bak() -> Box { unimplemented!() } - | ~~~~~~~~~~~~~~ + | ++++ + error[E0746]: return type cannot have an unboxed trait object --> $DIR/dyn-trait-return-should-be-impl-trait.rs:19:13 @@ -95,12 +95,16 @@ LL | fn bal() -> dyn Trait { = note: you can create a new `enum` with a variant for each returned type help: return a boxed trait object instead | -LL ~ fn bal() -> Box { -LL | if true { -LL ~ return Box::new(Struct); -LL | } -LL ~ Box::new(42) +LL | fn bal() -> Box { + | ++++ + +help: ... and box this value + | +LL | return Box::new(Struct); + | +++++++++ + +help: ... and box this value | +LL | Box::new(42) + | +++++++++ + error[E0308]: `if` and `else` have incompatible types --> $DIR/dyn-trait-return-should-be-impl-trait.rs:29:9 @@ -126,12 +130,16 @@ LL | fn bax() -> dyn Trait { = note: you can create a new `enum` with a variant for each returned type help: return a boxed trait object instead | -LL ~ fn bax() -> Box { -LL | if true { -LL ~ Box::new(Struct) -LL | } else { -LL ~ Box::new(42) +LL | fn bax() -> Box { + | ++++ + +help: ... and box this value + | +LL | Box::new(Struct) + | +++++++++ + +help: ... and box this value | +LL | Box::new(42) + | +++++++++ + error[E0308]: mismatched types --> $DIR/dyn-trait-return-should-be-impl-trait.rs:34:16 diff --git a/src/test/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr b/src/test/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr index 0c595f441ba8e..10510c1754eda 100644 --- a/src/test/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr +++ b/src/test/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr @@ -103,13 +103,16 @@ LL | fn hat() -> dyn std::fmt::Display { = note: you can create a new `enum` with a variant for each returned type help: return a boxed trait object instead | -LL ~ fn hat() -> Box { -LL | match 13 { -LL | 0 => { -LL ~ return Box::new(0i32); -LL | } -LL | _ => { - ... +LL | fn hat() -> Box { + | ++++ + +help: ... and box this value + | +LL | return Box::new(0i32); + | +++++++++ + +help: ... and box this value + | +LL | Box::new(1u32) + | +++++++++ + error[E0308]: `match` arms have incompatible types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:80:14 @@ -135,12 +138,20 @@ LL | fn pug() -> dyn std::fmt::Display { = note: you can create a new `enum` with a variant for each returned type help: return a boxed trait object instead | -LL ~ fn pug() -> Box { -LL | match 13 { -LL ~ 0 => Box::new(0i32), -LL ~ 1 => Box::new(1u32), -LL ~ _ => Box::new(2u32), +LL | fn pug() -> Box { + | ++++ + +help: ... and box this value + | +LL | 0 => Box::new(0i32), + | +++++++++ + +help: ... and box this value | +LL | 1 => Box::new(1u32), + | +++++++++ + +help: ... and box this value + | +LL | _ => Box::new(2u32), + | +++++++++ + error[E0308]: `if` and `else` have incompatible types --> $DIR/point-to-type-err-cause-on-impl-trait-return.rs:89:9 @@ -166,12 +177,16 @@ LL | fn man() -> dyn std::fmt::Display { = note: you can create a new `enum` with a variant for each returned type help: return a boxed trait object instead | -LL ~ fn man() -> Box { -LL | if false { -LL ~ Box::new(0i32) -LL | } else { -LL ~ Box::new(1u32) +LL | fn man() -> Box { + | ++++ + +help: ... and box this value + | +LL | Box::new(0i32) + | +++++++++ + +help: ... and box this value | +LL | Box::new(1u32) + | +++++++++ + error: aborting due to 14 previous errors diff --git a/src/test/ui/issues/issue-18107.stderr b/src/test/ui/issues/issue-18107.stderr index 1eb6822b8a11a..28478457b296d 100644 --- a/src/test/ui/issues/issue-18107.stderr +++ b/src/test/ui/issues/issue-18107.stderr @@ -15,7 +15,7 @@ LL | impl AbstractRenderer help: use a boxed trait object if all return paths implement trait `AbstractRenderer` | LL | Box - | + | ++++ + error: aborting due to previous error diff --git a/src/test/ui/unsized/box-instead-of-dyn-fn.rs b/src/test/ui/unsized/box-instead-of-dyn-fn.rs new file mode 100644 index 0000000000000..2fa741bc1c50b --- /dev/null +++ b/src/test/ui/unsized/box-instead-of-dyn-fn.rs @@ -0,0 +1,15 @@ +use std::fmt::Debug; + +// Test to suggest boxing the return type, and the closure branch of the `if` + +fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> dyn Fn() + 'a { + //~^ ERROR return type cannot have an unboxed trait object + if a % 2 == 0 { + move || println!("{a}") + } else { + Box::new(move || println!("{}", b)) + //~^ ERROR `if` and `else` have incompatible types + } +} + +fn main() {} diff --git a/src/test/ui/unsized/box-instead-of-dyn-fn.stderr b/src/test/ui/unsized/box-instead-of-dyn-fn.stderr new file mode 100644 index 0000000000000..80f61cb3eae11 --- /dev/null +++ b/src/test/ui/unsized/box-instead-of-dyn-fn.stderr @@ -0,0 +1,39 @@ +error[E0308]: `if` and `else` have incompatible types + --> $DIR/box-instead-of-dyn-fn.rs:10:9 + | +LL | / if a % 2 == 0 { +LL | | move || println!("{a}") + | | ----------------------- expected because of this +LL | | } else { +LL | | Box::new(move || println!("{}", b)) + | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found struct `Box` +LL | | +LL | | } + | |_____- `if` and `else` have incompatible types + | + = note: expected type `[closure@$DIR/box-instead-of-dyn-fn.rs:8:9: 8:32]` + found struct `Box<[closure@$DIR/box-instead-of-dyn-fn.rs:10:18: 10:43]>` + +error[E0746]: return type cannot have an unboxed trait object + --> $DIR/box-instead-of-dyn-fn.rs:5:56 + | +LL | fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> dyn Fn() + 'a { + | ^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = note: for information on trait objects, see + = note: if all the returned values were of the same type you could use `impl Fn() + 'a` as the return type + = note: for information on `impl Trait`, see + = note: you can create a new `enum` with a variant for each returned type +help: return a boxed trait object instead + | +LL | fn print_on_or_the_other<'a>(a: i32, b: &'a String) -> Box { + | ++++ + +help: ... and box this value + | +LL | Box::new(move || println!("{a}")) + | +++++++++ + + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0746. +For more information about an error, try `rustc --explain E0308`. diff --git a/src/test/ui/unsized/issue-91801.rs b/src/test/ui/unsized/issue-91801.rs new file mode 100644 index 0000000000000..096b1a93574fc --- /dev/null +++ b/src/test/ui/unsized/issue-91801.rs @@ -0,0 +1,19 @@ +pub struct Something; + +type Validator<'a> = dyn 'a + Send + Sync + Fn(&'a Something) -> Result<(), ()>; + +pub static ALL_VALIDATORS: &[(&'static str, &'static Validator)] = + &[("validate that credits and debits balance", &validate_something)]; + +fn or<'a>(first: &'static Validator<'a>, second: &'static Validator<'a>) -> Validator<'a> { + //~^ ERROR return type cannot have an unboxed trait object + return Box::new(move |something: &'_ Something| -> Result<(), ()> { + first(something).or_else(|_| second(something)) + }); +} + +fn validate_something(_: &Something) -> Result<(), ()> { + Ok(()) +} + +fn main() {} diff --git a/src/test/ui/unsized/issue-91801.stderr b/src/test/ui/unsized/issue-91801.stderr new file mode 100644 index 0000000000000..e854514958629 --- /dev/null +++ b/src/test/ui/unsized/issue-91801.stderr @@ -0,0 +1,15 @@ +error[E0746]: return type cannot have an unboxed trait object + --> $DIR/issue-91801.rs:8:77 + | +LL | fn or<'a>(first: &'static Validator<'a>, second: &'static Validator<'a>) -> Validator<'a> { + | ^^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = note: for information on `impl Trait`, see +help: use `impl Fn(&'a Something) -> Result<(), ()> + Send + Sync + 'a` as the return type, as all return paths are of type `Box<[closure@$DIR/issue-91801.rs:10:21: 12:6]>`, which implements `Fn(&'a Something) -> Result<(), ()> + Send + Sync + 'a` + | +LL | fn or<'a>(first: &'static Validator<'a>, second: &'static Validator<'a>) -> impl Fn(&'a Something) -> Result<(), ()> + Send + Sync + 'a { + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0746`. diff --git a/src/test/ui/unsized/issue-91803.rs b/src/test/ui/unsized/issue-91803.rs new file mode 100644 index 0000000000000..c74897cc4bc50 --- /dev/null +++ b/src/test/ui/unsized/issue-91803.rs @@ -0,0 +1,8 @@ +trait Foo<'a> {} + +fn or<'a>(first: &'static dyn Foo<'a>) -> dyn Foo<'a> { + //~^ ERROR return type cannot have an unboxed trait object + return Box::new(panic!()); +} + +fn main() {} diff --git a/src/test/ui/unsized/issue-91803.stderr b/src/test/ui/unsized/issue-91803.stderr new file mode 100644 index 0000000000000..2dad9e8929421 --- /dev/null +++ b/src/test/ui/unsized/issue-91803.stderr @@ -0,0 +1,15 @@ +error[E0746]: return type cannot have an unboxed trait object + --> $DIR/issue-91803.rs:3:43 + | +LL | fn or<'a>(first: &'static dyn Foo<'a>) -> dyn Foo<'a> { + | ^^^^^^^^^^^ doesn't have a size known at compile-time + | + = note: for information on `impl Trait`, see +help: use `impl Foo<'a>` as the return type, as all return paths are of type `Box<_>`, which implements `Foo<'a>` + | +LL | fn or<'a>(first: &'static dyn Foo<'a>) -> impl Foo<'a> { + | ~~~~~~~~~~~~ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0746`.