mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
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:
@@ -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
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>;
|
||||
|
||||
@@ -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) {}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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
@@ -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() };
|
||||
}
|
||||
|
||||
|
||||
@@ -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"]
|
||||
@@ -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() {}
|
||||
@@ -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]
|
||||
}
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user