diff --git a/compiler/rustc_codegen_cranelift/src/unsize.rs b/compiler/rustc_codegen_cranelift/src/unsize.rs index 8cfe93b4d9c91..339628053a953 100644 --- a/compiler/rustc_codegen_cranelift/src/unsize.rs +++ b/compiler/rustc_codegen_cranelift/src/unsize.rs @@ -34,7 +34,22 @@ pub(crate) fn unsized_info<'tcx>( let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); if data_a.principal_def_id() == data_b.principal_def_id() { - // A NOP cast that doesn't actually change anything, should be allowed even with invalid vtables. + // Codegen takes advantage of the additional assumption, where if the + // principal trait def id of what's being casted doesn't change, + // then we don't need to adjust the vtable at all. This + // corresponds to the fact that `dyn Tr: Unsize>` + // requires that `A = B`; we don't allow *upcasting* objects + // between the same trait with different args. If we, for + // some reason, were to relax the `Unsize` trait, it could become + // unsound, so let's assert here that the trait refs are *equal*. + // + // We can use `assert_eq` because the binders should have been anonymized, + // and because higher-ranked equality now requires the binders are equal. + debug_assert_eq!( + data_a.principal(), + data_b.principal(), + "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}" + ); return old_info; } diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 892dfb91201e3..69693230ce070 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -1087,16 +1087,17 @@ fn link_natively( let strip = sess.opts.cg.strip; if sess.target.is_like_osx { + let stripcmd = "/usr/bin/strip"; match (strip, crate_type) { (Strip::Debuginfo, _) => { - strip_symbols_with_external_utility(sess, "strip", out_filename, Some("-S")) + strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-S")) } // Per the manpage, `-x` is the maximum safe strip level for dynamic libraries. (#93988) (Strip::Symbols, CrateType::Dylib | CrateType::Cdylib | CrateType::ProcMacro) => { - strip_symbols_with_external_utility(sess, "strip", out_filename, Some("-x")) + strip_symbols_with_external_utility(sess, stripcmd, out_filename, Some("-x")) } (Strip::Symbols, _) => { - strip_symbols_with_external_utility(sess, "strip", out_filename, None) + strip_symbols_with_external_utility(sess, stripcmd, out_filename, None) } (Strip::None, _) => {} } diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index fcf48d3e4a31c..5c67600e4eec1 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -125,8 +125,28 @@ fn unsized_info<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( let old_info = old_info.expect("unsized_info: missing old info for trait upcasting coercion"); if data_a.principal_def_id() == data_b.principal_def_id() { - // A NOP cast that doesn't actually change anything, should be allowed even with - // invalid vtables. + // Codegen takes advantage of the additional assumption, where if the + // principal trait def id of what's being casted doesn't change, + // then we don't need to adjust the vtable at all. This + // corresponds to the fact that `dyn Tr: Unsize>` + // requires that `A = B`; we don't allow *upcasting* objects + // between the same trait with different args. If we, for + // some reason, were to relax the `Unsize` trait, it could become + // unsound, so let's assert here that the trait refs are *equal*. + // + // We can use `assert_eq` because the binders should have been anonymized, + // and because higher-ranked equality now requires the binders are equal. + debug_assert_eq!( + data_a.principal(), + data_b.principal(), + "NOP unsize vtable changed principal trait ref: {data_a} -> {data_b}" + ); + + // A NOP cast that doesn't actually change anything, let's avoid any + // unnecessary work. This relies on the assumption that if the principal + // traits are equal, then the associated type bounds (`dyn Trait`) + // are also equal, which is ensured by the fact that normalization is + // a function and we do not allow overlapping impls. return old_info; } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index eda0b8c75f322..e353be6a105f0 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -4,7 +4,8 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::LangItem; use rustc_index::IndexVec; use rustc_index::bit_set::BitSet; -use rustc_infer::traits::Reveal; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_infer::traits::{Obligation, ObligationCause, Reveal}; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::visit::{NonUseContext, PlaceContext, Visitor}; use rustc_middle::mir::*; @@ -16,6 +17,8 @@ use rustc_middle::ty::{ use rustc_middle::{bug, span_bug}; use rustc_target::abi::{FIRST_VARIANT, Size}; use rustc_target::spec::abi::Abi; +use rustc_trait_selection::traits::ObligationCtxt; +use rustc_type_ir::Upcast; use crate::util::{is_within_packed, relate_types}; @@ -586,6 +589,22 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { crate::util::relate_types(self.tcx, self.param_env, variance, src, dest) } + + /// Check that the given predicate definitely holds in the param-env of this MIR body. + fn predicate_must_hold_modulo_regions( + &self, + pred: impl Upcast, ty::Predicate<'tcx>>, + ) -> bool { + let infcx = self.tcx.infer_ctxt().build(); + let ocx = ObligationCtxt::new(&infcx); + ocx.register_obligation(Obligation::new( + self.tcx, + ObligationCause::dummy(), + self.param_env, + pred, + )); + ocx.select_all_or_error().is_empty() + } } impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { @@ -1202,8 +1221,18 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } CastKind::PointerCoercion(PointerCoercion::Unsize, _) => { - // This is used for all `CoerceUnsized` types, - // not just pointers/references, so is hard to check. + // Pointers being unsize coerced should at least implement + // `CoerceUnsized`. + if !self.predicate_must_hold_modulo_regions(ty::TraitRef::new( + self.tcx, + self.tcx.require_lang_item( + LangItem::CoerceUnsized, + Some(self.body.source_info(location).span), + ), + [op_ty, *target_type], + )) { + self.fail(location, format!("Unsize coercion, but `{op_ty}` isn't coercible to `{target_type}`")); + } } CastKind::PointerCoercion(PointerCoercion::DynStar, _) => { // FIXME(dyn-star): make sure nothing needs to be done here. diff --git a/library/core/src/char/methods.rs b/library/core/src/char/methods.rs index 3dcaab6a12beb..55e67e5c255e9 100644 --- a/library/core/src/char/methods.rs +++ b/library/core/src/char/methods.rs @@ -606,6 +606,7 @@ impl char { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_char_len_utf", since = "1.52.0")] #[inline] + #[must_use] pub const fn len_utf8(self) -> usize { len_utf8(self as u32) } @@ -637,6 +638,7 @@ impl char { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_stable(feature = "const_char_len_utf", since = "1.52.0")] #[inline] + #[must_use] pub const fn len_utf16(self) -> usize { len_utf16(self as u32) } @@ -1738,6 +1740,7 @@ impl EscapeDebugExtArgs { } #[inline] +#[must_use] const fn len_utf8(code: u32) -> usize { match code { ..MAX_ONE_B => 1, @@ -1748,6 +1751,7 @@ const fn len_utf8(code: u32) -> usize { } #[inline] +#[must_use] const fn len_utf16(code: u32) -> usize { if (code & 0xFFFF) == code { 1 } else { 2 } } diff --git a/library/core/src/slice/sort/shared/mod.rs b/library/core/src/slice/sort/shared/mod.rs index e0f8d475a2e30..e2cdcb3dd511d 100644 --- a/library/core/src/slice/sort/shared/mod.rs +++ b/library/core/src/slice/sort/shared/mod.rs @@ -1,4 +1,4 @@ -#![cfg_attr(feature = "optimize_for_size", allow(dead_code))] +#![cfg_attr(any(feature = "optimize_for_size", target_pointer_width = "16"), allow(dead_code))] use crate::marker::Freeze; diff --git a/library/core/src/slice/sort/stable/mod.rs b/library/core/src/slice/sort/stable/mod.rs index e13fbc37e80c4..7adcc83b818d1 100644 --- a/library/core/src/slice/sort/stable/mod.rs +++ b/library/core/src/slice/sort/stable/mod.rs @@ -1,22 +1,22 @@ //! This module contains the entry points for `slice::sort`. -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::cmp; use crate::intrinsics; use crate::mem::{self, MaybeUninit, SizedTypeProperties}; -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::smallsort::{ SMALL_SORT_GENERAL_SCRATCH_LEN, StableSmallSortTypeImpl, insertion_sort_shift_left, }; pub(crate) mod merge; -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] pub(crate) mod drift; -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] pub(crate) mod quicksort; -#[cfg(feature = "optimize_for_size")] +#[cfg(any(feature = "optimize_for_size", target_pointer_width = "16"))] pub(crate) mod tiny; /// Stable sort called driftsort by Orson Peters and Lukas Bergdoll. @@ -45,7 +45,7 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less cfg_if! { if #[cfg(target_pointer_width = "16")] { - let heap_buf = BufT::with_capacity(alloc_len); + let mut heap_buf = BufT::with_capacity(alloc_len); let scratch = heap_buf.as_uninit_slice_mut(); } else { // For small inputs 4KiB of stack storage suffices, which allows us to avoid @@ -85,7 +85,7 @@ pub fn sort bool, BufT: BufGuard>(v: &mut [T], is_less /// /// Deliberately don't inline the main sorting routine entrypoint to ensure the /// inlined insertion sort i-cache footprint remains minimal. -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] #[inline(never)] fn driftsort_main bool, BufT: BufGuard>(v: &mut [T], is_less: &mut F) { // By allocating n elements of memory we can ensure the entire input can diff --git a/library/core/src/slice/sort/unstable/mod.rs b/library/core/src/slice/sort/unstable/mod.rs index 8bbd85443d478..2eb653c4601a7 100644 --- a/library/core/src/slice/sort/unstable/mod.rs +++ b/library/core/src/slice/sort/unstable/mod.rs @@ -2,9 +2,9 @@ use crate::intrinsics; use crate::mem::SizedTypeProperties; -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::find_existing_run; -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] use crate::slice::sort::shared::smallsort::insertion_sort_shift_left; pub(crate) mod heapsort; @@ -55,7 +55,7 @@ pub fn sort bool>(v: &mut [T], is_less: &mut F) { /// /// Deliberately don't inline the main sorting routine entrypoint to ensure the /// inlined insertion sort i-cache footprint remains minimal. -#[cfg(not(feature = "optimize_for_size"))] +#[cfg(not(any(feature = "optimize_for_size", target_pointer_width = "16")))] #[inline(never)] fn ipnsort(v: &mut [T], is_less: &mut F) where diff --git a/library/std/src/io/error.rs b/library/std/src/io/error.rs index 795cc64e957da..f20814dd95cc1 100644 --- a/library/std/src/io/error.rs +++ b/library/std/src/io/error.rs @@ -402,7 +402,7 @@ pub enum ErrorKind { /// The operation was partially successful and needs to be checked /// later on due to not blocking. - #[unstable(feature = "io_error_inprogress", issue = "none")] + #[unstable(feature = "io_error_inprogress", issue = "130840")] InProgress, // "Unusual" error kinds which do not correspond simply to (sets diff --git a/library/std/src/random.rs b/library/std/src/random.rs index ecbf02eee8430..604fa4df11066 100644 --- a/library/std/src/random.rs +++ b/library/std/src/random.rs @@ -71,7 +71,8 @@ impl RandomSource for DefaultRandomSource { /// /// This is a convenience function for `T::random(&mut DefaultRandomSource)` and /// will sample according to the same distribution as the underlying [`Random`] -/// trait implementation. +/// trait implementation. See [`DefaultRandomSource`] for more information about +/// how randomness is sourced. /// /// **Warning:** Be careful when manipulating random values! The /// [`random`](Random::random) method on integers samples them with a uniform diff --git a/tests/crashes/129219.rs b/tests/crashes/129219.rs deleted file mode 100644 index effbfcd8b8e45..0000000000000 --- a/tests/crashes/129219.rs +++ /dev/null @@ -1,26 +0,0 @@ -//@ known-bug: rust-lang/rust#129219 -//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir --edition=2018 - -use core::marker::Unsize; - -pub trait CastTo: Unsize {} - -impl CastTo for U {} - -impl Cast for T {} -pub trait Cast { - fn cast(&self) -> &T - where - Self: CastTo, - { - self - } -} - -pub trait Foo {} -impl Foo for [i32; 0] {} - -fn main() { - let x: &dyn Foo = &[]; - let x = x.cast::<[i32]>(); -} diff --git a/tests/ui/mir/validate/validate-unsize-cast.rs b/tests/ui/mir/validate/validate-unsize-cast.rs new file mode 100644 index 0000000000000..198af8d2e13a9 --- /dev/null +++ b/tests/ui/mir/validate/validate-unsize-cast.rs @@ -0,0 +1,33 @@ +//@ compile-flags: -Zmir-opt-level=0 -Zmir-enable-passes=+Inline,+GVN -Zvalidate-mir + +#![feature(unsize)] + +use std::marker::Unsize; + +pub trait CastTo: Unsize {} + +// Not well-formed! +impl CastTo for T {} +//~^ ERROR the trait bound `T: Unsize` is not satisfied + +pub trait Cast { + fn cast(&self) + where + Self: CastTo; +} +impl Cast for T { + #[inline(always)] + fn cast(&self) + where + Self: CastTo, + { + let x: &U = self; + } +} + +fn main() { + // When we inline this call, then we run GVN, then + // GVN tries to evaluate the `() -> [i32]` unsize. + // That's invalid! + ().cast::<[i32]>(); +} diff --git a/tests/ui/mir/validate/validate-unsize-cast.stderr b/tests/ui/mir/validate/validate-unsize-cast.stderr new file mode 100644 index 0000000000000..cfb47b34e980b --- /dev/null +++ b/tests/ui/mir/validate/validate-unsize-cast.stderr @@ -0,0 +1,20 @@ +error[E0277]: the trait bound `T: Unsize` is not satisfied + --> $DIR/validate-unsize-cast.rs:10:42 + | +LL | impl CastTo for T {} + | ^ the trait `Unsize` is not implemented for `T` + | + = note: all implementations of `Unsize` are provided automatically by the compiler, see for more information +note: required by a bound in `CastTo` + --> $DIR/validate-unsize-cast.rs:7:30 + | +LL | pub trait CastTo: Unsize {} + | ^^^^^^^^^ required by this bound in `CastTo` +help: consider further restricting this bound + | +LL | impl, U: ?Sized> CastTo for T {} + | ++++++++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`.