Auto merge of #155851 - jhpratt:rollup-Jst4hhC, r=jhpratt

Rollup of 12 pull requests

Successful merges:

 - rust-lang/rust#149624 (Fix requires_lto targets needing lto set in cargo)
 - rust-lang/rust#155317 (`std::io::Take`: Clarify & optimize `BorrowedBuf::set_init` usage.)
 - rust-lang/rust#155579 (Make Rcs and Arcs use pointer comparison for unsized types)
 - rust-lang/rust#155588 (Implement more traits for FRTs)
 - rust-lang/rust#155708 (Fix heap overflow in slice::join caused by misbehaving Borrow)
 - rust-lang/rust#155778 (Avoid Vec allocation in TyCtxt::mk_place_elem)
 - rust-lang/rust#151014 (std: sys: process: uefi: Add program searching)
 - rust-lang/rust#155682 (Add boxing suggestions for `impl Trait` return type mismatches)
 - rust-lang/rust#155770 (Avoid misleading closure return type note)
 - rust-lang/rust#155818 (Convert attribute `FinalizeFn` to fn pointer)
 - rust-lang/rust#155829 (rustc_attr_parsing: use a `try {}` in `or_malformed`)
 - rust-lang/rust#155835 (couple of `crate_name` cleanups)
This commit is contained in:
bors
2026-04-27 02:32:52 +00:00
31 changed files with 568 additions and 61 deletions
@@ -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
+3 -4
View File
@@ -84,8 +84,7 @@ pub(super) struct GroupTypeInnerAccept<S: Stage> {
pub(crate) type AcceptFn<S> =
Box<dyn for<'sess, 'a> Fn(&mut AcceptContext<'_, 'sess, S>, &ArgParser) + Send + Sync>;
pub(crate) type FinalizeFn<S> =
Box<dyn Send + Sync + Fn(&mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind>>;
pub(crate) type FinalizeFn<S> = fn(&mut FinalizeContext<'_, '_, S>) -> Option<AttributeKind>;
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"),
+2 -2
View File
@@ -297,7 +297,7 @@ pub fn parse_attribute_list(
let mut attr_paths: Vec<RefPathParser<'_>> = Vec::new();
let mut early_parsed_state = EarlyParsedState::default();
let mut finalizers: Vec<&FinalizeFn<S>> = Vec::with_capacity(attrs.len());
let mut finalizers: Vec<FinalizeFn<S>> = 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);
+1 -1
View File
@@ -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)
@@ -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"));
}
@@ -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<Span> = visitor
.returns
.iter()
.filter(|expr| expr.span.can_be_used_for_suggestions())
.map(|expr| expr.span.shrink_to_lo())
.collect();
let ends: Vec<Span> = 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;
+1 -1
View File
@@ -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,
+4 -4
View File
@@ -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(
+5 -3
View File
@@ -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);
+3
View File
@@ -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
+3 -3
View File
@@ -2615,7 +2615,7 @@ impl<T: ?Sized + PartialEq, A: Allocator> RcEqIdent<T, A> for Rc<T, A> {
#[rustc_unsafe_specialization_marker]
pub(crate) trait MarkerEq: PartialEq<Self> {}
impl<T: Eq> MarkerEq for T {}
impl<T: ?Sized + Eq> 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<T: Eq> MarkerEq for T {}
impl<T: ?Sized + MarkerEq, A: Allocator> RcEqIdent<T, A> for Rc<T, A> {
#[inline]
fn eq(&self, other: &Rc<T, A>) -> 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<T, A>) -> bool {
!Rc::ptr_eq(self, other) && **self != **other
!ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr()) && **self != **other
}
}
+27 -5
View File
@@ -126,6 +126,26 @@ macro_rules! copy_slice_and_advance {
// the bounds for String-join are S: Borrow<str> 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<B, T, S>(slice: &[S], sep: &[T]) -> Vec<T>
where
@@ -161,19 +181,21 @@ fn join_generic_copy<B, T, S>(slice: &[S], sep: &[T]) -> Vec<T>
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
+2 -2
View File
@@ -3549,12 +3549,12 @@ impl<T: ?Sized + PartialEq, A: Allocator> ArcEqIdent<T, A> for Arc<T, A> {
impl<T: ?Sized + crate::rc::MarkerEq, A: Allocator> ArcEqIdent<T, A> for Arc<T, A> {
#[inline]
fn eq(&self, other: &Arc<T, A>) -> 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<T, A>) -> bool {
!Arc::ptr_eq(self, other) && **self != **other
!ptr::eq(self.ptr.as_ptr(), other.ptr.as_ptr()) && **self != **other
}
}
+28
View File
@@ -86,6 +86,34 @@ fn eq(&self, other: &TestEq) -> bool {
assert_eq!(*x.0.borrow(), 0);
}
#[test]
fn eq_unsized() {
#[derive(Eq)]
struct TestEq<T: ?Sized>(RefCell<usize>, T);
impl<T: ?Sized> PartialEq for TestEq<T> {
fn eq(&self, other: &TestEq<T>) -> bool {
*self.0.borrow_mut() += 1;
*other.0.borrow_mut() += 1;
true
}
}
let x = Arc::<TestEq<[u8; 3]>>::new(TestEq(RefCell::new(0), [0, 1, 2])) as Arc<TestEq<[u8]>>;
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<T, A = std::alloc::Global> = Arc<T, A>;
+28
View File
@@ -87,6 +87,34 @@ fn eq(&self, other: &TestEq) -> bool {
assert_eq!(*x.0.borrow(), 0);
}
#[test]
fn eq_unsized() {
#[derive(Eq)]
struct TestEq<T: ?Sized>(RefCell<usize>, T);
impl<T: ?Sized> PartialEq for TestEq<T> {
fn eq(&self, other: &TestEq<T>) -> bool {
*self.0.borrow_mut() += 1;
*other.0.borrow_mut() += 1;
true
}
}
let x = Rc::<TestEq<[u8; 3]>>::new(TestEq(RefCell::new(0), [0, 1, 2])) as Rc<TestEq<[u8]>>;
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: TrustedLen>(_: &I) {}
+19
View File
@@ -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<u32>);
impl Borrow<str> 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() {
+89 -9
View File
@@ -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<T: ?Sized, const VARIANT: u32, const FIELD: u32> {
// We want this type to be invariant over `T`, because otherwise `field_of!(Struct<'short>,
@@ -14,16 +15,50 @@ pub struct FieldRepresentingType<T: ?Sized, const VARIANT: u32, const FIELD: u32
_phantom: PhantomData<fn(T) -> T>,
}
// SAFETY: `FieldRepresentingType` doesn't contain any `T`
unsafe impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Send
for FieldRepresentingType<T, VARIANT, FIELD>
{
}
// SAFETY: `FieldRepresentingType` doesn't contain any `T`
unsafe impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Sync
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> fmt::Debug
for FieldRepresentingType<T, VARIANT, FIELD>
{
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::<T>().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::<T>() };
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::<T>() };
// write!(f, "field_of!({type_name}, {VARIANT}.{FIELD})")
// ```
}
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Copy
@@ -39,6 +74,51 @@ fn clone(&self) -> Self {
}
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Default
for FieldRepresentingType<T, VARIANT, FIELD>
{
fn default() -> Self {
Self { _phantom: PhantomData::default() }
}
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Hash
for FieldRepresentingType<T, VARIANT, FIELD>
{
fn hash<H: Hasher>(&self, state: &mut H) {
self._phantom.hash(state);
}
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> PartialEq
for FieldRepresentingType<T, VARIANT, FIELD>
{
fn eq(&self, other: &Self) -> bool {
self._phantom == other._phantom
}
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Eq
for FieldRepresentingType<T, VARIANT, FIELD>
{
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> PartialOrd
for FieldRepresentingType<T, VARIANT, FIELD>
{
fn partial_cmp(&self, other: &Self) -> Option<crate::cmp::Ordering> {
self._phantom.partial_cmp(&other._phantom)
}
}
impl<T: ?Sized, const VARIANT: u32, const FIELD: u32> Ord
for FieldRepresentingType<T, VARIANT, FIELD>
{
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
+12 -13
View File
@@ -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() };
}
+34 -2
View File
@@ -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<u8>, Vec<u8>)> {
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<S: AsRef<OsStr> + ?Sized>(prog: &S) -> Option<PathBuf> {
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};
@@ -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`).
@@ -0,0 +1,8 @@
[package]
name = "amdgpu_lto"
version = "0.1.0"
edition = "2024"
[lib]
path = "lib.rs"
crate-type = ["cdylib"]
+15
View File
@@ -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() {}
+28
View File
@@ -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();
}
@@ -0,0 +1,14 @@
struct SmolStr;
const _: fn() = || {
match Some(()) {
Some(()) => (),
None => return,
};
let _: String = {
SmolStr
//~^ ERROR mismatched types
};
};
fn main() {}
@@ -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`.
@@ -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::<field_of!(MyStruct, 0)>();
| ^^^^^^^^^^^^^^^^^^^^^^ 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<F: 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::<field_of!(MyStruct, 1)>();
| ^^^^^^^^^^^^^^^^^^^^^^ 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<F: 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`.
@@ -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::<field_of!(MyStruct, 0)>();
| ^^^^^^^^^^^^^^^^^^^^^^ 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<F: 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::<field_of!(MyStruct, 1)>();
| ^^^^^^^^^^^^^^^^^^^^^^ 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<F: 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`.
@@ -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<F: Field>() {}
fn main() {
// FIXME(FRTs): this requires relaxing the `Base: ?Sized` bound in the
// `Field` trait & compiler changes.
assert_field::<field_of!(MyStruct, 0)>();
//~^ 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::<field_of!(MyStruct, 1)>();
//~^ ERROR: the trait bound `field_of!(MyStruct, 1): std::field::Field` is not satisfied [E0277]
}
+33 -3
View File
@@ -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<T: Send + Sync + Unpin + Copy + Clone + Sized + Freeze>() {}
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::<field_of!(Struct, field)>();
assert_traits::<field_of!(Struct, tail)>();
assert_traits::<field_of!(Union, field)>();
assert_traits::<field_of!(Enum, Variant1.field)>();
assert_traits::<field_of!(Enum, Variant2.0)>();
assert_traits::<field_of!(Tuple, 0)>();
assert_traits::<field_of!(Tuple, 1)>();
assert_traits::<field_of!(Tuple, 2)>();
assert_traits::<field_of!(Tuple, 3)>();
}
@@ -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<dyn DynCompatible> {
|
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
@@ -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<dyn std::fmt::Display> {
|
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<dyn std::fmt::Display> {
|
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<dyn std::fmt::Display> {
|
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<dyn std::fmt::Display> {
|
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<dyn std::fmt::Display> {
|
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<dyn std::fmt::Display> {
|
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()