diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index e56ed166592a..b215f77c39ad 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -236,9 +236,11 @@ fn parse_directive_items<'p, S: Stage>( }} macro or_malformed($($code:tt)*) {{ - let Some(ret) = (||{ - Some($($code)*) - })() else { + let Some(ret) = ( + try { + $($code)* + } + ) else { malformed!() }; ret diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index cebbabfcbf1b..4aa7ebffbd3c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -84,8 +84,7 @@ pub(super) struct GroupTypeInnerAccept { pub(crate) type AcceptFn = Box Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser) + Send + Sync>; -pub(crate) type FinalizeFn = - Box) -> Option>; +pub(crate) type FinalizeFn = fn(&mut FinalizeContext<'_, '_, S>) -> Option; macro_rules! attribute_parsers { ( @@ -131,10 +130,10 @@ mod late { }), safety: <$names as crate::attributes::AttributeParser<$stage>>::SAFETY, allowed_targets: <$names as crate::attributes::AttributeParser<$stage>>::ALLOWED_TARGETS, - finalizer: Box::new(|cx| { + finalizer: |cx| { let state = STATE_OBJECT.take(); state.finalize(cx) - }) + } }); } Entry::Occupied(_) => panic!("Attribute {path:?} has multiple accepters"), diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 0dfa67951e62..d350bfee7f34 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -297,7 +297,7 @@ pub fn parse_attribute_list( let mut attr_paths: Vec> = Vec::new(); let mut early_parsed_state = EarlyParsedState::default(); - let mut finalizers: Vec<&FinalizeFn> = Vec::with_capacity(attrs.len()); + let mut finalizers: Vec> = Vec::with_capacity(attrs.len()); for attr in attrs { // If we're only looking for a single attribute, skip all the ones we don't care about. @@ -413,7 +413,7 @@ pub fn parse_attribute_list( }; (accept.accept_fn)(&mut cx, &args); - finalizers.push(&accept.finalizer); + finalizers.push(accept.finalizer); if !matches!(cx.stage.should_emit(), ShouldEmit::Nothing) { Self::check_target(&accept.allowed_targets, target, &mut cx); diff --git a/compiler/rustc_codegen_ssa/src/back/write.rs b/compiler/rustc_codegen_ssa/src/back/write.rs index 5601a950fbdb..ff91a08de4de 100644 --- a/compiler/rustc_codegen_ssa/src/back/write.rs +++ b/compiler/rustc_codegen_ssa/src/back/write.rs @@ -153,7 +153,7 @@ macro_rules! if_regular { // `#![no_builtins]` is assumed to not participate in LTO and // instead goes on to generate object code. EmitObj::Bitcode - } else if need_bitcode_in_object(tcx) { + } else if need_bitcode_in_object(tcx) || sess.target.requires_lto { EmitObj::ObjectCode(BitcodeSection::Full) } else { EmitObj::ObjectCode(BitcodeSection::None) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index e34628bad66b..4dac314b9181 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -2009,6 +2009,9 @@ fn report_return_mismatched_types<'infcx>( // note in this case, since it would be incorrect. && let Some(fn_sig) = fcx.body_fn_sig() && fn_sig.output().is_ty_var() + && fcx.ret_coercion.as_ref().is_some_and(|ret_coercion| { + fcx.resolve_vars_if_possible(ret_coercion.borrow().expected_ty()) == expected + }) { err.span_note(sp, format!("return type inferred to be `{expected}` here")); } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 1f08dff18f3e..afd5356d5a1e 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -7,6 +7,7 @@ use rustc_data_structures::packed::Pu128; use rustc_errors::{Applicability, Diag, MultiSpan, listify, msg}; use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; +use rustc_hir::intravisit::Visitor; use rustc_hir::lang_items::LangItem; use rustc_hir::{ self as hir, Arm, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, @@ -26,13 +27,14 @@ use rustc_span::{ExpnKind, Ident, MacroKind, Span, Spanned, Symbol, sym}; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::error_reporting::traits::DefIdOrName; +use rustc_trait_selection::error_reporting::traits::suggestions::ReturnsVisitor; use rustc_trait_selection::infer::InferCtxtExt; use rustc_trait_selection::traits; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use tracing::{debug, instrument}; use super::FnCtxt; -use crate::errors; +use crate::errors::{self, SuggestBoxingForReturnImplTrait}; use crate::fn_ctxt::rustc_span::BytePos; use crate::method::probe; use crate::method::probe::{IsSuggestion, Mode, ProbeScope}; @@ -963,6 +965,40 @@ pub(in super::super) fn suggest_missing_return_type( ); } + let trait_def_id = trait_ref.trait_ref.path.res.def_id(); + if self.tcx.is_dyn_compatible(trait_def_id) { + err.subdiagnostic(SuggestBoxingForReturnImplTrait::ChangeReturnType { + start_sp: hir_ty.span.with_hi(hir_ty.span.lo() + BytePos(4)), + end_sp: hir_ty.span.shrink_to_hi(), + }); + + let body = self.tcx.hir_body_owned_by(fn_id); + let mut visitor = ReturnsVisitor::default(); + visitor.visit_body(&body); + + if !visitor.returns.is_empty() { + let starts: Vec = visitor + .returns + .iter() + .filter(|expr| expr.span.can_be_used_for_suggestions()) + .map(|expr| expr.span.shrink_to_lo()) + .collect(); + let ends: Vec = visitor + .returns + .iter() + .filter(|expr| expr.span.can_be_used_for_suggestions()) + .map(|expr| expr.span.shrink_to_hi()) + .collect(); + + if !starts.is_empty() { + err.subdiagnostic(SuggestBoxingForReturnImplTrait::BoxReturnExpr { + starts, + ends, + }); + } + } + } + self.try_suggest_return_impl_trait(err, expected, found, fn_id); self.try_note_caller_chooses_ty_for_ty_param(err, expected, found); return true; diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index 90af4d785945..29ec97a6ca59 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -143,7 +143,7 @@ pub(crate) struct InvalidConstInValtree { #[note( "an ideal reproduction consists of the code before and some patch that then triggers the bug when applied and compiled again" )] -#[note("as a workaround, you can run {$run_cmd} to allow your project to compile")] +#[note("as a workaround, you can {$run_cmd} to allow your project to compile")] pub(crate) struct IncrementCompilation { pub run_cmd: String, pub dep_node: String, diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index d1f82af3416b..03193400f88e 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -2416,10 +2416,10 @@ pub fn mk_place_index(self, place: Place<'tcx>, index: Local) -> Place<'tcx> { /// to build a full `Place` it's just a convenient way to grab a projection and modify it in /// flight. pub fn mk_place_elem(self, place: Place<'tcx>, elem: PlaceElem<'tcx>) -> Place<'tcx> { - let mut projection = place.projection.to_vec(); - projection.push(elem); - - Place { local: place.local, projection: self.mk_place_elems(&projection) } + Place { + local: place.local, + projection: self.mk_place_elems_from_iter(place.projection.iter().chain([elem])), + } } pub fn mk_poly_existential_predicates( diff --git a/compiler/rustc_middle/src/verify_ich.rs b/compiler/rustc_middle/src/verify_ich.rs index a1ab4d8cc4d0..5aa7a1d91f80 100644 --- a/compiler/rustc_middle/src/verify_ich.rs +++ b/compiler/rustc_middle/src/verify_ich.rs @@ -1,6 +1,8 @@ use std::cell::Cell; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_hir::def_id::LOCAL_CRATE; +use rustc_session::utils::was_invoked_from_cargo; use tracing::instrument; use crate::dep_graph::{DepGraphData, SerializedDepNodeIndex}; @@ -66,10 +68,10 @@ fn incremental_verify_ich_failed<'tcx>( if old_in_panic { tcx.dcx().emit_err(crate::error::Reentrant); } else { - let run_cmd = if let Some(crate_name) = &tcx.sess.opts.crate_name { - format!("`cargo clean -p {crate_name}` or `cargo clean`") + let run_cmd = if was_invoked_from_cargo() { + format!("run `cargo clean -p {}` or `cargo clean`", tcx.crate_name(LOCAL_CRATE)) } else { - "`cargo clean`".to_string() + "clean your build cache".to_owned() }; let dep_node = tcx.dep_graph.data().unwrap().prev_node_of(prev_index); diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index d84bfeb8fff8..cd9d573957f4 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1205,6 +1205,7 @@ pub fn path(&self, flavor: OutputType) -> OutFileName { } pub fn interface_path(&self) -> PathBuf { + debug!("using crate_name={} for interface_path", self.crate_stem); self.out_directory.join(format!("lib{}.rs", self.crate_stem)) } @@ -1214,6 +1215,7 @@ fn output_path(&self, flavor: OutputType) -> PathBuf { let extension = flavor.extension(); match flavor { OutputType::Metadata => { + debug!("using crate_name={} for {extension}", self.crate_stem); self.out_directory.join(format!("lib{}.{}", self.crate_stem, extension)) } _ => self.with_directory_and_extension(&self.out_directory, extension), @@ -1288,6 +1290,7 @@ pub fn with_extension(&self, extension: &str) -> PathBuf { } pub fn with_directory_and_extension(&self, directory: &Path, extension: &str) -> PathBuf { + debug!("using filestem={} for {extension}", self.filestem); let mut path = directory.join(&self.filestem); path.set_extension(extension); path diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 4e6d88665859..d0c415a2ec67 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2615,7 +2615,7 @@ impl RcEqIdent for Rc { #[rustc_unsafe_specialization_marker] pub(crate) trait MarkerEq: PartialEq {} -impl MarkerEq for T {} +impl MarkerEq for T {} /// We're doing this specialization here, and not as a more general optimization on `&T`, because it /// would otherwise add a cost to all equality checks on refs. We assume that `Rc`s are used to @@ -2628,12 +2628,12 @@ impl MarkerEq for T {} impl RcEqIdent for Rc { #[inline] fn eq(&self, other: &Rc) -> bool { - Rc::ptr_eq(self, other) || **self == **other + ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr()) || **self == **other } #[inline] fn ne(&self, other: &Rc) -> bool { - !Rc::ptr_eq(self, other) && **self != **other + !ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr()) && **self != **other } } diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 2966f3ccc179..98120896bc9b 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -126,6 +126,26 @@ macro_rules! copy_slice_and_advance { // the bounds for String-join are S: Borrow and for Vec-join Borrow<[T]> // [T] and str both impl AsRef<[T]> for some T // => s.borrow().as_ref() and we always have slices +// +// # Safety notes +// +// `Borrow` is a safe trait, and implementations are not required +// to be deterministic. An inconsistent `Borrow` implementation could return slices +// of different lengths on consecutive calls (e.g. by using interior mutability). +// +// This implementation calls `borrow()` multiple times: +// 1. To calculate `reserved_len`, all elements are borrowed once. +// 2. The first element is borrowed again when copied via `extend_from_slice`. +// 3. Subsequent elements are borrowed a second time when building the mapped iterator. +// +// Risks and Mitigations: +// - If the first element GROWS on the second borrow, the length subtraction underflows. +// We mitigate this by doing a `checked_sub` to panic rather than allowing an underflow +// that fabricates a huge destination slice. +// - If elements 2..N GROW on their second borrow, the target slice bounds set by `checked_sub` +// means that `split_at_mut` inside `copy_slice_and_advance!` will correctly panic. +// - If elements SHRINK on their second borrow, the spare space is never written, and the final +// length set via `set_len` masks trailing uninitialized bytes. #[cfg(not(no_global_oom_handling))] fn join_generic_copy(slice: &[S], sep: &[T]) -> Vec where @@ -161,19 +181,21 @@ fn join_generic_copy(slice: &[S], sep: &[T]) -> Vec unsafe { let pos = result.len(); - let target = result.spare_capacity_mut().get_unchecked_mut(..reserved_len - pos); + let target_len = reserved_len.checked_sub(pos).expect("inconsistent Borrow implementation"); + let target = result.spare_capacity_mut().get_unchecked_mut(..target_len); // Convert the separator and slices to slices of MaybeUninit - // to simplify implementation in specialize_for_lengths + // to simplify implementation in specialize_for_lengths. let sep_uninit = core::slice::from_raw_parts(sep.as_ptr().cast(), sep.len()); let iter_uninit = iter.map(|it| { let it = it.borrow().as_ref(); core::slice::from_raw_parts(it.as_ptr().cast(), it.len()) }); - // copy separator and slices over without bounds checks - // generate loops with hardcoded offsets for small separators - // massive improvements possible (~ x2) + // copy separator and slices over without bounds checks. + // `specialize_for_lengths!` internally calls `s.borrow()`, but because it uses + // the bounds-checked `split_at_mut` any misbehaving implementation + // will not write out of bounds. let remain = specialize_for_lengths!(sep_uninit, target, iter_uninit; 0, 1, 2, 3, 4); // A weird borrow implementation may return different diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 8004dd38d073..229fcd2b429c 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -3549,12 +3549,12 @@ impl ArcEqIdent for Arc { impl ArcEqIdent for Arc { #[inline] fn eq(&self, other: &Arc) -> bool { - Arc::ptr_eq(self, other) || **self == **other + ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr()) || **self == **other } #[inline] fn ne(&self, other: &Arc) -> bool { - !Arc::ptr_eq(self, other) && **self != **other + !ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr()) && **self != **other } } diff --git a/library/alloctests/tests/arc.rs b/library/alloctests/tests/arc.rs index 00bdf527133f..a56204187c0a 100644 --- a/library/alloctests/tests/arc.rs +++ b/library/alloctests/tests/arc.rs @@ -86,6 +86,34 @@ fn eq(&self, other: &TestEq) -> bool { assert_eq!(*x.0.borrow(), 0); } +#[test] +fn eq_unsized() { + #[derive(Eq)] + struct TestEq(RefCell, T); + impl PartialEq for TestEq { + fn eq(&self, other: &TestEq) -> bool { + *self.0.borrow_mut() += 1; + *other.0.borrow_mut() += 1; + true + } + } + let x = Arc::>::new(TestEq(RefCell::new(0), [0, 1, 2])) as Arc>; + assert!(x == x); + assert!(!(x != x)); + assert_eq!(*x.0.borrow(), 0); +} + +#[test] +fn eq_unsized_slice() { + let a: Arc<[()]> = Arc::new([(); 3]); + let ptr: *const () = Arc::into_raw(a.clone()).cast(); + let b: Arc<[()]> = unsafe { Arc::from_raw(core::ptr::slice_from_raw_parts(ptr, 42)) }; + assert!(a == a); + assert!(!(a != a)); + assert!(a != b); + assert!(!(a == b)); +} + // The test code below is identical to that in `rc.rs`. // For better maintainability we therefore define this type alias. type Rc = Arc; diff --git a/library/alloctests/tests/rc.rs b/library/alloctests/tests/rc.rs index bb68eb4ac9e3..5be0e8f33911 100644 --- a/library/alloctests/tests/rc.rs +++ b/library/alloctests/tests/rc.rs @@ -87,6 +87,34 @@ fn eq(&self, other: &TestEq) -> bool { assert_eq!(*x.0.borrow(), 0); } +#[test] +fn eq_unsized() { + #[derive(Eq)] + struct TestEq(RefCell, T); + impl PartialEq for TestEq { + fn eq(&self, other: &TestEq) -> bool { + *self.0.borrow_mut() += 1; + *other.0.borrow_mut() += 1; + true + } + } + let x = Rc::>::new(TestEq(RefCell::new(0), [0, 1, 2])) as Rc>; + assert!(x == x); + assert!(!(x != x)); + assert_eq!(*x.0.borrow(), 0); +} + +#[test] +fn eq_unsized_slice() { + let a: Rc<[()]> = Rc::new([(); 3]); + let ptr: *const () = Rc::into_raw(a.clone()).cast(); + let b: Rc<[()]> = unsafe { Rc::from_raw(core::ptr::slice_from_raw_parts(ptr, 42)) }; + assert!(a == a); + assert!(!(a != a)); + assert!(a != b); + assert!(!(a == b)); +} + const SHARED_ITER_MAX: u16 = 100; fn assert_trusted_len(_: &I) {} diff --git a/library/alloctests/tests/str.rs b/library/alloctests/tests/str.rs index c0bcdb8500af..49baa53c9d3e 100644 --- a/library/alloctests/tests/str.rs +++ b/library/alloctests/tests/str.rs @@ -194,6 +194,25 @@ fn borrow(&self) -> &str { test_join!("0-0-0", arr, "-"); } +#[test] +#[should_panic(expected = "inconsistent Borrow implementation")] +fn test_join_inconsistent_borrow() { + use std::borrow::Borrow; + use std::cell::Cell; + + struct E(Cell); + + impl Borrow for E { + fn borrow(&self) -> &str { + let count = self.0.get(); + self.0.set(count + 1); + if count == 0 { "" } else { "longer string" } + } + } + + let _s = [E(Cell::new(0)), E(Cell::new(0))].join(""); +} + #[test] #[cfg_attr(miri, ignore)] // Miri is too slow fn test_unsafe_slice() { diff --git a/library/core/src/field.rs b/library/core/src/field.rs index 120d7a9f4760..90d16e5f2af5 100644 --- a/library/core/src/field.rs +++ b/library/core/src/field.rs @@ -1,11 +1,12 @@ //! Field Reflection +use crate::fmt; +use crate::hash::{Hash, Hasher}; use crate::marker::PhantomData; /// Field Representing Type #[unstable(feature = "field_representing_type_raw", issue = "none")] #[lang = "field_representing_type"] -#[expect(missing_debug_implementations)] #[fundamental] pub struct FieldRepresentingType { // We want this type to be invariant over `T`, because otherwise `field_of!(Struct<'short>, @@ -14,16 +15,50 @@ pub struct FieldRepresentingType T>, } -// SAFETY: `FieldRepresentingType` doesn't contain any `T` -unsafe impl Send - for FieldRepresentingType -{ -} - -// SAFETY: `FieldRepresentingType` doesn't contain any `T` -unsafe impl Sync +impl fmt::Debug for FieldRepresentingType { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + enum Member { + Name(&'static str), + Index(u32), + } + impl fmt::Display for Member { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Name(name) => fmt::Display::fmt(name, f), + Self::Index(idx) => fmt::Display::fmt(idx, f), + } + } + } + let (variant, field) = const { + use crate::mem::type_info::{Type, TypeKind}; + match Type::of::().kind { + TypeKind::Struct(struct_) => { + (None, Member::Name(struct_.fields[FIELD as usize].name)) + } + TypeKind::Tuple(_) => (None, Member::Index(FIELD)), + TypeKind::Enum(enum_) => { + let variant = &enum_.variants[VARIANT as usize]; + (Some(variant.name), Member::Name(variant.fields[FIELD as usize].name)) + } + TypeKind::Union(union) => (None, Member::Name(union.fields[FIELD as usize].name)), + _ => unreachable!(), + } + }; + let type_name = const { crate::any::type_name::() }; + match variant { + Some(variant) => write!(f, "field_of!({type_name}, {variant}.{field})"), + None => write!(f, "field_of!({type_name}, {field})"), + } + // NOTE: if there are changes in the reflection work and the above no + // longer compiles, then the following debug impl could also work in + // the meantime: + // ```rust + // let type_name = const { type_name::() }; + // write!(f, "field_of!({type_name}, {VARIANT}.{FIELD})") + // ``` + } } impl Copy @@ -39,6 +74,51 @@ fn clone(&self) -> Self { } } +impl Default + for FieldRepresentingType +{ + fn default() -> Self { + Self { _phantom: PhantomData::default() } + } +} + +impl Hash + for FieldRepresentingType +{ + fn hash(&self, state: &mut H) { + self._phantom.hash(state); + } +} + +impl PartialEq + for FieldRepresentingType +{ + fn eq(&self, other: &Self) -> bool { + self._phantom == other._phantom + } +} + +impl Eq + for FieldRepresentingType +{ +} + +impl PartialOrd + for FieldRepresentingType +{ + fn partial_cmp(&self, other: &Self) -> Option { + self._phantom.partial_cmp(&other._phantom) + } +} + +impl Ord + for FieldRepresentingType +{ + fn cmp(&self, other: &Self) -> crate::cmp::Ordering { + self._phantom.cmp(&other._phantom) + } +} + /// Expands to the field representing type of the given field. /// /// The container type may be a tuple, `struct`, `union` or `enum`. In the case of an enum, the diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 934d4bd68403..f79a93214ea2 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -3098,9 +3098,7 @@ fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { let is_init = buf.is_init(); // SAFETY: no uninit data is written to ibuf - let ibuf = unsafe { &mut buf.as_mut()[..limit] }; - - let mut sliced_buf: BorrowedBuf<'_> = ibuf.into(); + let mut sliced_buf = BorrowedBuf::from(unsafe { &mut buf.as_mut()[..limit] }); if is_init { // SAFETY: `sliced_buf` is a subslice of `buf`, so if `buf` was initialized then @@ -3108,22 +3106,23 @@ fn read_buf(&mut self, mut buf: BorrowedCursor<'_>) -> Result<()> { unsafe { sliced_buf.set_init() }; } - let mut cursor = sliced_buf.unfilled(); - let result = self.inner.read_buf(cursor.reborrow()); + let result = self.inner.read_buf(sliced_buf.unfilled()); - let should_init = cursor.is_init(); + let did_init_up_to_limit = sliced_buf.is_init(); let filled = sliced_buf.len(); - // cursor / sliced_buf / ibuf must drop here + // sliced_buf must drop here // Avoid accidentally quadratic behaviour by initializing the whole // cursor if only part of it was initialized. - if should_init { - // SAFETY: no uninit data is written - let uninit = unsafe { &mut buf.as_mut()[limit..] }; - uninit.write_filled(0); - // SAFETY: all bytes that were not initialized by `T::read_buf` - // have just been written to. + if did_init_up_to_limit && !is_init { + // SAFETY: No uninit data will be written. + let unfilled_before_advance = unsafe { buf.as_mut() }; + + unfilled_before_advance[limit..].write_filled(0); + + // SAFETY: `unfilled_before_advance[..limit]` was initialized by `T::read_buf`, and + // `unfilled_before_advance[limit..]` was just initialized. unsafe { buf.set_init() }; } diff --git a/library/std/src/sys/process/uefi.rs b/library/std/src/sys/process/uefi.rs index 88dd4c899b37..3ae3aa7f2e30 100644 --- a/library/std/src/sys/process/uefi.rs +++ b/library/std/src/sys/process/uefi.rs @@ -5,7 +5,7 @@ pub use crate::ffi::OsString as EnvKey; use crate::ffi::{OsStr, OsString}; use crate::num::{NonZero, NonZeroI32}; -use crate::path::Path; +use crate::path::{Path, PathBuf}; use crate::process::StdioPipes; use crate::sys::fs::File; use crate::sys::io::error_string; @@ -138,7 +138,9 @@ fn create_stdin( } pub fn output(command: &mut Command) -> io::Result<(ExitStatus, Vec, Vec)> { - let mut cmd = uefi_command_internal::Image::load_image(&command.prog)?; + let prog_path = resolve_program(&command.prog) + .ok_or(io::const_error!(io::ErrorKind::NotFound, "could not find the program."))?; + let mut cmd = uefi_command_internal::Image::load_image(prog_path.as_os_str())?; // UEFI adds the bin name by default if !command.args.is_empty() { @@ -366,6 +368,36 @@ pub fn read_output( match out.diverge() {} } +// Search for programs similar to UEFI Shell defined in Section 3.6.1. It follows the following flow: +// 1. If program is already absolute path, just check if it exists. +// 2. For non-absolute path, search relative to current directory. +// 3. Search the path list sequentially. +// +// [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_2_2.pdf). +fn resolve_program + ?Sized>(prog: &S) -> Option { + let absolute_prog_path = crate::path::absolute(prog.as_ref()).ok()?; + + match crate::fs::exists(&absolute_prog_path) { + Ok(true) => return Some(absolute_prog_path), + // If program path was already absolute and is not found, then stop. + Ok(false) if Path::new(prog.as_ref()).is_absolute() => return None, + _ => {} + } + + // Search for the program in path. + if let Ok(path_var) = crate::env::var("path") { + for p in crate::env::split_paths(&path_var) { + let temp = p.join(prog.as_ref()); + + if let Ok(true) = crate::fs::exists(&temp) { + return Some(temp); + } + } + } + + None +} + #[allow(dead_code)] mod uefi_command_internal { use r_efi::protocols::{loaded_image, simple_text_input, simple_text_output}; diff --git a/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md b/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md index dbdb96283a5f..8934e7085b8d 100644 --- a/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md +++ b/src/doc/rustc/src/platform-support/amdgcn-amd-amdhsa.md @@ -59,11 +59,6 @@ Build the library as `cdylib`: # Cargo.toml [lib] crate-type = ["cdylib"] - -[profile.dev] -lto = true # LTO must be explicitly enabled for now -[profile.release] -lto = true ``` The target-cpu must be from the list [supported by LLVM] (or printed with `rustc --target amdgcn-amd-amdhsa --print target-cpus`). diff --git a/tests/run-make-cargo/amdgpu-lto/Cargo.toml b/tests/run-make-cargo/amdgpu-lto/Cargo.toml new file mode 100644 index 000000000000..b2607b747fd7 --- /dev/null +++ b/tests/run-make-cargo/amdgpu-lto/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "amdgpu_lto" +version = "0.1.0" +edition = "2024" + +[lib] +path = "lib.rs" +crate-type = ["cdylib"] diff --git a/tests/run-make-cargo/amdgpu-lto/lib.rs b/tests/run-make-cargo/amdgpu-lto/lib.rs new file mode 100644 index 000000000000..d17cf5a8316c --- /dev/null +++ b/tests/run-make-cargo/amdgpu-lto/lib.rs @@ -0,0 +1,15 @@ +#![feature(abi_gpu_kernel)] +#![no_std] + +#[panic_handler] +fn panic_handler(_info: &core::panic::PanicInfo) -> ! { + loop {} +} + +#[unsafe(no_mangle)] +fn foo(a: i32, b: i32) -> i32 { + a + b +} + +#[unsafe(no_mangle)] +extern "gpu-kernel" fn kernel() {} diff --git a/tests/run-make-cargo/amdgpu-lto/rmake.rs b/tests/run-make-cargo/amdgpu-lto/rmake.rs new file mode 100644 index 000000000000..cb3dc81f34d1 --- /dev/null +++ b/tests/run-make-cargo/amdgpu-lto/rmake.rs @@ -0,0 +1,28 @@ +// Check that compiling for the amdgpu target which needs LTO works with a default +// cargo configuration. + +//@ needs-llvm-components: amdgpu +//@ needs-rust-lld + +#![deny(warnings)] + +use run_make_support::{cargo, path}; + +fn main() { + let target_dir = path("target"); + + cargo() + .args(&[ + "build", + "--release", + "--lib", + "--manifest-path", + "Cargo.toml", + "-Zbuild-std=core", + "--target", + "amdgcn-amd-amdhsa", + ]) + .env("RUSTFLAGS", "-Ctarget-cpu=gfx900") + .env("CARGO_TARGET_DIR", &target_dir) + .run(); +} diff --git a/tests/ui/closures/closure-return-block-note-issue-155670.rs b/tests/ui/closures/closure-return-block-note-issue-155670.rs new file mode 100644 index 000000000000..fccce9ee54ca --- /dev/null +++ b/tests/ui/closures/closure-return-block-note-issue-155670.rs @@ -0,0 +1,14 @@ +struct SmolStr; + +const _: fn() = || { + match Some(()) { + Some(()) => (), + None => return, + }; + let _: String = { + SmolStr + //~^ ERROR mismatched types + }; +}; + +fn main() {} diff --git a/tests/ui/closures/closure-return-block-note-issue-155670.stderr b/tests/ui/closures/closure-return-block-note-issue-155670.stderr new file mode 100644 index 000000000000..f38d2f14200d --- /dev/null +++ b/tests/ui/closures/closure-return-block-note-issue-155670.stderr @@ -0,0 +1,9 @@ +error[E0308]: mismatched types + --> $DIR/closure-return-block-note-issue-155670.rs:9:9 + | +LL | SmolStr + | ^^^^^^^ expected `String`, found `SmolStr` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/field_representing_types/not-field-if-unsized.next.stderr b/tests/ui/field_representing_types/not-field-if-unsized.next.stderr new file mode 100644 index 000000000000..e3cee8090772 --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-unsized.next.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied + --> $DIR/not-field-if-unsized.rs:17:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyStruct, 0)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-unsized.rs:12:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error[E0277]: the trait bound `field_of!(MyStruct, 1): std::field::Field` is not satisfied + --> $DIR/not-field-if-unsized.rs:21:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyStruct, 1)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-unsized.rs:12:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_representing_types/not-field-if-unsized.old.stderr b/tests/ui/field_representing_types/not-field-if-unsized.old.stderr new file mode 100644 index 000000000000..e3cee8090772 --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-unsized.old.stderr @@ -0,0 +1,27 @@ +error[E0277]: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied + --> $DIR/not-field-if-unsized.rs:17:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyStruct, 0)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-unsized.rs:12:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error[E0277]: the trait bound `field_of!(MyStruct, 1): std::field::Field` is not satisfied + --> $DIR/not-field-if-unsized.rs:21:20 + | +LL | assert_field::(); + | ^^^^^^^^^^^^^^^^^^^^^^ the nightly-only, unstable trait `std::field::Field` is not implemented for `field_of!(MyStruct, 1)` + | +note: required by a bound in `assert_field` + --> $DIR/not-field-if-unsized.rs:12:20 + | +LL | fn assert_field() {} + | ^^^^^ required by this bound in `assert_field` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/field_representing_types/not-field-if-unsized.rs b/tests/ui/field_representing_types/not-field-if-unsized.rs new file mode 100644 index 000000000000..739fc91c2942 --- /dev/null +++ b/tests/ui/field_representing_types/not-field-if-unsized.rs @@ -0,0 +1,23 @@ +//@ revisions: old next +//@ [next] compile-flags: -Znext-solver +#![expect(incomplete_features)] +#![feature(field_projections)] + +use std::field::{Field, field_of}; + +pub trait Trait {} + +pub struct MyStruct(usize, dyn Trait); + +fn assert_field() {} + +fn main() { + // FIXME(FRTs): this requires relaxing the `Base: ?Sized` bound in the + // `Field` trait & compiler changes. + assert_field::(); + //~^ ERROR: the trait bound `field_of!(MyStruct, 0): std::field::Field` is not satisfied [E0277] + + // FIXME(FRTs): improve this error message, point to the `dyn Trait` span. + assert_field::(); + //~^ ERROR: the trait bound `field_of!(MyStruct, 1): std::field::Field` is not satisfied [E0277] +} diff --git a/tests/ui/field_representing_types/traits.rs b/tests/ui/field_representing_types/traits.rs index 6b5bb15f9ee9..bc31376e60ea 100644 --- a/tests/ui/field_representing_types/traits.rs +++ b/tests/ui/field_representing_types/traits.rs @@ -1,13 +1,17 @@ //@ revisions: old next //@ [next] compile-flags: -Znext-solver //@ run-pass -#![feature(field_projections, freeze)] +#![feature(field_projections, freeze, unsafe_unpin)] #![expect(incomplete_features, dead_code)] use std::field::field_of; -use std::marker::{Freeze, Unpin}; +use std::fmt::Debug; +use std::hash::Hash; +use std::marker::{Freeze, Unpin, UnsafeUnpin}; +use std::panic::{RefUnwindSafe, UnwindSafe}; struct Struct { field: u32, + tail: [u32], } union Union { @@ -19,11 +23,37 @@ enum Enum { Variant2(u32), } -fn assert_traits() {} +type Tuple = ((), usize, String, dyn Debug); + +fn assert_traits< + T: Sized + + Freeze + + RefUnwindSafe + + Send + + Sync + + Unpin + + UnsafeUnpin + + UnwindSafe + + Copy + + Debug + + Default + + Eq + + Hash + + Ord, +>() { +} fn main() { assert_traits::(); + assert_traits::(); + assert_traits::(); + assert_traits::(); assert_traits::(); + + assert_traits::(); + assert_traits::(); + assert_traits::(); + assert_traits::(); } diff --git a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-impl-trait.stderr b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-impl-trait.stderr index 2447a5d8d4b8..bba17eb2494e 100644 --- a/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-impl-trait.stderr +++ b/tests/ui/impl-trait/dyn-incompatible-trait-in-return-position-impl-trait.stderr @@ -21,6 +21,18 @@ LL | return A; LL | } LL | B | ^ expected `A`, found `B` + | +help: you could change the return type to be a boxed trait object + | +LL - fn cat() -> impl DynCompatible { +LL + fn cat() -> Box { + | +help: if you change the return type to expect trait objects, box the returned expressions + | +LL ~ return Box::new(A); +LL | } +LL ~ Box::new(B) + | error: aborting due to 2 previous errors diff --git a/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr b/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr index 2a4c5ff4a5be..13a78cb0fcf3 100644 --- a/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr +++ b/tests/ui/impl-trait/point-to-type-err-cause-on-impl-trait-return.stderr @@ -72,6 +72,17 @@ LL | } LL | 1u32 | ^^^^ expected `i32`, found `u32` | +help: you could change the return type to be a boxed trait object + | +LL - fn foo() -> impl std::fmt::Display { +LL + fn foo() -> Box { + | +help: if you change the return type to expect trait objects, box the returned expressions + | +LL ~ return Box::new(0i32); +LL | } +LL ~ Box::new(1u32) + | help: change the type of the numeric literal from `u32` to `i32` | LL - 1u32 @@ -90,6 +101,17 @@ LL | } else { LL | return 1u32; | ^^^^ expected `i32`, found `u32` | +help: you could change the return type to be a boxed trait object + | +LL - fn bar() -> impl std::fmt::Display { +LL + fn bar() -> Box { + | +help: if you change the return type to expect trait objects, box the returned expressions + | +LL ~ return Box::new(0i32); +LL | } else { +LL ~ return Box::new(1u32); + | help: change the type of the numeric literal from `u32` to `i32` | LL - return 1u32; @@ -108,6 +130,17 @@ LL | } else { LL | 1u32 | ^^^^ expected `i32`, found `u32` | +help: you could change the return type to be a boxed trait object + | +LL - fn baz() -> impl std::fmt::Display { +LL + fn baz() -> Box { + | +help: if you change the return type to expect trait objects, box the returned expressions + | +LL ~ return Box::new(0i32); +LL | } else { +LL ~ Box::new(1u32) + | help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit | LL | }.try_into().unwrap() @@ -153,6 +186,16 @@ LL | 0 => return 0i32, LL | _ => 1u32, | ^^^^ expected `i32`, found `u32` | +help: you could change the return type to be a boxed trait object + | +LL - fn bat() -> impl std::fmt::Display { +LL + fn bat() -> Box { + | +help: if you change the return type to expect trait objects, box the returned expressions + | +LL ~ 0 => return Box::new(0i32), +LL ~ _ => Box::new(1u32), + | help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit | LL | }.try_into().unwrap() @@ -171,6 +214,17 @@ LL | | _ => 2u32, LL | | } | |_____^ expected `i32`, found `u32` | +help: you could change the return type to be a boxed trait object + | +LL - fn can() -> impl std::fmt::Display { +LL + fn can() -> Box { + | +help: if you change the return type to expect trait objects, box the returned expressions + | +LL ~ 0 => return Box::new(0i32), +LL ~ 1 => Box::new(1u32), +LL ~ _ => Box::new(2u32), + | help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit | LL | }.try_into().unwrap() @@ -188,6 +242,18 @@ LL | return 0i32; LL | 1u32 | ^^^^ expected `i32`, found `u32` | +help: you could change the return type to be a boxed trait object + | +LL - fn cat() -> impl std::fmt::Display { +LL + fn cat() -> Box { + | +help: if you change the return type to expect trait objects, box the returned expressions + | +LL ~ return Box::new(0i32); +LL | } +LL | _ => { +LL ~ Box::new(1u32) + | help: you can convert a `u32` to an `i32` and panic if the converted value doesn't fit | LL | }.try_into().unwrap()