Auto merge of #147019 - Zalathar:rollup-boxzbmo, r=Zalathar

Rollup of 14 pull requests

Successful merges:

 - rust-lang/rust#145067 (RawVecInner: add missing `unsafe` to unsafe fns)
 - rust-lang/rust#145277 (Do not materialise X in [X; 0] when X is unsizing a const)
 - rust-lang/rust#145973 (Add `std` support for `armv7a-vex-v5`)
 - rust-lang/rust#146667 (Add an attribute to check the number of lanes in a SIMD vector after monomorphization)
 - rust-lang/rust#146735 (unstably constify float mul_add methods)
 - rust-lang/rust#146737 (f16_f128: enable some more tests in Miri)
 - rust-lang/rust#146766 (Add attributes for #[global_allocator] functions)
 - rust-lang/rust#146905 (llvm: update remarks support on LLVM 22)
 - rust-lang/rust#146982 (Remove erroneous normalization step in `tests/run-make/linker-warning`)
 - rust-lang/rust#147005 (Small string formatting cleanup)
 - rust-lang/rust#147007 (Explicitly note `&[SocketAddr]` impl of `ToSocketAddrs`)
 - rust-lang/rust#147008 (bootstrap.py: Respect build.jobs while building bootstrap tool)
 - rust-lang/rust#147013 (rustdoc: Fix documentation for `--doctest-build-arg`)
 - rust-lang/rust#147015 (Use `LLVMDisposeTargetMachine`)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-09-25 14:03:21 +00:00
commit 6f34f4ee07
90 changed files with 1670 additions and 319 deletions

View File

@ -1,40 +1,4 @@
use std::num::IntErrorKind;
use rustc_hir::limit::Limit;
use super::prelude::*;
use crate::session_diagnostics::LimitInvalid;
impl<S: Stage> AcceptContext<'_, '_, S> {
fn parse_limit_int(&self, nv: &NameValueParser) -> Option<Limit> {
let Some(limit) = nv.value_as_str() else {
self.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return None;
};
let error_str = match limit.as_str().parse() {
Ok(i) => return Some(Limit::new(i)),
Err(e) => match e.kind() {
IntErrorKind::PosOverflow => "`limit` is too large",
IntErrorKind::Empty => "`limit` must be a non-negative integer",
IntErrorKind::InvalidDigit => "not a valid integer",
IntErrorKind::NegOverflow => {
panic!(
"`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead"
)
}
IntErrorKind::Zero => {
panic!("zero is a valid `limit` so should have returned Ok() when parsing")
}
kind => panic!("unimplemented IntErrorKind variant: {:?}", kind),
},
};
self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str });
None
}
}
pub(crate) struct CrateNameParser;

View File

@ -49,3 +49,21 @@ impl<S: Stage> SingleAttributeParser<S> for RustcObjectLifetimeDefaultParser {
Some(AttributeKind::RustcObjectLifetimeDefault)
}
}
pub(crate) struct RustcSimdMonomorphizeLaneLimitParser;
impl<S: Stage> SingleAttributeParser<S> for RustcSimdMonomorphizeLaneLimitParser {
const PATH: &[Symbol] = &[sym::rustc_simd_monomorphize_lane_limit];
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepInnermost;
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Struct)]);
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
let ArgParser::NameValue(nv) = args else {
cx.expected_name_value(cx.attr_span, None);
return None;
};
Some(AttributeKind::RustcSimdMonomorphizeLaneLimit(cx.parse_limit_int(nv)?))
}
}

View File

@ -1,11 +1,15 @@
use std::num::IntErrorKind;
use rustc_ast::LitKind;
use rustc_ast::attr::AttributeExt;
use rustc_feature::is_builtin_attr_name;
use rustc_hir::RustcVersion;
use rustc_hir::limit::Limit;
use rustc_span::{Symbol, sym};
use crate::context::{AcceptContext, Stage};
use crate::parser::ArgParser;
use crate::parser::{ArgParser, NameValueParser};
use crate::session_diagnostics::LimitInvalid;
/// Parse a rustc version number written inside string literal in an attribute,
/// like appears in `since = "1.0.0"`. Suffixes like "-dev" and "-nightly" are
@ -85,3 +89,34 @@ pub(crate) fn parse_single_integer<S: Stage>(
};
Some(num.0)
}
impl<S: Stage> AcceptContext<'_, '_, S> {
pub(crate) fn parse_limit_int(&self, nv: &NameValueParser) -> Option<Limit> {
let Some(limit) = nv.value_as_str() else {
self.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
return None;
};
let error_str = match limit.as_str().parse() {
Ok(i) => return Some(Limit::new(i)),
Err(e) => match e.kind() {
IntErrorKind::PosOverflow => "`limit` is too large",
IntErrorKind::Empty => "`limit` must be a non-negative integer",
IntErrorKind::InvalidDigit => "not a valid integer",
IntErrorKind::NegOverflow => {
panic!(
"`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead"
)
}
IntErrorKind::Zero => {
panic!("zero is a valid `limit` so should have returned Ok() when parsing")
}
kind => panic!("unimplemented IntErrorKind variant: {:?}", kind),
},
};
self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str });
None
}
}

View File

@ -53,7 +53,7 @@ use crate::attributes::prototype::CustomMirParser;
use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser};
use crate::attributes::rustc_internal::{
RustcLayoutScalarValidRangeEnd, RustcLayoutScalarValidRangeStart,
RustcObjectLifetimeDefaultParser,
RustcObjectLifetimeDefaultParser, RustcSimdMonomorphizeLaneLimitParser,
};
use crate::attributes::semantics::MayDangleParser;
use crate::attributes::stability::{
@ -198,6 +198,7 @@ attribute_parsers!(
Single<RustcLayoutScalarValidRangeEnd>,
Single<RustcLayoutScalarValidRangeStart>,
Single<RustcObjectLifetimeDefaultParser>,
Single<RustcSimdMonomorphizeLaneLimitParser>,
Single<SanitizeParser>,
Single<ShouldPanicParser>,
Single<SkipDuringMethodDispatchParser>,

View File

@ -85,7 +85,7 @@ impl AllocFnFactory<'_, '_> {
body,
define_opaque: None,
}));
let item = self.cx.item(self.span, self.attrs(), kind);
let item = self.cx.item(self.span, self.attrs(method), kind);
self.cx.stmt_item(self.ty_span, item)
}
@ -100,8 +100,18 @@ impl AllocFnFactory<'_, '_> {
self.cx.expr_call(self.ty_span, method, args)
}
fn attrs(&self) -> AttrVec {
thin_vec![self.cx.attr_word(sym::rustc_std_internal_symbol, self.span)]
fn attrs(&self, method: &AllocatorMethod) -> AttrVec {
let alloc_attr = match method.name {
sym::alloc => sym::rustc_allocator,
sym::dealloc => sym::rustc_deallocator,
sym::realloc => sym::rustc_reallocator,
sym::alloc_zeroed => sym::rustc_allocator_zeroed,
_ => unreachable!("Unknown allocator method!"),
};
thin_vec![
self.cx.attr_word(sym::rustc_std_internal_symbol, self.span),
self.cx.attr_word(alloc_attr, self.span)
]
}
fn arg_ty(&self, input: &AllocatorMethodInput, args: &mut ThinVec<Param>) -> Box<Expr> {

View File

@ -439,7 +439,10 @@ pub(crate) struct FullyMonomorphizedLayoutCx<'tcx>(pub(crate) TyCtxt<'tcx>);
impl<'tcx> LayoutOfHelpers<'tcx> for FullyMonomorphizedLayoutCx<'tcx> {
#[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
if let LayoutError::SizeOverflow(_)
| LayoutError::InvalidSimd { .. }
| LayoutError::ReferencesError(_) = err
{
self.0.sess.dcx().span_fatal(span, err.to_string())
} else {
self.0
@ -458,7 +461,9 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for FullyMonomorphizedLayoutCx<'tcx> {
span: Span,
fn_abi_request: FnAbiRequest<'tcx>,
) -> ! {
if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err {
if let FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::InvalidSimd { .. }) =
err
{
self.0.sess.dcx().emit_fatal(Spanned { span, node: err })
} else {
match fn_abi_request {

View File

@ -42,7 +42,10 @@ impl<'tcx> AsmCodegenMethods<'tcx> for GlobalAsmContext<'_, 'tcx> {
impl<'tcx> LayoutOfHelpers<'tcx> for GlobalAsmContext<'_, 'tcx> {
#[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
if let LayoutError::SizeOverflow(_)
| LayoutError::InvalidSimd { .. }
| LayoutError::ReferencesError(_) = err
{
self.tcx.sess.dcx().span_fatal(span, err.to_string())
} else {
self.tcx

View File

@ -529,7 +529,10 @@ impl<'gcc, 'tcx> HasX86AbiOpt for CodegenCx<'gcc, 'tcx> {
impl<'gcc, 'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
#[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
if let LayoutError::SizeOverflow(_)
| LayoutError::InvalidSimd { .. }
| LayoutError::ReferencesError(_) = err
{
self.tcx.dcx().emit_fatal(respan(span, err.into_diagnostic()))
} else {
self.tcx.dcx().emit_fatal(ssa_errors::FailedToGetLayout { span, ty, err })
@ -545,7 +548,9 @@ impl<'gcc, 'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'gcc, 'tcx> {
span: Span,
fn_abi_request: FnAbiRequest<'tcx>,
) -> ! {
if let FnAbiError::Layout(LayoutError::SizeOverflow(_)) = err {
if let FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::InvalidSimd { .. }) =
err
{
self.tcx.dcx().emit_fatal(respan(span, err))
} else {
match fn_abi_request {

View File

@ -95,8 +95,6 @@ impl Drop for OwnedTargetMachine {
// SAFETY: constructing ensures we have a valid pointer created by
// llvm::LLVMRustCreateTargetMachine OwnedTargetMachine is not copyable so there is no
// double free or use after free.
unsafe {
llvm::LLVMRustDisposeTargetMachine(self.tm_unique.as_ptr());
}
unsafe { llvm::LLVMDisposeTargetMachine(self.tm_unique) };
}
}

View File

@ -1044,7 +1044,10 @@ impl<'tcx, 'll> HasTypingEnv<'tcx> for CodegenCx<'ll, 'tcx> {
impl<'tcx> LayoutOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
#[inline]
fn handle_layout_err(&self, err: LayoutError<'tcx>, span: Span, ty: Ty<'tcx>) -> ! {
if let LayoutError::SizeOverflow(_) | LayoutError::ReferencesError(_) = err {
if let LayoutError::SizeOverflow(_)
| LayoutError::ReferencesError(_)
| LayoutError::InvalidSimd { .. } = err
{
self.tcx.dcx().emit_fatal(Spanned { span, node: err.into_diagnostic() })
} else {
self.tcx.dcx().emit_fatal(ssa_errors::FailedToGetLayout { span, ty, err })
@ -1061,7 +1064,11 @@ impl<'tcx> FnAbiOfHelpers<'tcx> for CodegenCx<'_, 'tcx> {
fn_abi_request: FnAbiRequest<'tcx>,
) -> ! {
match err {
FnAbiError::Layout(LayoutError::SizeOverflow(_) | LayoutError::Cycle(_)) => {
FnAbiError::Layout(
LayoutError::SizeOverflow(_)
| LayoutError::Cycle(_)
| LayoutError::InvalidSimd { .. },
) => {
self.tcx.dcx().emit_fatal(Spanned { span, node: err });
}
_ => match fn_abi_request {

View File

@ -477,8 +477,8 @@ pub(crate) fn spanned_type_di_node<'ll, 'tcx>(
ty::CoroutineClosure(..) => build_closure_env_di_node(cx, unique_type_id),
ty::Coroutine(..) => enums::build_coroutine_di_node(cx, unique_type_id),
ty::Adt(def, ..) => match def.adt_kind() {
AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id),
AdtKind::Union => build_union_type_di_node(cx, unique_type_id),
AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id, span),
AdtKind::Union => build_union_type_di_node(cx, unique_type_id, span),
AdtKind::Enum => enums::build_enum_type_di_node(cx, unique_type_id, span),
},
ty::Tuple(_) => build_tuple_type_di_node(cx, unique_type_id),
@ -1076,6 +1076,7 @@ fn visibility_di_flags<'ll, 'tcx>(
fn build_struct_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
unique_type_id: UniqueTypeId<'tcx>,
span: Span,
) -> DINodeCreationResult<'ll> {
let struct_type = unique_type_id.expect_ty();
let ty::Adt(adt_def, _) = struct_type.kind() else {
@ -1083,7 +1084,7 @@ fn build_struct_type_di_node<'ll, 'tcx>(
};
assert!(adt_def.is_struct());
let containing_scope = get_namespace_for_item(cx, adt_def.did());
let struct_type_and_layout = cx.layout_of(struct_type);
let struct_type_and_layout = cx.spanned_layout_of(struct_type, span);
let variant_def = adt_def.non_enum_variant();
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(file_metadata_from_def_id(cx, Some(adt_def.did())))
@ -1276,6 +1277,7 @@ fn build_closure_env_di_node<'ll, 'tcx>(
fn build_union_type_di_node<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
unique_type_id: UniqueTypeId<'tcx>,
span: Span,
) -> DINodeCreationResult<'ll> {
let union_type = unique_type_id.expect_ty();
let (union_def_id, variant_def) = match union_type.kind() {
@ -1283,7 +1285,7 @@ fn build_union_type_di_node<'ll, 'tcx>(
_ => bug!("build_union_type_di_node on a non-ADT"),
};
let containing_scope = get_namespace_for_item(cx, union_def_id);
let union_ty_and_layout = cx.layout_of(union_type);
let union_ty_and_layout = cx.spanned_layout_of(union_type, span);
let type_name = compute_debuginfo_type_name(cx.tcx, union_type, false);
let def_location = if cx.sess().opts.unstable_opts.debug_info_type_line_numbers {
Some(file_metadata_from_def_id(cx, Some(union_def_id)))

View File

@ -1053,6 +1053,8 @@ unsafe extern "C" {
SLen: c_uint,
) -> MetadataKindId;
pub(crate) fn LLVMDisposeTargetMachine(T: ptr::NonNull<TargetMachine>);
// Create modules.
pub(crate) fn LLVMModuleCreateWithNameInContext(
ModuleID: *const c_char,
@ -2499,7 +2501,6 @@ unsafe extern "C" {
UseWasmEH: bool,
) -> *mut TargetMachine;
pub(crate) fn LLVMRustDisposeTargetMachine(T: *mut TargetMachine);
pub(crate) fn LLVMRustAddLibraryInfo<'a>(
PM: &PassManager<'a>,
M: &'a Module,

View File

@ -636,6 +636,14 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
dest,
rustc_apfloat::Round::NearestTiesToEven,
)?,
sym::fmaf16 => self.fma_intrinsic::<Half>(args, dest)?,
sym::fmaf32 => self.fma_intrinsic::<Single>(args, dest)?,
sym::fmaf64 => self.fma_intrinsic::<Double>(args, dest)?,
sym::fmaf128 => self.fma_intrinsic::<Quad>(args, dest)?,
sym::fmuladdf16 => self.float_muladd_intrinsic::<Half>(args, dest)?,
sym::fmuladdf32 => self.float_muladd_intrinsic::<Single>(args, dest)?,
sym::fmuladdf64 => self.float_muladd_intrinsic::<Double>(args, dest)?,
sym::fmuladdf128 => self.float_muladd_intrinsic::<Quad>(args, dest)?,
// Unsupported intrinsic: skip the return_to_block below.
_ => return interp_ok(false),
@ -1035,4 +1043,42 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.write_scalar(res, dest)?;
interp_ok(())
}
fn fma_intrinsic<F>(
&mut self,
args: &[OpTy<'tcx, M::Provenance>],
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()>
where
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
let c: F = self.read_scalar(&args[2])?.to_float()?;
let res = a.mul_add(b, c).value;
let res = self.adjust_nan(res, &[a, b, c]);
self.write_scalar(res, dest)?;
interp_ok(())
}
fn float_muladd_intrinsic<F>(
&mut self,
args: &[OpTy<'tcx, M::Provenance>],
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx, ()>
where
F: rustc_apfloat::Float + rustc_apfloat::FloatConvert<F> + Into<Scalar<M::Provenance>>,
{
let a: F = self.read_scalar(&args[0])?.to_float()?;
let b: F = self.read_scalar(&args[1])?.to_float()?;
let c: F = self.read_scalar(&args[2])?.to_float()?;
let fuse = M::float_fuse_mul_add(self);
let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
let res = self.adjust_nan(res, &[a, b, c]);
self.write_scalar(res, dest)?;
interp_ok(())
}
}

View File

@ -289,6 +289,9 @@ pub trait Machine<'tcx>: Sized {
a
}
/// Determines whether the `fmuladd` intrinsics fuse the multiply-add or use separate operations.
fn float_fuse_mul_add(_ecx: &mut InterpCx<'tcx, Self>) -> bool;
/// Called before a basic block terminator is executed.
#[inline]
fn before_terminator(_ecx: &mut InterpCx<'tcx, Self>) -> InterpResult<'tcx> {
@ -672,6 +675,11 @@ pub macro compile_time_machine(<$tcx: lifetime>) {
match fn_val {}
}
#[inline(always)]
fn float_fuse_mul_add(_ecx: &mut InterpCx<$tcx, Self>) -> bool {
true
}
#[inline(always)]
fn ub_checks(_ecx: &InterpCx<$tcx, Self>) -> InterpResult<$tcx, bool> {
// We can't look at `tcx.sess` here as that can differ across crates, which can lead to

View File

@ -1211,6 +1211,12 @@ pub static BUILTIN_ATTRIBUTES: &[BuiltinAttribute] = &[
"the `#[rustc_layout_scalar_valid_range_end]` attribute is just used to enable \
niche optimizations in the standard library",
),
rustc_attr!(
rustc_simd_monomorphize_lane_limit, Normal, template!(NameValueStr: "N"), ErrorFollowing,
EncodeCrossCrate::Yes,
"the `#[rustc_simd_monomorphize_lane_limit]` attribute is just used by std::simd \
for better error messages",
),
rustc_attr!(
rustc_nonnull_optimization_guaranteed, Normal, template!(Word), WarnFollowing,
EncodeCrossCrate::Yes,

View File

@ -651,6 +651,9 @@ pub enum AttributeKind {
/// Represents `#[rustc_object_lifetime_default]`.
RustcObjectLifetimeDefault,
/// Represents `#[rustc_simd_monomorphize_lane_limit = "N"]`.
RustcSimdMonomorphizeLaneLimit(Limit),
/// Represents `#[sanitize]`
///
/// the on set and off set are distjoint since there's a third option: unset.

View File

@ -88,6 +88,7 @@ impl AttributeKind {
RustcLayoutScalarValidRangeEnd(..) => Yes,
RustcLayoutScalarValidRangeStart(..) => Yes,
RustcObjectLifetimeDefault => No,
RustcSimdMonomorphizeLaneLimit(..) => Yes, // Affects layout computation, which needs to work cross-crate
Sanitize { .. } => No,
ShouldPanic { .. } => No,
SkipDuringMethodDispatch { .. } => No,

View File

@ -219,6 +219,7 @@ fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError
}
Unknown(..)
| SizeOverflow(..)
| InvalidSimd { .. }
| NormalizationFailure(..)
| ReferencesError(..)
| Cycle(..) => {

View File

@ -386,8 +386,8 @@ fn get_codegen_sysroot(
.collect::<Vec<_>>()
.join("\n* ");
let err = format!(
"failed to find a `codegen-backends` folder \
in the sysroot candidates:\n* {candidates}"
"failed to find a `codegen-backends` folder in the sysroot candidates:\n\
* {candidates}"
);
early_dcx.early_fatal(err);
});
@ -396,10 +396,8 @@ fn get_codegen_sysroot(
let d = sysroot.read_dir().unwrap_or_else(|e| {
let err = format!(
"failed to load default codegen backend, couldn't \
read `{}`: {}",
"failed to load default codegen backend, couldn't read `{}`: {e}",
sysroot.display(),
e
);
early_dcx.early_fatal(err);
});

View File

@ -359,10 +359,6 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
return wrap(TM);
}
extern "C" void LLVMRustDisposeTargetMachine(LLVMTargetMachineRef TM) {
delete unwrap(TM);
}
// Unfortunately, the LLVM C API doesn't provide a way to create the
// TargetLibraryInfo pass, so we use this method to do so.
extern "C" void LLVMRustAddLibraryInfo(LLVMPassManagerRef PMR, LLVMModuleRef M,

View File

@ -1685,6 +1685,14 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler(
RemarkStreamer(std::move(RemarkStreamer)),
LlvmRemarkStreamer(std::move(LlvmRemarkStreamer)) {}
#if LLVM_VERSION_GE(22, 0)
~RustDiagnosticHandler() {
if (RemarkStreamer) {
RemarkStreamer->releaseSerializer();
}
}
#endif
virtual bool handleDiagnostics(const DiagnosticInfo &DI) override {
// If this diagnostic is one of the optimization remark kinds, we can
// check if it's enabled before emitting it. This can avoid many
@ -1779,9 +1787,14 @@ extern "C" void LLVMRustContextConfigureDiagnosticHandler(
// Do not delete the file after we gather remarks
RemarkFile->keep();
#if LLVM_VERSION_GE(22, 0)
auto RemarkSerializer = remarks::createRemarkSerializer(
llvm::remarks::Format::YAML, RemarkFile->os());
#else
auto RemarkSerializer = remarks::createRemarkSerializer(
llvm::remarks::Format::YAML, remarks::SerializerMode::Separate,
RemarkFile->os());
#endif
if (Error E = RemarkSerializer.takeError()) {
std::string Error = std::string("Cannot create remark serializer: ") +
toString(std::move(E));

View File

@ -98,6 +98,12 @@ middle_layout_references_error =
middle_layout_size_overflow =
values of the type `{$ty}` are too big for the target architecture
middle_layout_simd_too_many =
the SIMD type `{$ty}` has more elements than the limit {$max_lanes}
middle_layout_simd_zero_length =
the SIMD type `{$ty}` has zero elements
middle_layout_too_generic = the type `{$ty}` does not have a fixed layout
middle_layout_unknown =

View File

@ -143,6 +143,12 @@ pub enum LayoutError<'tcx> {
#[diag(middle_layout_size_overflow)]
Overflow { ty: Ty<'tcx> },
#[diag(middle_layout_simd_too_many)]
SimdTooManyLanes { ty: Ty<'tcx>, max_lanes: u64 },
#[diag(middle_layout_simd_zero_length)]
SimdZeroLength { ty: Ty<'tcx> },
#[diag(middle_layout_normalization_failure)]
NormalizationFailure { ty: Ty<'tcx>, failure_ty: String },

View File

@ -218,6 +218,15 @@ impl fmt::Display for ValidityRequirement {
}
}
#[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)]
pub enum SimdLayoutError {
/// The vector has 0 lanes.
ZeroLength,
/// The vector has more lanes than supported or permitted by
/// #\[rustc_simd_monomorphize_lane_limit\].
TooManyLanes(u64),
}
#[derive(Copy, Clone, Debug, HashStable, TyEncodable, TyDecodable)]
pub enum LayoutError<'tcx> {
/// A type doesn't have a sensible layout.
@ -230,6 +239,8 @@ pub enum LayoutError<'tcx> {
Unknown(Ty<'tcx>),
/// The size of a type exceeds [`TargetDataLayout::obj_size_bound`].
SizeOverflow(Ty<'tcx>),
/// A SIMD vector has invalid layout, such as zero-length or too many lanes.
InvalidSimd { ty: Ty<'tcx>, kind: SimdLayoutError },
/// The layout can vary due to a generic parameter.
///
/// Unlike `Unknown`, this variant is a "soft" error and indicates that the layout
@ -257,6 +268,10 @@ impl<'tcx> LayoutError<'tcx> {
match self {
Unknown(_) => middle_layout_unknown,
SizeOverflow(_) => middle_layout_size_overflow,
InvalidSimd { kind: SimdLayoutError::TooManyLanes(_), .. } => {
middle_layout_simd_too_many
}
InvalidSimd { kind: SimdLayoutError::ZeroLength, .. } => middle_layout_simd_zero_length,
TooGeneric(_) => middle_layout_too_generic,
NormalizationFailure(_, _) => middle_layout_normalization_failure,
Cycle(_) => middle_layout_cycle,
@ -271,6 +286,10 @@ impl<'tcx> LayoutError<'tcx> {
match self {
Unknown(ty) => E::Unknown { ty },
SizeOverflow(ty) => E::Overflow { ty },
InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } => {
E::SimdTooManyLanes { ty, max_lanes }
}
InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } => E::SimdZeroLength { ty },
TooGeneric(ty) => E::TooGeneric { ty },
NormalizationFailure(ty, e) => {
E::NormalizationFailure { ty, failure_ty: e.get_type_for_failure() }
@ -293,6 +312,12 @@ impl<'tcx> fmt::Display for LayoutError<'tcx> {
LayoutError::SizeOverflow(ty) => {
write!(f, "values of the type `{ty}` are too big for the target architecture")
}
LayoutError::InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) } => {
write!(f, "the SIMD type `{ty}` has more elements than the limit {max_lanes}")
}
LayoutError::InvalidSimd { ty, kind: SimdLayoutError::ZeroLength } => {
write!(f, "the SIMD type `{ty}` has zero elements")
}
LayoutError::NormalizationFailure(t, e) => write!(
f,
"unable to determine layout for `{}` because `{}` cannot be normalized",
@ -374,6 +399,7 @@ impl<'tcx> SizeSkeleton<'tcx> {
e @ LayoutError::Cycle(_)
| e @ LayoutError::Unknown(_)
| e @ LayoutError::SizeOverflow(_)
| e @ LayoutError::InvalidSimd { .. }
| e @ LayoutError::NormalizationFailure(..)
| e @ LayoutError::ReferencesError(_),
) => return Err(e),

View File

@ -8,6 +8,7 @@ use rustc_middle::middle::region;
use rustc_middle::mir::interpret::Scalar;
use rustc_middle::mir::*;
use rustc_middle::thir::*;
use rustc_middle::ty::adjustment::PointerCoercion;
use rustc_middle::ty::cast::{CastTy, mir_cast_kind};
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, Ty, UpvarArgs};
@ -656,6 +657,27 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
block.and(rvalue)
}
/// Recursively inspect a THIR expression and probe through unsizing
/// operations that can be const-folded today.
fn check_constness(&self, mut kind: &'a ExprKind<'tcx>) -> bool {
loop {
debug!(?kind, "check_constness");
match kind {
&ExprKind::ValueTypeAscription { source: eid, user_ty: _, user_ty_span: _ }
| &ExprKind::Use { source: eid }
| &ExprKind::PointerCoercion {
cast: PointerCoercion::Unsize,
source: eid,
is_from_as_cast: _,
}
| &ExprKind::Scope { region_scope: _, lint_level: _, value: eid } => {
kind = &self.thir[eid].kind
}
_ => return matches!(Category::of(&kind), Some(Category::Constant)),
}
}
}
fn build_zero_repeat(
&mut self,
mut block: BasicBlock,
@ -666,7 +688,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
let this = self;
let value_expr = &this.thir[value];
let elem_ty = value_expr.ty;
if let Some(Category::Constant) = Category::of(&value_expr.kind) {
if this.check_constness(&value_expr.kind) {
// Repeating a const does nothing
} else {
// For a non-const, we may need to generate an appropriate `Drop`

View File

@ -256,6 +256,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> {
| AttributeKind::MacroEscape( .. )
| AttributeKind::RustcLayoutScalarValidRangeStart(..)
| AttributeKind::RustcLayoutScalarValidRangeEnd(..)
| AttributeKind::RustcSimdMonomorphizeLaneLimit(..)
| AttributeKind::ExportStable
| AttributeKind::FfiConst(..)
| AttributeKind::UnstableFeatureBound(..)

View File

@ -1943,6 +1943,7 @@ symbols! {
rustc_regions,
rustc_reservation_impl,
rustc_serialize,
rustc_simd_monomorphize_lane_limit,
rustc_skip_during_method_dispatch,
rustc_specialization_trait,
rustc_std_internal_symbol,

View File

@ -34,7 +34,7 @@ pub(crate) fn target() -> Target {
description: Some("ARMv7-A Cortex-A9 VEX V5 Brain".into()),
tier: Some(3),
host_tools: Some(false),
std: Some(false),
std: Some(true),
},
pointer_width: 32,
data_layout: "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64".into(),

View File

@ -279,6 +279,7 @@ pub(crate) mod rustc {
LayoutError::Unknown(..)
| LayoutError::ReferencesError(..)
| LayoutError::TooGeneric(..)
| LayoutError::InvalidSimd { .. }
| LayoutError::NormalizationFailure(..) => Self::UnknownLayout,
LayoutError::SizeOverflow(..) => Self::SizeOverflow,
LayoutError::Cycle(err) => Self::TypeError(*err),

View File

@ -78,19 +78,6 @@ pub(crate) struct UnexpectedFnPtrAssociatedItem {
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(ty_utils_zero_length_simd_type)]
pub(crate) struct ZeroLengthSimdType<'tcx> {
pub ty: Ty<'tcx>,
}
#[derive(Diagnostic)]
#[diag(ty_utils_oversized_simd_type)]
pub(crate) struct OversizedSimdType<'tcx> {
pub ty: Ty<'tcx>,
pub max_lanes: u64,
}
#[derive(Diagnostic)]
#[diag(ty_utils_non_primitive_simd_type)]
pub(crate) struct NonPrimitiveSimdType<'tcx> {

View File

@ -7,12 +7,14 @@ use rustc_abi::{
VariantIdx, Variants, WrappingRange,
};
use rustc_hashes::Hash64;
use rustc_hir::attrs::AttributeKind;
use rustc_hir::find_attr;
use rustc_index::IndexVec;
use rustc_middle::bug;
use rustc_middle::query::Providers;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::layout::{
FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, TyAndLayout,
FloatExt, HasTyCtxt, IntegerExt, LayoutCx, LayoutError, LayoutOf, SimdLayoutError, TyAndLayout,
};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{
@ -23,7 +25,7 @@ use rustc_span::{Symbol, sym};
use tracing::{debug, instrument};
use {rustc_abi as abi, rustc_hir as hir};
use crate::errors::{NonPrimitiveSimdType, OversizedSimdType, ZeroLengthSimdType};
use crate::errors::NonPrimitiveSimdType;
mod invariant;
@ -121,11 +123,11 @@ fn map_error<'tcx>(
}
LayoutCalculatorError::ZeroLengthSimdType => {
// Can't be caught in typeck if the array length is generic.
cx.tcx().dcx().emit_fatal(ZeroLengthSimdType { ty })
LayoutError::InvalidSimd { ty, kind: SimdLayoutError::ZeroLength }
}
LayoutCalculatorError::OversizedSimdType { max_lanes } => {
// Can't be caught in typeck if the array length is generic.
cx.tcx().dcx().emit_fatal(OversizedSimdType { ty, max_lanes })
LayoutError::InvalidSimd { ty, kind: SimdLayoutError::TooManyLanes(max_lanes) }
}
LayoutCalculatorError::NonPrimitiveSimdType(field) => {
// This error isn't caught in typeck, e.g., if
@ -563,6 +565,22 @@ fn layout_of_uncached<'tcx>(
let e_ly = cx.layout_of(e_ty)?;
// Check for the rustc_simd_monomorphize_lane_limit attribute and check the lane limit
if let Some(limit) = find_attr!(
tcx.get_all_attrs(def.did()),
AttributeKind::RustcSimdMonomorphizeLaneLimit(limit) => limit
) {
if !limit.value_within_limit(e_len as usize) {
return Err(map_error(
&cx,
ty,
rustc_abi::LayoutCalculatorError::OversizedSimdType {
max_lanes: limit.0 as u64,
},
));
}
}
map_layout(cx.calc.simd_type(e_ly, e_len, def.repr().packed()))?
}

View File

@ -326,6 +326,7 @@ dependencies = [
"rustc-demangle",
"std_detect",
"unwind",
"vex-sdk",
"wasi 0.11.1+wasi-snapshot-preview1",
"wasi 0.14.4+wasi-0.2.4",
"windows-targets 0.0.0",
@ -379,6 +380,15 @@ dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "vex-sdk"
version = "0.27.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89f74fce61d7a7ba1589da9634c6305a72befb7cc9150c1f872d87d8060f32b9"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"

View File

@ -177,6 +177,8 @@ impl<T, A: Allocator> RawVec<T, A> {
/// the returned `RawVec`.
#[inline]
pub(crate) const fn new_in(alloc: A) -> Self {
// Check assumption made in `current_memory`
const { assert!(T::LAYOUT.size() % T::LAYOUT.align() == 0) };
Self { inner: RawVecInner::new_in(alloc, Alignment::of::<T>()), _marker: PhantomData }
}
@ -328,7 +330,8 @@ impl<T, A: Allocator> RawVec<T, A> {
#[inline]
#[track_caller]
pub(crate) fn reserve(&mut self, len: usize, additional: usize) {
self.inner.reserve(len, additional, T::LAYOUT)
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
unsafe { self.inner.reserve(len, additional, T::LAYOUT) }
}
/// A specialized version of `self.reserve(len, 1)` which requires the
@ -337,7 +340,8 @@ impl<T, A: Allocator> RawVec<T, A> {
#[inline(never)]
#[track_caller]
pub(crate) fn grow_one(&mut self) {
self.inner.grow_one(T::LAYOUT)
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
unsafe { self.inner.grow_one(T::LAYOUT) }
}
/// The same as `reserve`, but returns on errors instead of panicking or aborting.
@ -346,7 +350,8 @@ impl<T, A: Allocator> RawVec<T, A> {
len: usize,
additional: usize,
) -> Result<(), TryReserveError> {
self.inner.try_reserve(len, additional, T::LAYOUT)
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
unsafe { self.inner.try_reserve(len, additional, T::LAYOUT) }
}
/// Ensures that the buffer contains at least enough space to hold `len +
@ -369,7 +374,8 @@ impl<T, A: Allocator> RawVec<T, A> {
#[cfg(not(no_global_oom_handling))]
#[track_caller]
pub(crate) fn reserve_exact(&mut self, len: usize, additional: usize) {
self.inner.reserve_exact(len, additional, T::LAYOUT)
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
unsafe { self.inner.reserve_exact(len, additional, T::LAYOUT) }
}
/// The same as `reserve_exact`, but returns on errors instead of panicking or aborting.
@ -378,7 +384,8 @@ impl<T, A: Allocator> RawVec<T, A> {
len: usize,
additional: usize,
) -> Result<(), TryReserveError> {
self.inner.try_reserve_exact(len, additional, T::LAYOUT)
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
unsafe { self.inner.try_reserve_exact(len, additional, T::LAYOUT) }
}
/// Shrinks the buffer down to the specified capacity. If the given amount
@ -395,7 +402,8 @@ impl<T, A: Allocator> RawVec<T, A> {
#[track_caller]
#[inline]
pub(crate) fn shrink_to_fit(&mut self, cap: usize) {
self.inner.shrink_to_fit(cap, T::LAYOUT)
// SAFETY: All calls on self.inner pass T::LAYOUT as the elem_layout
unsafe { self.inner.shrink_to_fit(cap, T::LAYOUT) }
}
}
@ -518,8 +526,12 @@ impl<A: Allocator> RawVecInner<A> {
&self.alloc
}
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
#[inline]
fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull<u8>, Layout)> {
unsafe fn current_memory(&self, elem_layout: Layout) -> Option<(NonNull<u8>, Layout)> {
if elem_layout.size() == 0 || self.cap.as_inner() == 0 {
None
} else {
@ -535,48 +547,67 @@ impl<A: Allocator> RawVecInner<A> {
}
}
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
#[cfg(not(no_global_oom_handling))]
#[inline]
#[track_caller]
fn reserve(&mut self, len: usize, additional: usize, elem_layout: Layout) {
unsafe fn reserve(&mut self, len: usize, additional: usize, elem_layout: Layout) {
// Callers expect this function to be very cheap when there is already sufficient capacity.
// Therefore, we move all the resizing and error-handling logic from grow_amortized and
// handle_reserve behind a call, while making sure that this function is likely to be
// inlined as just a comparison and a call if the comparison fails.
#[cold]
fn do_reserve_and_handle<A: Allocator>(
unsafe fn do_reserve_and_handle<A: Allocator>(
slf: &mut RawVecInner<A>,
len: usize,
additional: usize,
elem_layout: Layout,
) {
if let Err(err) = slf.grow_amortized(len, additional, elem_layout) {
// SAFETY: Precondition passed to caller
if let Err(err) = unsafe { slf.grow_amortized(len, additional, elem_layout) } {
handle_error(err);
}
}
if self.needs_to_grow(len, additional, elem_layout) {
do_reserve_and_handle(self, len, additional, elem_layout);
unsafe {
do_reserve_and_handle(self, len, additional, elem_layout);
}
}
}
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
#[cfg(not(no_global_oom_handling))]
#[inline]
#[track_caller]
fn grow_one(&mut self, elem_layout: Layout) {
if let Err(err) = self.grow_amortized(self.cap.as_inner(), 1, elem_layout) {
unsafe fn grow_one(&mut self, elem_layout: Layout) {
// SAFETY: Precondition passed to caller
if let Err(err) = unsafe { self.grow_amortized(self.cap.as_inner(), 1, elem_layout) } {
handle_error(err);
}
}
fn try_reserve(
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
unsafe fn try_reserve(
&mut self,
len: usize,
additional: usize,
elem_layout: Layout,
) -> Result<(), TryReserveError> {
if self.needs_to_grow(len, additional, elem_layout) {
self.grow_amortized(len, additional, elem_layout)?;
// SAFETY: Precondition passed to caller
unsafe {
self.grow_amortized(len, additional, elem_layout)?;
}
}
unsafe {
// Inform the optimizer that the reservation has succeeded or wasn't needed
@ -585,22 +616,34 @@ impl<A: Allocator> RawVecInner<A> {
Ok(())
}
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
#[cfg(not(no_global_oom_handling))]
#[track_caller]
fn reserve_exact(&mut self, len: usize, additional: usize, elem_layout: Layout) {
if let Err(err) = self.try_reserve_exact(len, additional, elem_layout) {
unsafe fn reserve_exact(&mut self, len: usize, additional: usize, elem_layout: Layout) {
// SAFETY: Precondition passed to caller
if let Err(err) = unsafe { self.try_reserve_exact(len, additional, elem_layout) } {
handle_error(err);
}
}
fn try_reserve_exact(
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
unsafe fn try_reserve_exact(
&mut self,
len: usize,
additional: usize,
elem_layout: Layout,
) -> Result<(), TryReserveError> {
if self.needs_to_grow(len, additional, elem_layout) {
self.grow_exact(len, additional, elem_layout)?;
// SAFETY: Precondition passed to caller
unsafe {
self.grow_exact(len, additional, elem_layout)?;
}
}
unsafe {
// Inform the optimizer that the reservation has succeeded or wasn't needed
@ -609,11 +652,16 @@ impl<A: Allocator> RawVecInner<A> {
Ok(())
}
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
/// - `cap` must be less than or equal to `self.capacity(elem_layout.size())`
#[cfg(not(no_global_oom_handling))]
#[inline]
#[track_caller]
fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) {
if let Err(err) = self.shrink(cap, elem_layout) {
unsafe fn shrink_to_fit(&mut self, cap: usize, elem_layout: Layout) {
if let Err(err) = unsafe { self.shrink(cap, elem_layout) } {
handle_error(err);
}
}
@ -632,7 +680,13 @@ impl<A: Allocator> RawVecInner<A> {
self.cap = unsafe { Cap::new_unchecked(cap) };
}
fn grow_amortized(
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
/// - The sum of `len` and `additional` must be greater than or equal to
/// `self.capacity(elem_layout.size())`
unsafe fn grow_amortized(
&mut self,
len: usize,
additional: usize,
@ -657,14 +711,25 @@ impl<A: Allocator> RawVecInner<A> {
let new_layout = layout_array(cap, elem_layout)?;
let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?;
// SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items
// SAFETY:
// - For the `current_memory` call: Precondition passed to caller
// - For the `finish_grow` call: Precondition passed to caller
// + `current_memory` does the right thing
let ptr =
unsafe { finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)? };
// SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items
unsafe { self.set_ptr_and_cap(ptr, cap) };
Ok(())
}
fn grow_exact(
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
/// - The sum of `len` and `additional` must be greater than or equal to
/// `self.capacity(elem_layout.size())`
unsafe fn grow_exact(
&mut self,
len: usize,
additional: usize,
@ -679,7 +744,12 @@ impl<A: Allocator> RawVecInner<A> {
let cap = len.checked_add(additional).ok_or(CapacityOverflow)?;
let new_layout = layout_array(cap, elem_layout)?;
let ptr = finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)?;
// SAFETY:
// - For the `current_memory` call: Precondition passed to caller
// - For the `finish_grow` call: Precondition passed to caller
// + `current_memory` does the right thing
let ptr =
unsafe { finish_grow(new_layout, self.current_memory(elem_layout), &mut self.alloc)? };
// SAFETY: layout_array would have resulted in a capacity overflow if we tried to allocate more than `isize::MAX` items
unsafe {
self.set_ptr_and_cap(ptr, cap);
@ -687,9 +757,14 @@ impl<A: Allocator> RawVecInner<A> {
Ok(())
}
/// # Safety
/// - `elem_layout` must be valid for `self`, i.e. it must be the same `elem_layout` used to
/// initially construct `self`
/// - `elem_layout`'s size must be a multiple of its alignment
/// - `cap` must be less than or equal to `self.capacity(elem_layout.size())`
#[cfg(not(no_global_oom_handling))]
#[inline]
fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> {
unsafe fn shrink(&mut self, cap: usize, elem_layout: Layout) -> Result<(), TryReserveError> {
assert!(cap <= self.capacity(elem_layout.size()), "Tried to shrink to a larger capacity");
// SAFETY: Just checked this isn't trying to grow
unsafe { self.shrink_unchecked(cap, elem_layout) }
@ -711,8 +786,12 @@ impl<A: Allocator> RawVecInner<A> {
cap: usize,
elem_layout: Layout,
) -> Result<(), TryReserveError> {
let (ptr, layout) =
if let Some(mem) = self.current_memory(elem_layout) { mem } else { return Ok(()) };
// SAFETY: Precondition passed to caller
let (ptr, layout) = if let Some(mem) = unsafe { self.current_memory(elem_layout) } {
mem
} else {
return Ok(());
};
// If shrinking to 0, deallocate the buffer. We don't reach this point
// for the T::IS_ZST case since current_memory() will have returned
@ -748,7 +827,8 @@ impl<A: Allocator> RawVecInner<A> {
/// Ideally this function would take `self` by move, but it cannot because it exists to be
/// called from a `Drop` impl.
unsafe fn deallocate(&mut self, elem_layout: Layout) {
if let Some((ptr, layout)) = self.current_memory(elem_layout) {
// SAFETY: Precondition passed to caller
if let Some((ptr, layout)) = unsafe { self.current_memory(elem_layout) } {
unsafe {
self.alloc.deallocate(ptr, layout);
}
@ -756,10 +836,17 @@ impl<A: Allocator> RawVecInner<A> {
}
}
/// # Safety
/// If `current_memory` matches `Some((ptr, old_layout))`:
/// - `ptr` must denote a block of memory *currently allocated* via `alloc`
/// - `old_layout` must *fit* that block of memory
/// - `new_layout` must have the same alignment as `old_layout`
/// - `new_layout.size()` must be greater than or equal to `old_layout.size()`
/// If `current_memory` is `None`, this function is safe.
// not marked inline(never) since we want optimizers to be able to observe the specifics of this
// function, see tests/codegen-llvm/vec-reserve-extend.rs.
#[cold]
fn finish_grow<A>(
unsafe fn finish_grow<A>(
new_layout: Layout,
current_memory: Option<(NonNull<u8>, Layout)>,
alloc: &mut A,

View File

@ -85,7 +85,7 @@ struct ZST;
fn zst_sanity<T>(v: &RawVec<T>) {
assert_eq!(v.capacity(), usize::MAX);
assert_eq!(v.ptr(), core::ptr::Unique::<T>::dangling().as_ptr());
assert_eq!(v.inner.current_memory(T::LAYOUT), None);
assert_eq!(unsafe { v.inner.current_memory(T::LAYOUT) }, None);
}
#[test]
@ -126,12 +126,12 @@ fn zst() {
assert_eq!(v.try_reserve_exact(101, usize::MAX - 100), cap_err);
zst_sanity(&v);
assert_eq!(v.inner.grow_amortized(100, usize::MAX - 100, ZST::LAYOUT), cap_err);
assert_eq!(v.inner.grow_amortized(101, usize::MAX - 100, ZST::LAYOUT), cap_err);
assert_eq!(unsafe { v.inner.grow_amortized(100, usize::MAX - 100, ZST::LAYOUT) }, cap_err);
assert_eq!(unsafe { v.inner.grow_amortized(101, usize::MAX - 100, ZST::LAYOUT) }, cap_err);
zst_sanity(&v);
assert_eq!(v.inner.grow_exact(100, usize::MAX - 100, ZST::LAYOUT), cap_err);
assert_eq!(v.inner.grow_exact(101, usize::MAX - 100, ZST::LAYOUT), cap_err);
assert_eq!(unsafe { v.inner.grow_exact(100, usize::MAX - 100, ZST::LAYOUT) }, cap_err);
assert_eq!(unsafe { v.inner.grow_exact(101, usize::MAX - 100, ZST::LAYOUT) }, cap_err);
zst_sanity(&v);
}

View File

@ -1312,28 +1312,28 @@ pub fn log2f128(x: f128) -> f128;
/// [`f16::mul_add`](../../std/primitive.f16.html#method.mul_add)
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmaf16(a: f16, b: f16, c: f16) -> f16;
pub const fn fmaf16(a: f16, b: f16, c: f16) -> f16;
/// Returns `a * b + c` for `f32` values.
///
/// The stabilized version of this intrinsic is
/// [`f32::mul_add`](../../std/primitive.f32.html#method.mul_add)
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmaf32(a: f32, b: f32, c: f32) -> f32;
pub const fn fmaf32(a: f32, b: f32, c: f32) -> f32;
/// Returns `a * b + c` for `f64` values.
///
/// The stabilized version of this intrinsic is
/// [`f64::mul_add`](../../std/primitive.f64.html#method.mul_add)
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmaf64(a: f64, b: f64, c: f64) -> f64;
pub const fn fmaf64(a: f64, b: f64, c: f64) -> f64;
/// Returns `a * b + c` for `f128` values.
///
/// The stabilized version of this intrinsic is
/// [`f128::mul_add`](../../std/primitive.f128.html#method.mul_add)
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmaf128(a: f128, b: f128, c: f128) -> f128;
pub const fn fmaf128(a: f128, b: f128, c: f128) -> f128;
/// Returns `a * b + c` for `f16` values, non-deterministically executing
/// either a fused multiply-add or two operations with rounding of the
@ -1347,7 +1347,7 @@ pub fn fmaf128(a: f128, b: f128, c: f128) -> f128;
/// example.
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmuladdf16(a: f16, b: f16, c: f16) -> f16;
pub const fn fmuladdf16(a: f16, b: f16, c: f16) -> f16;
/// Returns `a * b + c` for `f32` values, non-deterministically executing
/// either a fused multiply-add or two operations with rounding of the
/// intermediate result.
@ -1360,7 +1360,7 @@ pub fn fmuladdf16(a: f16, b: f16, c: f16) -> f16;
/// example.
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmuladdf32(a: f32, b: f32, c: f32) -> f32;
pub const fn fmuladdf32(a: f32, b: f32, c: f32) -> f32;
/// Returns `a * b + c` for `f64` values, non-deterministically executing
/// either a fused multiply-add or two operations with rounding of the
/// intermediate result.
@ -1373,7 +1373,7 @@ pub fn fmuladdf32(a: f32, b: f32, c: f32) -> f32;
/// example.
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmuladdf64(a: f64, b: f64, c: f64) -> f64;
pub const fn fmuladdf64(a: f64, b: f64, c: f64) -> f64;
/// Returns `a * b + c` for `f128` values, non-deterministically executing
/// either a fused multiply-add or two operations with rounding of the
/// intermediate result.
@ -1386,7 +1386,7 @@ pub fn fmuladdf64(a: f64, b: f64, c: f64) -> f64;
/// example.
#[rustc_intrinsic]
#[rustc_nounwind]
pub fn fmuladdf128(a: f128, b: f128, c: f128) -> f128;
pub const fn fmuladdf128(a: f128, b: f128, c: f128) -> f128;
/// Returns the largest integer less than or equal to an `f16`.
///

View File

@ -1659,7 +1659,8 @@ impl f128 {
#[doc(alias = "fmaf128", alias = "fusedMultiplyAdd")]
#[unstable(feature = "f128", issue = "116909")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn mul_add(self, a: f128, b: f128) -> f128 {
#[rustc_const_unstable(feature = "const_mul_add", issue = "146724")]
pub const fn mul_add(self, a: f128, b: f128) -> f128 {
intrinsics::fmaf128(self, a, b)
}

View File

@ -1634,7 +1634,8 @@ impl f16 {
#[unstable(feature = "f16", issue = "116909")]
#[doc(alias = "fmaf16", alias = "fusedMultiplyAdd")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn mul_add(self, a: f16, b: f16) -> f16 {
#[rustc_const_unstable(feature = "const_mul_add", issue = "146724")]
pub const fn mul_add(self, a: f16, b: f16) -> f16 {
intrinsics::fmaf16(self, a, b)
}

View File

@ -1798,7 +1798,8 @@ pub mod math {
#[doc(alias = "fmaf", alias = "fusedMultiplyAdd")]
#[must_use = "method returns a new number and does not mutate the original value"]
#[unstable(feature = "core_float_math", issue = "137578")]
pub fn mul_add(x: f32, y: f32, z: f32) -> f32 {
#[rustc_const_unstable(feature = "const_mul_add", issue = "146724")]
pub const fn mul_add(x: f32, y: f32, z: f32) -> f32 {
intrinsics::fmaf32(x, y, z)
}

View File

@ -1796,7 +1796,8 @@ pub mod math {
#[doc(alias = "fma", alias = "fusedMultiplyAdd")]
#[unstable(feature = "core_float_math", issue = "137578")]
#[must_use = "method returns a new number and does not mutate the original value"]
pub fn mul_add(x: f64, a: f64, b: f64) -> f64 {
#[rustc_const_unstable(feature = "const_mul_add", issue = "146724")]
pub const fn mul_add(x: f64, a: f64, b: f64) -> f64 {
intrinsics::fmaf64(x, a, b)
}

View File

@ -20,24 +20,6 @@ const TOL_PRECISE: f128 = 1e-28;
// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support
// the intrinsics.
#[test]
#[cfg(not(miri))]
#[cfg(target_has_reliable_f128_math)]
fn test_mul_add() {
let nan: f128 = f128::NAN;
let inf: f128 = f128::INFINITY;
let neg_inf: f128 = f128::NEG_INFINITY;
assert_biteq!(12.3f128.mul_add(4.5, 6.7), 62.0500000000000000000000000000000037);
assert_biteq!((-12.3f128).mul_add(-4.5, -6.7), 48.6500000000000000000000000000000049);
assert_biteq!(0.0f128.mul_add(8.9, 1.2), 1.2);
assert_biteq!(3.4f128.mul_add(-0.0, 5.6), 5.6);
assert!(nan.mul_add(7.8, 9.0).is_nan());
assert_biteq!(inf.mul_add(7.8, 9.0), inf);
assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf);
assert_biteq!(8.9f128.mul_add(inf, 3.2), inf);
assert_biteq!((-3.2f128).mul_add(2.4, neg_inf), neg_inf);
}
#[test]
#[cfg(any(miri, target_has_reliable_f128_math))]
fn test_max_recip() {

View File

@ -22,24 +22,6 @@ const TOL_P4: f16 = 10.0;
// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support
// the intrinsics.
#[test]
#[cfg(not(miri))]
#[cfg(target_has_reliable_f16_math)]
fn test_mul_add() {
let nan: f16 = f16::NAN;
let inf: f16 = f16::INFINITY;
let neg_inf: f16 = f16::NEG_INFINITY;
assert_biteq!(12.3f16.mul_add(4.5, 6.7), 62.031);
assert_biteq!((-12.3f16).mul_add(-4.5, -6.7), 48.625);
assert_biteq!(0.0f16.mul_add(8.9, 1.2), 1.2);
assert_biteq!(3.4f16.mul_add(-0.0, 5.6), 5.6);
assert!(nan.mul_add(7.8, 9.0).is_nan());
assert_biteq!(inf.mul_add(7.8, 9.0), inf);
assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf);
assert_biteq!(8.9f16.mul_add(inf, 3.2), inf);
assert_biteq!((-3.2f16).mul_add(2.4, neg_inf), neg_inf);
}
#[test]
#[cfg(any(miri, target_has_reliable_f16_math))]
fn test_max_recip() {

View File

@ -1,21 +0,0 @@
use core::f32;
use super::assert_biteq;
// FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/
#[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)]
#[test]
fn test_mul_add() {
let nan: f32 = f32::NAN;
let inf: f32 = f32::INFINITY;
let neg_inf: f32 = f32::NEG_INFINITY;
assert_biteq!(f32::math::mul_add(12.3f32, 4.5, 6.7), 62.05);
assert_biteq!(f32::math::mul_add(-12.3f32, -4.5, -6.7), 48.65);
assert_biteq!(f32::math::mul_add(0.0f32, 8.9, 1.2), 1.2);
assert_biteq!(f32::math::mul_add(3.4f32, -0.0, 5.6), 5.6);
assert!(f32::math::mul_add(nan, 7.8, 9.0).is_nan());
assert_biteq!(f32::math::mul_add(inf, 7.8, 9.0), inf);
assert_biteq!(f32::math::mul_add(neg_inf, 7.8, 9.0), neg_inf);
assert_biteq!(f32::math::mul_add(8.9f32, inf, 3.2), inf);
assert_biteq!(f32::math::mul_add(-3.2f32, 2.4, neg_inf), neg_inf);
}

View File

@ -1,21 +0,0 @@
use core::f64;
use super::assert_biteq;
// FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/
#[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)]
#[test]
fn test_mul_add() {
let nan: f64 = f64::NAN;
let inf: f64 = f64::INFINITY;
let neg_inf: f64 = f64::NEG_INFINITY;
assert_biteq!(12.3f64.mul_add(4.5, 6.7), 62.050000000000004);
assert_biteq!((-12.3f64).mul_add(-4.5, -6.7), 48.650000000000006);
assert_biteq!(0.0f64.mul_add(8.9, 1.2), 1.2);
assert_biteq!(3.4f64.mul_add(-0.0, 5.6), 5.6);
assert!(nan.mul_add(7.8, 9.0).is_nan());
assert_biteq!(inf.mul_add(7.8, 9.0), inf);
assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf);
assert_biteq!(8.9f64.mul_add(inf, 3.2), inf);
assert_biteq!((-3.2f64).mul_add(2.4, neg_inf), neg_inf);
}

View File

@ -34,6 +34,10 @@ trait TestableFloat: Sized {
const RAW_12_DOT_5: Self;
const RAW_1337: Self;
const RAW_MINUS_14_DOT_25: Self;
/// The result of 12.3.mul_add(4.5, 6.7)
const MUL_ADD_RESULT: Self;
/// The result of (-12.3).mul_add(-4.5, -6.7)
const NEG_MUL_ADD_RESULT: Self;
}
impl TestableFloat for f16 {
@ -58,6 +62,8 @@ impl TestableFloat for f16 {
const RAW_12_DOT_5: Self = Self::from_bits(0x4a40);
const RAW_1337: Self = Self::from_bits(0x6539);
const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xcb20);
const MUL_ADD_RESULT: Self = 62.031;
const NEG_MUL_ADD_RESULT: Self = 48.625;
}
impl TestableFloat for f32 {
@ -84,6 +90,8 @@ impl TestableFloat for f32 {
const RAW_12_DOT_5: Self = Self::from_bits(0x41480000);
const RAW_1337: Self = Self::from_bits(0x44a72000);
const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc1640000);
const MUL_ADD_RESULT: Self = 62.05;
const NEG_MUL_ADD_RESULT: Self = 48.65;
}
impl TestableFloat for f64 {
@ -106,6 +114,8 @@ impl TestableFloat for f64 {
const RAW_12_DOT_5: Self = Self::from_bits(0x4029000000000000);
const RAW_1337: Self = Self::from_bits(0x4094e40000000000);
const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc02c800000000000);
const MUL_ADD_RESULT: Self = 62.050000000000004;
const NEG_MUL_ADD_RESULT: Self = 48.650000000000006;
}
impl TestableFloat for f128 {
@ -128,6 +138,8 @@ impl TestableFloat for f128 {
const RAW_12_DOT_5: Self = Self::from_bits(0x40029000000000000000000000000000);
const RAW_1337: Self = Self::from_bits(0x40094e40000000000000000000000000);
const RAW_MINUS_14_DOT_25: Self = Self::from_bits(0xc002c800000000000000000000000000);
const MUL_ADD_RESULT: Self = 62.0500000000000000000000000000000037;
const NEG_MUL_ADD_RESULT: Self = 48.6500000000000000000000000000000049;
}
/// Determine the tolerance for values of the argument type.
@ -359,8 +371,6 @@ macro_rules! float_test {
mod f128;
mod f16;
mod f32;
mod f64;
float_test! {
name: num,
@ -1180,15 +1190,12 @@ float_test! {
}
}
// FIXME(f16_f128,miri): many of these have to be disabled since miri does not yet support
// the intrinsics.
float_test! {
name: sqrt_domain,
attrs: {
const: #[cfg(false)],
f16: #[cfg(all(not(miri), target_has_reliable_f16_math))],
f128: #[cfg(all(not(miri), target_has_reliable_f128_math))],
f16: #[cfg(any(miri, target_has_reliable_f16_math))],
f128: #[cfg(any(miri, target_has_reliable_f128_math))],
},
test<Float> {
assert!(Float::NAN.sqrt().is_nan());
@ -1246,8 +1253,8 @@ float_test! {
float_test! {
name: total_cmp,
attrs: {
f16: #[cfg(all(not(miri), target_has_reliable_f16_math))],
f128: #[cfg(all(not(miri), target_has_reliable_f128_math))],
f16: #[cfg(any(miri, target_has_reliable_f16_math))],
f128: #[cfg(any(miri, target_has_reliable_f128_math))],
},
test<Float> {
use core::cmp::Ordering;
@ -1355,8 +1362,8 @@ float_test! {
name: total_cmp_s_nan,
attrs: {
const: #[cfg(false)],
f16: #[cfg(false)],
f128: #[cfg(all(not(miri), target_has_reliable_f128_math))],
f16: #[cfg(miri)],
f128: #[cfg(any(miri, target_has_reliable_f128_math))],
},
test<Float> {
use core::cmp::Ordering;
@ -1432,6 +1439,7 @@ float_test! {
name: powi,
attrs: {
const: #[cfg(false)],
// FIXME(f16_f128): `powi` does not work in Miri for these types
f16: #[cfg(all(not(miri), target_has_reliable_f16_math))],
f128: #[cfg(all(not(miri), target_has_reliable_f128_math))],
},
@ -1452,8 +1460,8 @@ float_test! {
float_test! {
name: to_degrees,
attrs: {
f16: #[cfg(target_has_reliable_f16)],
f128: #[cfg(target_has_reliable_f128)],
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let pi: Float = Float::PI;
@ -1473,8 +1481,8 @@ float_test! {
float_test! {
name: to_radians,
attrs: {
f16: #[cfg(target_has_reliable_f16)],
f128: #[cfg(target_has_reliable_f128)],
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let pi: Float = Float::PI;
@ -1494,8 +1502,8 @@ float_test! {
float_test! {
name: to_algebraic,
attrs: {
f16: #[cfg(target_has_reliable_f16)],
f128: #[cfg(target_has_reliable_f128)],
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let a: Float = 123.0;
@ -1518,8 +1526,8 @@ float_test! {
float_test! {
name: to_bits_conv,
attrs: {
f16: #[cfg(target_has_reliable_f16)],
f128: #[cfg(target_has_reliable_f128)],
f16: #[cfg(any(miri, target_has_reliable_f16))],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
assert_biteq!(flt(1.0), Float::RAW_1);
@ -1541,3 +1549,28 @@ float_test! {
assert_biteq!(Float::from_bits(masked_nan2), Float::from_bits(masked_nan2));
}
}
float_test! {
name: mul_add,
attrs: {
f16: #[cfg(any(miri, target_has_reliable_f16))],
// FIXME(#140515): mingw has an incorrect fma https://sourceforge.net/p/mingw-w64/bugs/848/
f32: #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)],
f64: #[cfg_attr(all(target_os = "windows", target_env = "gnu", not(target_abi = "llvm")), ignore)],
f128: #[cfg(any(miri, target_has_reliable_f128))],
},
test<Float> {
let nan: Float = Float::NAN;
let inf: Float = Float::INFINITY;
let neg_inf: Float = Float::NEG_INFINITY;
assert_biteq!(flt(12.3).mul_add(4.5, 6.7), Float::MUL_ADD_RESULT);
assert_biteq!((flt(-12.3)).mul_add(-4.5, -6.7), Float::NEG_MUL_ADD_RESULT);
assert_biteq!(flt(0.0).mul_add(8.9, 1.2), 1.2);
assert_biteq!(flt(3.4).mul_add(-0.0, 5.6), 5.6);
assert!(nan.mul_add(7.8, 9.0).is_nan());
assert_biteq!(inf.mul_add(7.8, 9.0), inf);
assert_biteq!(neg_inf.mul_add(7.8, 9.0), neg_inf);
assert_biteq!(flt(8.9).mul_add(inf, 3.2), inf);
assert_biteq!((flt(-3.2)).mul_add(2.4, neg_inf), neg_inf);
}
}

View File

@ -20,6 +20,7 @@
#![feature(const_convert)]
#![feature(const_destruct)]
#![feature(const_eval_select)]
#![feature(const_mul_add)]
#![feature(const_ops)]
#![feature(const_option_ops)]
#![feature(const_ref_cell)]

View File

@ -62,7 +62,7 @@ path = "../windows_targets"
rand = { version = "0.9.0", default-features = false, features = ["alloc"] }
rand_xorshift = "0.4.0"
[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
[target.'cfg(any(all(target_family = "wasm", target_os = "unknown"), target_os = "xous", target_os = "vexos", all(target_vendor = "fortanix", target_env = "sgx")))'.dependencies]
dlmalloc = { version = "0.2.10", features = ['rustc-dep-of-std'] }
[target.x86_64-fortanix-unknown-sgx.dependencies]
@ -89,6 +89,11 @@ wasip2 = { version = '0.14.4', features = [
r-efi = { version = "5.2.0", features = ['rustc-dep-of-std'] }
r-efi-alloc = { version = "2.0.0", features = ['rustc-dep-of-std'] }
[target.'cfg(target_os = "vexos")'.dependencies]
vex-sdk = { version = "0.27.0", features = [
'rustc-dep-of-std',
], default-features = false }
[features]
backtrace = [
'addr2line/rustc-dep-of-std',

View File

@ -52,6 +52,7 @@ fn main() {
|| target_os == "rtems"
|| target_os == "nuttx"
|| target_os == "cygwin"
|| target_os == "vexos"
// See src/bootstrap/src/core/build_steps/synthetic_targets.rs
|| env::var("RUSTC_BOOTSTRAP_SYNTHETIC_TARGET").is_ok()

View File

@ -1098,6 +1098,7 @@ pub mod consts {
/// * `"redox"`
/// * `"solaris"`
/// * `"solid_asp3`
/// * `"vexos"`
/// * `"vita"`
/// * `"vxworks"`
/// * `"xous"`
@ -1148,6 +1149,7 @@ pub mod consts {
///
/// <details><summary>Full list of possible values</summary>
///
/// * `"bin"`
/// * `"exe"`
/// * `"efi"`
/// * `"js"`

View File

@ -332,6 +332,7 @@
#![feature(char_internals)]
#![feature(clone_to_uninit)]
#![feature(const_convert)]
#![feature(const_mul_add)]
#![feature(core_intrinsics)]
#![feature(core_io_borrowed_buf)]
#![feature(drop_guard)]

View File

@ -28,6 +28,8 @@ use crate::{io, iter, option, slice, vec};
/// [`SocketAddr`] as expected by its [`FromStr`] implementation or a string like
/// `<host_name>:<port>` pair where `<port>` is a [`u16`] value.
///
/// * <code>&[[SocketAddr]]</code>: all [`SocketAddr`] values in the slice will be used.
///
/// This trait allows constructing network objects like [`TcpStream`] or
/// [`UdpSocket`] easily with values of various types for the bind/connection
/// address. It is needed because sometimes one type is more appropriate than

View File

@ -217,7 +217,8 @@ impl f32 {
#[must_use = "method returns a new number and does not mutate the original value"]
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn mul_add(self, a: f32, b: f32) -> f32 {
#[rustc_const_unstable(feature = "const_mul_add", issue = "146724")]
pub const fn mul_add(self, a: f32, b: f32) -> f32 {
core::f32::math::mul_add(self, a, b)
}

View File

@ -217,7 +217,8 @@ impl f64 {
#[must_use = "method returns a new number and does not mutate the original value"]
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn mul_add(self, a: f64, b: f64) -> f64 {
#[rustc_const_unstable(feature = "const_mul_add", issue = "146724")]
pub const fn mul_add(self, a: f64, b: f64) -> f64 {
core::f64::math::mul_add(self, a, b)
}

View File

@ -92,6 +92,9 @@ cfg_select! {
target_os = "uefi" => {
mod uefi;
}
target_os = "vexos" => {
mod vexos;
}
target_family = "wasm" => {
mod wasm;
}

View File

@ -0,0 +1,96 @@
// FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
#![allow(static_mut_refs)]
use crate::alloc::{GlobalAlloc, Layout, System};
use crate::ptr;
use crate::sync::atomic::{AtomicBool, Ordering};
// Symbols for heap section boundaries defined in the target's linkerscript
unsafe extern "C" {
static mut __heap_start: u8;
static mut __heap_end: u8;
}
static mut DLMALLOC: dlmalloc::Dlmalloc<Vexos> = dlmalloc::Dlmalloc::new_with_allocator(Vexos);
struct Vexos;
unsafe impl dlmalloc::Allocator for Vexos {
/// Allocs system resources
fn alloc(&self, _size: usize) -> (*mut u8, usize, u32) {
static INIT: AtomicBool = AtomicBool::new(false);
if !INIT.swap(true, Ordering::Relaxed) {
// This target has no growable heap, as user memory has a fixed
// size/location and VEXos does not manage allocation for us.
unsafe {
(
(&raw mut __heap_start).cast::<u8>(),
(&raw const __heap_end).offset_from_unsigned(&raw const __heap_start),
0,
)
}
} else {
(ptr::null_mut(), 0, 0)
}
}
fn remap(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize, _can_move: bool) -> *mut u8 {
ptr::null_mut()
}
fn free_part(&self, _ptr: *mut u8, _oldsize: usize, _newsize: usize) -> bool {
false
}
fn free(&self, _ptr: *mut u8, _size: usize) -> bool {
return false;
}
fn can_release_part(&self, _flags: u32) -> bool {
false
}
fn allocates_zeros(&self) -> bool {
false
}
fn page_size(&self) -> usize {
0x1000
}
}
#[stable(feature = "alloc_system_type", since = "1.28.0")]
unsafe impl GlobalAlloc for System {
#[inline]
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which
// guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used.
// Calling malloc() is safe because preconditions on this function match the trait method preconditions.
unsafe { DLMALLOC.malloc(layout.size(), layout.align()) }
}
#[inline]
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
// SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which
// guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used.
// Calling calloc() is safe because preconditions on this function match the trait method preconditions.
unsafe { DLMALLOC.calloc(layout.size(), layout.align()) }
}
#[inline]
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
// SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which
// guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used.
// Calling free() is safe because preconditions on this function match the trait method preconditions.
unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) }
}
#[inline]
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
// SAFETY: DLMALLOC access is guaranteed to be safe because we are a single-threaded target, which
// guarantees unique and non-reentrant access to the allocator. As such, no allocator lock is used.
// Calling realloc() is safe because preconditions on this function match the trait method preconditions.
unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) }
}
}

View File

@ -323,6 +323,17 @@ pub mod os {
pub const EXE_EXTENSION: &str = "efi";
}
#[cfg(target_os = "vexos")]
pub mod os {
pub const FAMILY: &str = "";
pub const OS: &str = "vexos";
pub const DLL_PREFIX: &str = "";
pub const DLL_SUFFIX: &str = "";
pub const DLL_EXTENSION: &str = "";
pub const EXE_SUFFIX: &str = ".bin";
pub const EXE_EXTENSION: &str = "bin";
}
#[cfg(target_os = "visionos")]
pub mod os {
pub const FAMILY: &str = "unix";

View File

@ -35,6 +35,10 @@ cfg_select! {
mod uefi;
use uefi as imp;
}
target_os = "vexos" => {
mod vexos;
use vexos as imp;
}
target_os = "wasi" => {
mod wasi;
use wasi as imp;

View File

@ -0,0 +1,615 @@
use crate::ffi::{OsString, c_char};
use crate::fmt;
use crate::fs::TryLockError;
use crate::hash::Hash;
use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
use crate::path::{Path, PathBuf};
use crate::sys::common::small_c_string::run_path_with_cstr;
use crate::sys::time::SystemTime;
use crate::sys::{unsupported, unsupported_err};
#[expect(dead_code)]
#[path = "unsupported.rs"]
mod unsupported_fs;
pub use unsupported_fs::{
DirBuilder, FileTimes, canonicalize, link, readlink, remove_dir_all, rename, rmdir, symlink,
unlink,
};
/// VEXos file descriptor.
///
/// This stores an opaque pointer to a [FatFs file object structure] managed by VEXos
/// representing an open file on disk.
///
/// [FatFs file object structure]: https://github.com/Xilinx/embeddedsw/blob/master/lib/sw_services/xilffs/src/include/ff.h?rgh-link-date=2025-09-23T20%3A03%3A43Z#L215
///
/// # Safety
///
/// Since this platform uses a pointer to to an internal filesystem structure with a lifetime
/// associated with it (rather than a UNIX-style file descriptor table), care must be taken to
/// ensure that the pointer held by `FileDesc` is valid for as long as it exists.
#[derive(Debug)]
struct FileDesc(*mut vex_sdk::FIL);
// SAFETY: VEXos's FDs can be used on a thread other than the one they were created on.
unsafe impl Send for FileDesc {}
// SAFETY: We assume an environment without threads (i.e. no RTOS).
// (If there were threads, it is possible that a mutex would be required.)
unsafe impl Sync for FileDesc {}
pub struct File {
fd: FileDesc,
}
#[derive(Clone)]
pub enum FileAttr {
Dir,
File { size: u64 },
}
pub struct ReadDir(!);
pub struct DirEntry {
path: PathBuf,
}
#[derive(Clone, Debug)]
pub struct OpenOptions {
read: bool,
write: bool,
append: bool,
truncate: bool,
create: bool,
create_new: bool,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct FilePermissions {}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct FileType {
is_dir: bool,
}
impl FileAttr {
pub fn size(&self) -> u64 {
match self {
Self::File { size } => *size,
Self::Dir => 0,
}
}
pub fn perm(&self) -> FilePermissions {
FilePermissions {}
}
pub fn file_type(&self) -> FileType {
FileType { is_dir: matches!(self, FileAttr::Dir) }
}
pub fn modified(&self) -> io::Result<SystemTime> {
unsupported()
}
pub fn accessed(&self) -> io::Result<SystemTime> {
unsupported()
}
pub fn created(&self) -> io::Result<SystemTime> {
unsupported()
}
}
impl FilePermissions {
pub fn readonly(&self) -> bool {
false
}
pub fn set_readonly(&mut self, _readonly: bool) {
panic!("Perimissions do not exist")
}
}
impl FileType {
pub fn is_dir(&self) -> bool {
self.is_dir
}
pub fn is_file(&self) -> bool {
!self.is_dir
}
pub fn is_symlink(&self) -> bool {
// No symlinks in VEXos - entries are either files or directories.
false
}
}
impl fmt::Debug for ReadDir {
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0
}
}
impl Iterator for ReadDir {
type Item = io::Result<DirEntry>;
fn next(&mut self) -> Option<io::Result<DirEntry>> {
self.0
}
}
impl DirEntry {
pub fn path(&self) -> PathBuf {
self.path.clone()
}
pub fn file_name(&self) -> OsString {
self.path.file_name().unwrap_or_default().into()
}
pub fn metadata(&self) -> io::Result<FileAttr> {
stat(&self.path)
}
pub fn file_type(&self) -> io::Result<FileType> {
Ok(self.metadata()?.file_type())
}
}
impl OpenOptions {
pub fn new() -> OpenOptions {
OpenOptions {
read: false,
write: false,
append: false,
truncate: false,
create: false,
create_new: false,
}
}
pub fn read(&mut self, read: bool) {
self.read = read;
}
pub fn write(&mut self, write: bool) {
self.write = write;
}
pub fn append(&mut self, append: bool) {
self.append = append;
}
pub fn truncate(&mut self, truncate: bool) {
self.truncate = truncate;
}
pub fn create(&mut self, create: bool) {
self.create = create;
}
pub fn create_new(&mut self, create_new: bool) {
self.create_new = create_new;
}
}
impl File {
pub fn open(path: &Path, opts: &OpenOptions) -> io::Result<File> {
run_path_with_cstr(path, &|path| {
// Enforce the invariants of `create_new`/`create`.
//
// Since VEXos doesn't have anything akin to POSIX's `oflags`, we need to enforce
// the requirements that `create_new` can't have an existing file and `!create`
// doesn't create a file ourselves.
if !opts.read && (opts.write || opts.append) && (opts.create_new || !opts.create) {
let status = unsafe { vex_sdk::vexFileStatus(path.as_ptr()) };
if opts.create_new && status != 0 {
return Err(io::const_error!(io::ErrorKind::AlreadyExists, "file exists",));
} else if !opts.create && status == 0 {
return Err(io::const_error!(
io::ErrorKind::NotFound,
"no such file or directory",
));
}
}
let file = match opts {
// read + write - unsupported
OpenOptions { read: true, write: true, .. } => {
return Err(io::const_error!(
io::ErrorKind::InvalidInput,
"opening files with read and write access is unsupported on this target",
));
}
// read
OpenOptions {
read: true,
write: false,
append: _,
truncate: false,
create: false,
create_new: false,
} => unsafe { vex_sdk::vexFileOpen(path.as_ptr(), c"".as_ptr()) },
// append
OpenOptions {
read: false,
write: _,
append: true,
truncate: false,
create: _,
create_new: _,
} => unsafe { vex_sdk::vexFileOpenWrite(path.as_ptr()) },
// write
OpenOptions {
read: false,
write: true,
append: false,
truncate,
create: _,
create_new: _,
} => unsafe {
if *truncate {
vex_sdk::vexFileOpenCreate(path.as_ptr())
} else {
// Open in append, but jump to the start of the file.
let fd = vex_sdk::vexFileOpenWrite(path.as_ptr());
vex_sdk::vexFileSeek(fd, 0, 0);
fd
}
},
_ => {
return Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid argument"));
}
};
if file.is_null() {
Err(io::const_error!(io::ErrorKind::NotFound, "could not open file"))
} else {
Ok(Self { fd: FileDesc(file) })
}
})
}
pub fn file_attr(&self) -> io::Result<FileAttr> {
// `vexFileSize` returns -1 upon error, so u64::try_from will fail on error.
if let Ok(size) = u64::try_from(unsafe {
// SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime.
vex_sdk::vexFileSize(self.fd.0)
}) {
Ok(FileAttr::File { size })
} else {
Err(io::const_error!(io::ErrorKind::InvalidData, "failed to get file size"))
}
}
pub fn fsync(&self) -> io::Result<()> {
self.flush()
}
pub fn datasync(&self) -> io::Result<()> {
self.flush()
}
pub fn lock(&self) -> io::Result<()> {
unsupported()
}
pub fn lock_shared(&self) -> io::Result<()> {
unsupported()
}
pub fn try_lock(&self) -> Result<(), TryLockError> {
Err(TryLockError::Error(unsupported_err()))
}
pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
Err(TryLockError::Error(unsupported_err()))
}
pub fn unlock(&self) -> io::Result<()> {
unsupported()
}
pub fn truncate(&self, _size: u64) -> io::Result<()> {
unsupported()
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let len = buf.len() as u32;
let buf_ptr = buf.as_mut_ptr();
let read = unsafe {
// SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime.
vex_sdk::vexFileRead(buf_ptr.cast::<c_char>(), 1, len, self.fd.0)
};
if read < 0 {
Err(io::const_error!(io::ErrorKind::Other, "could not read from file"))
} else {
Ok(read as usize)
}
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
crate::io::default_read_vectored(|b| self.read(b), bufs)
}
#[inline]
pub fn is_read_vectored(&self) -> bool {
false
}
pub fn read_buf(&self, cursor: BorrowedCursor<'_>) -> io::Result<()> {
crate::io::default_read_buf(|b| self.read(b), cursor)
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
let len = buf.len() as u32;
let buf_ptr = buf.as_ptr();
let written = unsafe {
// SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime.
vex_sdk::vexFileWrite(buf_ptr.cast_mut().cast::<c_char>(), 1, len, self.fd.0)
};
if written < 0 {
Err(io::const_error!(io::ErrorKind::Other, "could not write to file"))
} else {
Ok(written as usize)
}
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
crate::io::default_write_vectored(|b| self.write(b), bufs)
}
#[inline]
pub fn is_write_vectored(&self) -> bool {
false
}
pub fn flush(&self) -> io::Result<()> {
unsafe {
// SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime.
vex_sdk::vexFileSync(self.fd.0);
}
Ok(())
}
pub fn tell(&self) -> io::Result<u64> {
// SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime.
let position = unsafe { vex_sdk::vexFileTell(self.fd.0) };
position.try_into().map_err(|_| {
io::const_error!(io::ErrorKind::InvalidData, "failed to get current location in file")
})
}
pub fn size(&self) -> Option<io::Result<u64>> {
None
}
pub fn seek(&self, pos: SeekFrom) -> io::Result<u64> {
const SEEK_SET: i32 = 0;
const SEEK_CUR: i32 = 1;
const SEEK_END: i32 = 2;
fn try_convert_offset<T: TryInto<u32>>(offset: T) -> io::Result<u32> {
offset.try_into().map_err(|_| {
io::const_error!(
io::ErrorKind::InvalidInput,
"cannot seek to an offset too large to fit in a 32 bit integer",
)
})
}
// SAFETY: `self.fd` contains a valid pointer to `FIL` for this struct's lifetime.
match pos {
SeekFrom::Start(offset) => unsafe {
map_fresult(vex_sdk::vexFileSeek(self.fd.0, try_convert_offset(offset)?, SEEK_SET))?
},
SeekFrom::End(offset) => unsafe {
if offset >= 0 {
map_fresult(vex_sdk::vexFileSeek(
self.fd.0,
try_convert_offset(offset)?,
SEEK_END,
))?
} else {
// `vexFileSeek` does not support seeking with negative offset, meaning
// we have to calculate the offset from the end of the file ourselves.
// Seek to the end of the file to get the end position in the open buffer.
map_fresult(vex_sdk::vexFileSeek(self.fd.0, 0, SEEK_END))?;
let end_position = self.tell()?;
map_fresult(vex_sdk::vexFileSeek(
self.fd.0,
// NOTE: Files internally use a 32-bit representation for stream
// position, so `end_position as i64` should never overflow.
try_convert_offset(end_position as i64 + offset)?,
SEEK_SET,
))?
}
},
SeekFrom::Current(offset) => unsafe {
if offset >= 0 {
map_fresult(vex_sdk::vexFileSeek(
self.fd.0,
try_convert_offset(offset)?,
SEEK_CUR,
))?
} else {
// `vexFileSeek` does not support seeking with negative offset, meaning
// we have to calculate the offset from the stream position ourselves.
map_fresult(vex_sdk::vexFileSeek(
self.fd.0,
try_convert_offset((self.tell()? as i64) + offset)?,
SEEK_SET,
))?
}
},
}
Ok(self.tell()?)
}
pub fn duplicate(&self) -> io::Result<File> {
unsupported()
}
pub fn set_permissions(&self, _perm: FilePermissions) -> io::Result<()> {
unsupported()
}
pub fn set_times(&self, _times: FileTimes) -> io::Result<()> {
unsupported()
}
}
impl fmt::Debug for File {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("File").field("fd", &self.fd.0).finish()
}
}
impl Drop for File {
fn drop(&mut self) {
unsafe { vex_sdk::vexFileClose(self.fd.0) };
}
}
pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
// While there *is* a userspace function for reading file directories,
// the necessary implementation cannot currently be done cleanly, as
// VEXos does not expose directory length to user programs.
//
// This means that we would need to create a large fixed-length buffer
// and hope that the folder's contents didn't exceed that buffer's length,
// which obviously isn't behavior we want to rely on in the standard library.
unsupported()
}
pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
unsupported()
}
pub fn exists(path: &Path) -> io::Result<bool> {
run_path_with_cstr(path, &|path| Ok(unsafe { vex_sdk::vexFileStatus(path.as_ptr()) } != 0))
}
pub fn stat(p: &Path) -> io::Result<FileAttr> {
// `vexFileStatus` returns 3 if the given path is a directory, 1 if the path is a
// file, or 0 if no such path exists.
const FILE_STATUS_DIR: u32 = 3;
run_path_with_cstr(p, &|c_path| {
let file_type = unsafe { vex_sdk::vexFileStatus(c_path.as_ptr()) };
// We can't get the size if its a directory because we cant open it as a file
if file_type == FILE_STATUS_DIR {
Ok(FileAttr::Dir)
} else {
let mut opts = OpenOptions::new();
opts.read(true);
let file = File::open(p, &opts)?;
file.file_attr()
}
})
}
pub fn lstat(p: &Path) -> io::Result<FileAttr> {
// Symlinks aren't supported in this filesystem
stat(p)
}
// Cannot use `copy` from `common` here, since `File::set_permissions` is unsupported on this target.
pub fn copy(from: &Path, to: &Path) -> io::Result<u64> {
use crate::fs::File;
// NOTE: If `from` is a directory, this call should fail due to vexFileOpen* returning null.
let mut reader = File::open(from)?;
let mut writer = File::create(to)?;
io::copy(&mut reader, &mut writer)
}
fn map_fresult(fresult: vex_sdk::FRESULT) -> io::Result<()> {
// VEX uses a derivative of FatFs (Xilinx's xilffs library) for filesystem operations.
match fresult {
vex_sdk::FRESULT::FR_OK => Ok(()),
vex_sdk::FRESULT::FR_DISK_ERR => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"internal function reported an unrecoverable hard error",
)),
vex_sdk::FRESULT::FR_INT_ERR => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"internal error in filesystem runtime",
)),
vex_sdk::FRESULT::FR_NOT_READY => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"the storage device could not be prepared to work",
)),
vex_sdk::FRESULT::FR_NO_FILE => Err(io::const_error!(
io::ErrorKind::NotFound,
"could not find the file in the directory"
)),
vex_sdk::FRESULT::FR_NO_PATH => Err(io::const_error!(
io::ErrorKind::NotFound,
"a directory in the path name could not be found",
)),
vex_sdk::FRESULT::FR_INVALID_NAME => Err(io::const_error!(
io::ErrorKind::InvalidInput,
"the given string is invalid as a path name",
)),
vex_sdk::FRESULT::FR_DENIED => Err(io::const_error!(
io::ErrorKind::PermissionDenied,
"the required access for this operation was denied",
)),
vex_sdk::FRESULT::FR_EXIST => Err(io::const_error!(
io::ErrorKind::AlreadyExists,
"an object with the same name already exists in the directory",
)),
vex_sdk::FRESULT::FR_INVALID_OBJECT => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"invalid or null file/directory object",
)),
vex_sdk::FRESULT::FR_WRITE_PROTECTED => Err(io::const_error!(
io::ErrorKind::PermissionDenied,
"a write operation was performed on write-protected media",
)),
vex_sdk::FRESULT::FR_INVALID_DRIVE => Err(io::const_error!(
io::ErrorKind::InvalidInput,
"an invalid drive number was specified in the path name",
)),
vex_sdk::FRESULT::FR_NOT_ENABLED => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"work area for the logical drive has not been registered",
)),
vex_sdk::FRESULT::FR_NO_FILESYSTEM => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"valid FAT volume could not be found on the drive",
)),
vex_sdk::FRESULT::FR_MKFS_ABORTED => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"failed to create filesystem volume"
)),
vex_sdk::FRESULT::FR_TIMEOUT => Err(io::const_error!(
io::ErrorKind::TimedOut,
"the function was canceled due to a timeout of thread-safe control",
)),
vex_sdk::FRESULT::FR_LOCKED => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"the operation to the object was rejected by file sharing control",
)),
vex_sdk::FRESULT::FR_NOT_ENOUGH_CORE => {
Err(io::const_error!(io::ErrorKind::OutOfMemory, "not enough memory for the operation"))
}
vex_sdk::FRESULT::FR_TOO_MANY_OPEN_FILES => Err(io::const_error!(
io::ErrorKind::Uncategorized,
"maximum number of open files has been reached",
)),
vex_sdk::FRESULT::FR_INVALID_PARAMETER => {
Err(io::const_error!(io::ErrorKind::InvalidInput, "a given parameter was invalid"))
}
_ => unreachable!(), // C-style enum
}
}

View File

@ -45,6 +45,10 @@ cfg_select! {
mod trusty;
pub use self::trusty::*;
}
target_os = "vexos" => {
mod vexos;
pub use self::vexos::*;
}
all(target_os = "wasi", target_env = "p2") => {
mod wasip2;
pub use self::wasip2::*;

View File

@ -0,0 +1,80 @@
#[path = "../unsupported/os.rs"]
pub mod os;
#[path = "../unsupported/pipe.rs"]
pub mod pipe;
pub mod time;
#[expect(dead_code)]
#[path = "../unsupported/common.rs"]
mod unsupported_common;
pub use unsupported_common::{
decode_error_kind, init, is_interrupted, unsupported, unsupported_err,
};
use crate::arch::global_asm;
use crate::ptr;
use crate::sys::stdio;
use crate::time::{Duration, Instant};
global_asm!(
r#"
.section .boot, "ax"
.global _boot
_boot:
ldr sp, =__stack_top @ Set up the user stack.
b _start @ Jump to the Rust entrypoint.
"#
);
#[cfg(not(test))]
#[unsafe(no_mangle)]
pub unsafe extern "C" fn _start() -> ! {
unsafe extern "C" {
static mut __bss_start: u8;
static mut __bss_end: u8;
fn main() -> i32;
}
// Clear the .bss (uninitialized statics) section by filling it with zeroes.
// This is required, since the compiler assumes it will be zeroed on first access.
ptr::write_bytes(
&raw mut __bss_start,
0,
(&raw mut __bss_end).offset_from_unsigned(&raw mut __bss_start),
);
main();
cleanup();
abort_internal()
}
// SAFETY: must be called only once during runtime cleanup.
// NOTE: this is not guaranteed to run, for example when the program aborts.
pub unsafe fn cleanup() {
let exit_time = Instant::now();
const FLUSH_TIMEOUT: Duration = Duration::from_millis(15);
// Force the serial buffer to flush
while exit_time.elapsed() < FLUSH_TIMEOUT {
vex_sdk::vexTasksRun();
// If the buffer has been fully flushed, exit the loop
if vex_sdk::vexSerialWriteFree(stdio::STDIO_CHANNEL) == (stdio::STDOUT_BUF_SIZE as i32) {
break;
}
}
}
pub fn abort_internal() -> ! {
unsafe {
vex_sdk::vexSystemExitRequest();
loop {
vex_sdk::vexTasksRun();
}
}
}

View File

@ -0,0 +1,28 @@
use crate::time::Duration;
#[expect(dead_code)]
#[path = "../unsupported/time.rs"]
mod unsupported_time;
pub use unsupported_time::{SystemTime, UNIX_EPOCH};
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
pub struct Instant(Duration);
impl Instant {
pub fn now() -> Instant {
let micros = unsafe { vex_sdk::vexSystemHighResTimeGet() };
Self(Duration::from_micros(micros))
}
pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
self.0.checked_sub(other.0)
}
pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant(self.0.checked_add(*other)?))
}
pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
Some(Instant(self.0.checked_sub(*other)?))
}
}

View File

@ -101,6 +101,7 @@ cfg_select! {
any(
all(target_family = "wasm", target_os = "unknown"),
target_os = "xous",
target_os = "vexos",
) => {
// FIXME: finally remove std support for wasm32-unknown-unknown
// FIXME: add random data generation to xous
@ -116,6 +117,7 @@ cfg_select! {
all(target_family = "wasm", target_os = "unknown"),
all(target_os = "wasi", target_env = "p2"),
target_os = "xous",
target_os = "vexos",
)))]
pub fn hashmap_random_keys() -> (u64, u64) {
let mut buf = [0; 16];

View File

@ -29,6 +29,10 @@ cfg_select! {
mod uefi;
pub use uefi::*;
}
target_os = "vexos" => {
mod vexos;
pub use vexos::*;
}
all(target_os = "wasi", target_env = "p1") => {
mod wasip1;
pub use wasip1::*;

View File

@ -0,0 +1,100 @@
use crate::io;
pub struct Stdin;
pub struct Stdout;
pub type Stderr = Stdout;
pub const STDIO_CHANNEL: u32 = 1;
impl Stdin {
pub const fn new() -> Stdin {
Stdin
}
}
impl io::Read for Stdin {
fn read(&mut self, mut buf: &mut [u8]) -> io::Result<usize> {
let mut count = 0;
for out_byte in buf.iter_mut() {
let byte = unsafe { vex_sdk::vexSerialReadChar(STDIO_CHANNEL) };
if byte < 0 {
break;
}
*out_byte = byte as u8;
count += 1;
}
Ok(count)
}
}
impl Stdout {
pub const fn new() -> Stdout {
Stdout
}
}
impl io::Write for Stdout {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let mut written = 0;
// HACK: VEXos holds an internal ringbuffer for serial writes that is flushed to USB1
// roughly every millisecond by `vexTasksRun`. For writes larger than 2048 bytes, we
// must block until that buffer is flushed to USB1 before writing the rest of `buf`.
//
// This is fairly nonstandard for a `write` implementation, but it avoids a guaranteed
// recursive panic when using macros such as `print!` to write large amounts of data
// (buf.len() > 2048) to stdout at once.
for chunk in buf.chunks(STDOUT_BUF_SIZE) {
if unsafe { vex_sdk::vexSerialWriteFree(STDIO_CHANNEL) as usize } < chunk.len() {
self.flush().unwrap();
}
let count: usize = unsafe {
vex_sdk::vexSerialWriteBuffer(STDIO_CHANNEL, chunk.as_ptr(), chunk.len() as u32)
}
.try_into()
.map_err(|_| {
io::const_error!(io::ErrorKind::Uncategorized, "internal write error occurred")
})?;
written += count;
// This is a sanity check to ensure that we don't end up with non-contiguous
// buffer writes. e.g. a chunk gets only partially written, but we continue
// attempting to write the remaining chunks.
//
// In practice, this should never really occur since the previous flush ensures
// enough space in FIFO to write the entire chunk to vexSerialWriteBuffer.
if count != chunk.len() {
break;
}
}
Ok(written)
}
fn flush(&mut self) -> io::Result<()> {
// This may block for up to a millisecond.
unsafe {
while (vex_sdk::vexSerialWriteFree(STDIO_CHANNEL) as usize) != STDOUT_BUF_SIZE {
vex_sdk::vexTasksRun();
}
}
Ok(())
}
}
pub const STDIN_BUF_SIZE: usize = 4096;
pub const STDOUT_BUF_SIZE: usize = 2048;
pub fn is_ebadf(_err: &io::Error) -> bool {
false
}
pub fn panic_output() -> Option<impl io::Write> {
Some(Stdout::new())
}

View File

@ -81,6 +81,13 @@ cfg_select! {
))]
pub use unsupported::set_name;
}
target_os = "vexos" => {
mod vexos;
pub use vexos::{sleep, yield_now};
#[expect(dead_code)]
mod unsupported;
pub use unsupported::{Thread, available_parallelism, current_os_id, set_name, DEFAULT_MIN_STACK_SIZE};
}
all(target_os = "wasi", target_env = "p1") => {
mod wasip1;
pub use wasip1::{DEFAULT_MIN_STACK_SIZE, sleep, yield_now};

View File

@ -0,0 +1,17 @@
use crate::time::{Duration, Instant};
pub fn yield_now() {
unsafe {
vex_sdk::vexTasksRun();
}
}
pub fn sleep(dur: Duration) {
let start = Instant::now();
while start.elapsed() < dur {
unsafe {
vex_sdk::vexTasksRun();
}
}
}

View File

@ -29,6 +29,7 @@ cfg_select! {
target_os = "uefi",
target_os = "zkvm",
target_os = "trusty",
target_os = "vexos",
) => {
mod no_threads;
pub use no_threads::{EagerStorage, LazyStorage, thread_local_inner};
@ -98,6 +99,7 @@ pub(crate) mod guard {
target_os = "uefi",
target_os = "zkvm",
target_os = "trusty",
target_os = "vexos",
) => {
pub(crate) fn enable() {
// FIXME: Right now there is no concept of "thread exit" on

View File

@ -596,6 +596,7 @@ class RustBuild(object):
self.download_url = (
os.getenv("RUSTUP_DIST_SERVER") or self.stage0_data["dist_server"]
)
self.jobs = self.get_toml("jobs", "build") or "default"
self.build = args.build or self.build_triple()
@ -1144,6 +1145,7 @@ class RustBuild(object):
args = [
self.cargo(),
"build",
"--jobs=" + self.jobs,
"--manifest-path",
os.path.join(self.rust_root, "src/bootstrap/Cargo.toml"),
"-Zroot-dir=" + self.rust_root,

View File

@ -4,7 +4,7 @@
Allows compiling user programs for the [VEX V5 Brain](https://www.vexrobotics.com/276-4810.html), a microcontroller for educational and competitive robotics.
Rust support for this target is not affiliated with VEX Robotics or IFI.
Rust support for this target is not affiliated with VEX Robotics or IFI, and does not link to any official VEX SDK.
## Target maintainers
@ -17,11 +17,24 @@ This target is maintained by members of the [vexide](https://github.com/vexide)
## Requirements
This target is cross-compiled and currently requires `#![no_std]`. Dynamic linking is unsupported.
This target is cross-compiled. Dynamic linking is unsupported.
When compiling for this target, the "C" calling convention maps to AAPCS with VFP registers (hard float ABI) and the "system" calling convention maps to AAPCS without VFP registers (soft float ABI).
`#![no_std]` crates can be built using `build-std` to build `core` and `panic_abort` and optionally `alloc`. Unwinding panics are not yet supported on this target.
This target generates binaries in the ELF format that may uploaded to the brain with external tools.
`std` has only partial support due platform limitations. Notably:
- `std::process` and `std::net` are unimplemented. `std::thread` only supports sleeping and yielding, as this is a single-threaded environment.
- `std::time` has full support for `Instant`, but no support for `SystemTime`.
- `std::io` has full support for `stdin`/`stdout`/`stderr`. `stdout` and `stderr` both write to to USB channel 1 on this platform and are not differentiated.
- `std::fs` has limited support for reading or writing to files. Directory operations, file deletion, and some file opening features are unsupported and will return errors.
- A global allocator implemented on top of `dlmalloc` is provided.
- Modules that do not need to interact with the OS beyond allocation such as `std::collections`, `std::hash`, `std::future`, `std::sync`, etc are fully supported.
- Random number generation and hashing is insecure, as there is no reliable source of entropy on this platform.
In order to support some APIs, users are expected to provide a supporting runtime SDK for `libstd` to link against. This library may be provided either by [`vex-sdk-build`](https://github.com/vexide/vex-sdk/tree/main/packages/vex-sdk-build) (which will download an official SDK from VEX) or through an open-source implementation such as [`vex-sdk-jumptable`](https://crates.io/crates/vex-sdk-jumptable).
When compiling for this target, the "C" calling convention maps to AAPCS with VFP registers (hard float ABI) and the "system" calling convention maps to AAPCS without VFP registers (softfp ABI).
This target generates binaries in the ELF format that may be uploaded to the brain with external tools.
## Building the target
@ -29,10 +42,7 @@ You can build Rust with support for this target by adding it to the `target` lis
## Building Rust programs
Rust does not yet ship pre-compiled artifacts for this target. To compile for
this target, you will either need to build Rust with the target enabled (see
"Building the target" above), or build your own copy of `core` by using
`build-std` or similar.
Rust does not yet ship pre-compiled artifacts for this target. To compile for this target, you will either need to build Rust with the target enabled (see "Building the target" above), or build your own copy of `core` by using `build-std` or similar.
When the compiler builds a binary, an ELF build artifact will be produced. Additional tools are required for this artifact to be recognizable to VEXos as a user program.

View File

@ -739,7 +739,7 @@ This flag can be passed multiple times to nest wrappers.
## Passing arguments to rustc when compiling doctests
You can use the `--doctest-compilation-args` flag if you want to add options when compiling the
You can use the `--doctest-build-arg` flag if you want to add options when compiling the
doctest. For example if you have:
```rust,no_run
@ -784,10 +784,10 @@ failures:
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s
```
But if you can limit the lint level to warning by using `--doctest_compilation_args=--cap-lints=warn`:
But if you can limit the lint level to warning by using `--doctest-build-arg=--cap-lints=warn`:
```console
$ rustdoc --test --doctest_compilation_args=--cap-lints=warn file.rs
$ rustdoc --test --doctest-build-arg=--cap-lints=warn file.rs
running 1 test
test tests/rustdoc-ui/doctest/rustflags.rs - Bar (line 5) ... ok
@ -795,24 +795,8 @@ test tests/rustdoc-ui/doctest/rustflags.rs - Bar (line 5) ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.06s
```
The parsing of arguments works as follows: if it encounters a `"` or a `'`, it will continue
until it finds the character unescaped (without a prepending `\`). If not inside a string, a
whitespace character will also split arguments. Example:
```text
"hello 'a'\" ok" how are 'you today?'
```
will be split as follows:
```text
[
"hello 'a'\" ok",
"how",
"are",
"you today?",
]
```
In order to pass multiple arguments to the underlying compiler,
pass `--doctest-build-arg ARG` for each argument `ARG`.
## `--generate-macro-expansion`: Generate macros expansion toggles in source code

View File

@ -65,5 +65,10 @@
<strong>Note:</strong> Encountered an error during type layout; {#+ #}
the type's layout depended on the type's layout itself. {# #}
</p>
{% when Err(LayoutError::InvalidSimd {..}) %}
<p> {# #}
<strong>Note:</strong> Encountered an error during type layout; {#+ #}
the vector type had zero elements or too many elements. {# #}
</p>
{% endmatch %}
</div> {# #}

View File

@ -1,4 +1,3 @@
use rand::Rng;
use rustc_apfloat::{self, Float, FloatConvert, Round};
use rustc_middle::mir;
use rustc_middle::ty::{self, FloatTy};
@ -39,46 +38,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"sqrtf64" => sqrt::<rustc_apfloat::ieee::Double>(this, args, dest)?,
"sqrtf128" => sqrt::<rustc_apfloat::ieee::Quad>(this, args, dest)?,
"fmaf32" => {
let [a, b, c] = check_intrinsic_arg_count(args)?;
let a = this.read_scalar(a)?.to_f32()?;
let b = this.read_scalar(b)?.to_f32()?;
let c = this.read_scalar(c)?.to_f32()?;
let res = a.mul_add(b, c).value;
let res = this.adjust_nan(res, &[a, b, c]);
this.write_scalar(res, dest)?;
}
"fmaf64" => {
let [a, b, c] = check_intrinsic_arg_count(args)?;
let a = this.read_scalar(a)?.to_f64()?;
let b = this.read_scalar(b)?.to_f64()?;
let c = this.read_scalar(c)?.to_f64()?;
let res = a.mul_add(b, c).value;
let res = this.adjust_nan(res, &[a, b, c]);
this.write_scalar(res, dest)?;
}
"fmuladdf32" => {
let [a, b, c] = check_intrinsic_arg_count(args)?;
let a = this.read_scalar(a)?.to_f32()?;
let b = this.read_scalar(b)?.to_f32()?;
let c = this.read_scalar(c)?.to_f32()?;
let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
let res = this.adjust_nan(res, &[a, b, c]);
this.write_scalar(res, dest)?;
}
"fmuladdf64" => {
let [a, b, c] = check_intrinsic_arg_count(args)?;
let a = this.read_scalar(a)?.to_f64()?;
let b = this.read_scalar(b)?.to_f64()?;
let c = this.read_scalar(c)?.to_f64()?;
let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
let res = this.adjust_nan(res, &[a, b, c]);
this.write_scalar(res, dest)?;
}
#[rustfmt::skip]
| "fadd_fast"
| "fsub_fast"

View File

@ -1293,6 +1293,11 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
ecx.equal_float_min_max(a, b)
}
#[inline(always)]
fn float_fuse_mul_add(ecx: &mut InterpCx<'tcx, Self>) -> bool {
ecx.machine.float_nondet && ecx.machine.rng.get_mut().random()
}
#[inline(always)]
fn ub_checks(ecx: &InterpCx<'tcx, Self>) -> InterpResult<'tcx, bool> {
interp_ok(ecx.tcx.sess.ub_checks())

View File

@ -578,6 +578,7 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
"rustc-literal-escaper",
"shlex",
"unwinding",
"vex-sdk",
"wasi",
"windows-sys",
"windows-targets",

View File

@ -0,0 +1,41 @@
//@ compile-flags: -C opt-level=3
#![crate_type = "lib"]
mod foobar {
use std::alloc::{GlobalAlloc, Layout};
struct Allocator;
unsafe impl GlobalAlloc for Allocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
// CHECK-LABEL: ; __rustc::__rust_alloc
// CHECK-NEXT: ; Function Attrs: {{.*}}allockind("alloc,uninitialized,aligned") allocsize(0){{.*}}
// CHECK-NEXT: define{{.*}} noalias{{.*}} ptr @{{.*}}__rust_alloc(i[[SIZE:[0-9]+]] {{.*}}%size, i[[SIZE]] allocalign{{.*}} %align)
panic!()
}
unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
// CHECK-LABEL: ; __rustc::__rust_dealloc
// CHECK-NEXT: ; Function Attrs: {{.*}}allockind("free"){{.*}}
// CHECK-NEXT: define{{.*}} void @{{.*}}__rust_dealloc(ptr allocptr{{.*}} %ptr, i[[SIZE]] {{.*}} %size, i[[SIZE]] {{.*}} %align)
panic!()
}
unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 {
// CHECK-LABEL: ; __rustc::__rust_realloc
// CHECK-NEXT: ; Function Attrs: {{.*}}allockind("realloc,aligned") allocsize(3){{.*}}
// CHECK-NEXT: define{{.*}} noalias{{.*}} ptr @{{.*}}__rust_realloc(ptr allocptr{{.*}} %ptr, i[[SIZE]] {{.*}} %size, i[[SIZE]] allocalign{{.*}} %align, i[[SIZE]] {{.*}} %new_size)
panic!()
}
unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 {
// CHECK-LABEL: ; __rustc::__rust_alloc_zeroed
// CHECK-NEXT: ; Function Attrs: {{.*}}allockind("alloc,zeroed,aligned") allocsize(0){{.*}}
// CHECK-NEXT: define{{.*}} noalias{{.*}} ptr @{{.*}}__rust_alloc_zeroed(i[[SIZE]] {{.*}} %size, i[[SIZE]] allocalign{{.*}} %align)
panic!()
}
}
#[global_allocator]
static GLOBAL: Allocator = Allocator;
}

View File

@ -61,7 +61,6 @@ fn main() {
diff()
.expected_file("short-error.txt")
.actual_text("(linker error)", out.stderr())
.normalize(r#"/rustc[^/_-]*/"#, "/rustc/")
.normalize("libpanic_abort", "libpanic_unwind")
.normalize(
regex::escape(

View File

@ -0,0 +1,46 @@
//@ run-pass
#![feature(unsize)]
#![feature(coerce_unsized)]
use std::fmt::Display;
use std::marker::Unsize;
use std::ops::CoerceUnsized;
use std::rc::Weak;
#[repr(transparent)]
struct X<'a, T: ?Sized> {
f: &'a T,
}
impl<'a, T: ?Sized> Drop for X<'a, T> {
fn drop(&mut self) {
panic!()
}
}
impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> CoerceUnsized<X<'a, U>> for X<'a, T> where
&'a T: CoerceUnsized<&'a U>
{
}
const Y: X<'static, i32> = X { f: &0 };
fn main() {
let _: [X<'static, dyn Display>; 0] = [Y; 0];
coercion_on_weak_in_const();
coercion_on_weak_as_cast();
}
fn coercion_on_weak_in_const() {
const X: Weak<i32> = Weak::new();
const Y: [Weak<dyn Send>; 0] = [X; 0];
let _ = Y;
}
fn coercion_on_weak_as_cast() {
const Y: X<'static, i32> = X { f: &0 };
// What happens in the following code is that
// a constant is explicitly coerced into
let _a: [X<'static, dyn Display>; 0] = [Y as X<'static, dyn Display>; 0];
}

View File

@ -0,0 +1,5 @@
#![feature(rustc_attrs, repr_simd)]
#[repr(simd, packed)]
#[rustc_simd_monomorphize_lane_limit = "8"]
pub struct Simd<T, const N: usize>(pub [T; N]);

View File

@ -6,7 +6,5 @@
struct Simd<T, const N: usize>([T; N]);
fn main() {
let _too_big = Simd([1_u16; 54321]);
let _too_big = Simd([1_u16; 54321]); //~ ERROR the SIMD type `Simd<u16, 54321>` has more elements than the limit 32768
}
//~? ERROR monomorphising SIMD type `Simd<u16, 54321>` of length greater than 32768

View File

@ -1,4 +1,8 @@
error: monomorphising SIMD type `Simd<u16, 54321>` of length greater than 32768
error: the SIMD type `Simd<u16, 54321>` has more elements than the limit 32768
--> $DIR/monomorphize-too-long.rs:9:9
|
LL | let _too_big = Simd([1_u16; 54321]);
| ^^^^^^^^
error: aborting due to 1 previous error

View File

@ -6,7 +6,5 @@
struct Simd<T, const N: usize>([T; N]);
fn main() {
let _empty = Simd([1.0; 0]);
let _empty = Simd([1.0; 0]); //~ ERROR the SIMD type `Simd<f64, 0>` has zero elements
}
//~? ERROR monomorphising SIMD type `Simd<f64, 0>` of zero length

View File

@ -1,4 +1,8 @@
error: monomorphising SIMD type `Simd<f64, 0>` of zero length
error: the SIMD type `Simd<f64, 0>` has zero elements
--> $DIR/monomorphize-zero-length.rs:9:9
|
LL | let _empty = Simd([1.0; 0]);
| ^^^^^^
error: aborting due to 1 previous error

View File

@ -0,0 +1,12 @@
//@ build-fail
//@ aux-crate:simd=simd-lane-limit.rs
extern crate simd;
use simd::Simd;
fn main() {
// test non-power-of-two, since #[repr(simd, packed)] has unusual layout
let _x: Simd<i32, 24> = Simd([0; 24]);
//~^ ERROR the SIMD type `simd::Simd<i32, 24>` has more elements than the limit 8
}

View File

@ -0,0 +1,8 @@
error: the SIMD type `simd::Simd<i32, 24>` has more elements than the limit 8
--> $DIR/simd-lane-limit-err-npow2.rs:10:9
|
LL | let _x: Simd<i32, 24> = Simd([0; 24]);
| ^^
error: aborting due to 1 previous error

View File

@ -0,0 +1,11 @@
//@ build-fail
//@ aux-crate:simd=simd-lane-limit.rs
extern crate simd;
use simd::Simd;
fn main() {
let _x: Simd<i32, 16> = Simd([0; 16]);
//~^ ERROR the SIMD type `simd::Simd<i32, 16>` has more elements than the limit 8
}

View File

@ -0,0 +1,8 @@
error: the SIMD type `simd::Simd<i32, 16>` has more elements than the limit 8
--> $DIR/simd-lane-limit-err.rs:9:9
|
LL | let _x: Simd<i32, 16> = Simd([0; 16]);
| ^^
error: aborting due to 1 previous error

View File

@ -0,0 +1,14 @@
//@ build-pass
//@ aux-crate:simd=simd-lane-limit.rs
extern crate simd;
use simd::Simd;
fn main() {
let _x: Simd<i32, 4> = Simd([0; 4]);
let _y: Simd<i32, 8> = Simd([0; 8]);
// test non-power-of-two, since #[repr(simd, packed)] has unusual layout
let _z: Simd<i32, 6> = Simd([0; 6]);
}

View File

@ -6,7 +6,5 @@
struct Simd<const N: usize>([f32; N]);
fn main() {
let _ = Simd::<0>([]);
let _empty = Simd::<0>([]); //~ ERROR the SIMD type `Simd<0>` has zero elements
}
//~? ERROR monomorphising SIMD type `Simd<0>` of zero length

View File

@ -1,4 +1,8 @@
error: monomorphising SIMD type `Simd<0>` of zero length
error: the SIMD type `Simd<0>` has zero elements
--> $DIR/type-generic-monomorphisation-empty.rs:9:9
|
LL | let _empty = Simd::<0>([]);
| ^^^^^^
error: aborting due to 1 previous error

View File

@ -6,7 +6,6 @@
struct Simd<const N: usize>([f32; N]);
fn main() {
let _ = Simd::<65536>([0.; 65536]);
let _x = Simd::<65536>([0.; 65536]);
//~^ ERROR the SIMD type `Simd<65536>` has more elements than the limit 32768
}
//~? ERROR monomorphising SIMD type `Simd<65536>` of length greater than 32768

View File

@ -1,4 +1,8 @@
error: monomorphising SIMD type `Simd<65536>` of length greater than 32768
error: the SIMD type `Simd<65536>` has more elements than the limit 32768
--> $DIR/type-generic-monomorphisation-oversized.rs:9:9
|
LL | let _x = Simd::<65536>([0.; 65536]);
| ^^
error: aborting due to 1 previous error