diff --git a/Cargo.lock b/Cargo.lock index 873182dde9102..9d5d0f2d403f9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6213,9 +6213,9 @@ dependencies = [ [[package]] name = "wasi-preview1-component-adapter-provider" -version = "44.0.2" +version = "46.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2211ca2d69a88055eefb06bad741dde3180c9d4020f7e42fea72caba83f9c10c" +checksum = "1b6c48003fe59c201c97a7786ff55feabe6b6f83b598aa9ff5bcc4f94d940bf3" [[package]] name = "wasip2" @@ -6282,9 +6282,9 @@ dependencies = [ [[package]] name = "wasm-component-ld" -version = "0.5.25" +version = "0.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee72c06c556db23aca2d29e1e183d96efdffa7110b24915629a5091de600a894" +checksum = "51a12709376d4ce64f472699500db3b0e5902cc2bef16fb6ca3098bfdac032fa" dependencies = [ "anyhow", "clap", @@ -6293,12 +6293,12 @@ dependencies = [ "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.252.0", + "wasmparser 0.253.0", "wat", "windows-sys 0.61.2", "winsplit", - "wit-component 0.252.0", - "wit-parser 0.252.0", + "wit-component 0.253.0", + "wit-parser 0.253.0", ] [[package]] @@ -6330,12 +6330,12 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8185ae345fa5687c054626ff9a50e7089797a343d9904d1dc9820eb4c4d3196f" +checksum = "59972d6cd272259de647b7c1f1912e45e289c75ffd4be04e10695507cd7e1b59" dependencies = [ "leb128fmt", - "wasmparser 0.252.0", + "wasmparser 0.253.0", ] [[package]] @@ -6352,14 +6352,14 @@ dependencies = [ [[package]] name = "wasm-metadata" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b7e08e02a3cd55bf778009d4cd6faae50da011f293644daf78a531a32d6d142" +checksum = "b3f45816ef616806f48498bcd831377de578c4fa51db0c83ab8ceb78cc13523b" dependencies = [ "anyhow", "indexmap", - "wasm-encoder 0.252.0", - "wasmparser 0.252.0", + "wasm-encoder 0.253.0", + "wasmparser 0.253.0", ] [[package]] @@ -6396,9 +6396,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3eb099dcadcde5be9eef55e3a337128efd4e44b4c93122487e4d2e4e1c6627c" +checksum = "19db11f87d2486580e1e8b6f494c54df7e0566b87d0b599db843c24019667339" dependencies = [ "bitflags", "hashbrown 0.17.0", @@ -6409,22 +6409,22 @@ dependencies = [ [[package]] name = "wast" -version = "252.0.0" +version = "253.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942a3449d6a593fccc111a6241c8df52bda168af30e40bf9580d4394d7374c65" +checksum = "d3264542f8965c5d84fb1085d924bfba9a6314bb228eff13a2de14d7627664d0" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.2", - "wasm-encoder 0.252.0", + "wasm-encoder 0.253.0", ] [[package]] name = "wat" -version = "1.252.0" +version = "1.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c72a4ba7088f7bac94cf516e49882bdf97068904a563768cf249efc839ec42cb" +checksum = "4bfc5ce906144200c972ec617470aa35bd847472e170b26dde3e80541c674055" dependencies = [ "wast", ] @@ -6872,9 +6872,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76db0662b590f45d33d0e363fa13539a5a1eecd35d5a12fe208c335461c1053d" +checksum = "dbbd2500ac3488489ee8c6e59b79d7e47e6da5bfb019efd35d5dca57b78af624" dependencies = [ "anyhow", "bitflags", @@ -6883,10 +6883,10 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.252.0", - "wasm-metadata 0.252.0", - "wasmparser 0.252.0", - "wit-parser 0.252.0", + "wasm-encoder 0.253.0", + "wasm-metadata 0.253.0", + "wasmparser 0.253.0", + "wit-parser 0.253.0", ] [[package]] @@ -6909,9 +6909,9 @@ dependencies = [ [[package]] name = "wit-parser" -version = "0.252.0" +version = "0.253.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4266bea110371c620ccf3201c5023676046bc4556e5c7cfb5d500bda5ebc162d" +checksum = "4d997b8e5920fcbeec742b58e583325d6419a6aca617ae8075c406a61c65ba8a" dependencies = [ "anyhow", "hashbrown 0.17.0", @@ -6923,7 +6923,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-ident", - "wasmparser 0.252.0", + "wasmparser 0.253.0", ] [[package]] diff --git a/RELEASES.md b/RELEASES.md index 2fa271401fbb3..3146bdeb1b118 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,3 +1,93 @@ +Version 1.97.0 (2026-07-09) +========================== + + + +Language +-------- +- [Consider `Result` and `ControlFlow` to be equivalent to `T` for must use lint](https://github.com/rust-lang/rust/pull/148214) +- [Add allow-by-default `dead_code_pub_in_binary` lint for unused pub items in binary crates](https://github.com/rust-lang/rust/pull/149509) +- [Stabilize the `div32`, `lam-bh`, `lamcas`, `ld-seq-sa` and `scq` target features](https://github.com/rust-lang/rust/pull/154510) +- [Stabilize `cfg(target_has_atomic_primitive_alignment)`](https://github.com/rust-lang/rust/pull/155006) +- [Allow trailing `self` in imports in more cases](https://github.com/rust-lang/rust/pull/155137) + + + + +Platform Support +---------------- +- [nvptx64-nvidia-cuda: drop support for old architectures and old ISAs](https://github.com/rust-lang/rust/pull/152443) + + +Refer to Rust's [platform support page][platform-support-doc] +for more information on Rust's tiered platform support. + +[platform-support-doc]: https://doc.rust-lang.org/rustc/platform-support.html + + + + +Stabilized APIs +--------------- + +- [`Default for RepeatN`](https://doc.rust-lang.org/stable/std/iter/struct.RepeatN.html#impl-Default-for-RepeatN%3CA%3E) +- [`Copy for ffi::FromBytesUntilNulError`](https://doc.rust-lang.org/stable/std/ffi/struct.FromBytesUntilNulError.html#impl-Copy-for-FromBytesUntilNulError) +- [`Send for std::fs::File` on UEFI](https://github.com/rust-lang/rust/pull/154003) +- [`<{integer}>::isolate_highest_one`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.isolate_highest_one) +- [`<{integer}>::isolate_lowest_one`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.isolate_lowest_one) +- [`<{integer}>::highest_one`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.highest_one) +- [`<{integer}>::lowest_one`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.lowest_one) +- [`<{integer}>::bit_width`](https://doc.rust-lang.org/stable/std/primitive.u32.html#method.bit_width) +- [`NonZero<{integer}>::isolate_highest_one`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.isolate_highest_one) +- [`NonZero<{integer}>::isolate_lowest_one`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.isolate_lowest_one) +- [`NonZero<{integer}>::highest_one`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.highest_one) +- [`NonZero<{integer}>::lowest_one`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.lowest_one) +- [`NonZero<{integer}>::bit_width`](https://doc.rust-lang.org/stable/std/num/struct.NonZero.html#method.bit_width) + + +These previously stable APIs are now stable in const contexts: + +- [`char::is_control`](https://doc.rust-lang.org/stable/std/primitive.char.html#method.is_control) + + + + +Cargo +----- +- [Stabilize `build.warnings` config.](https://github.com/rust-lang/cargo/pull/16796) This controls how lint warnings from local packages are treated. Useful for enforcing a warning-free build in CI, replacing `-Dwarnings`. [docs](https://doc.rust-lang.org/nightly/cargo/reference/config.html#buildwarnings) +- [Stabilize `resolver.lockfile-path` config.](https://github.com/rust-lang/cargo/pull/16694) This allows specifying the path to the lockfile to use when resolving dependencies. Useful when working with read-only source directories. [docs](https://doc.rust-lang.org/nightly/cargo/reference/config.html#resolverlockfile-path) +- [cargo-clean: Error when `--target-dir` doesn't look like a Cargo target directory.](https://github.com/rust-lang/cargo/pull/16712) This prevents accidental deletion of non-target directories. +- [Add `-m` shorthand for `--manifest-path`](https://github.com/rust-lang/cargo/pull/16858) +- [Remove `curl` dependency from `crates-io` crate](https://github.com/rust-lang/cargo/pull/16936) + + + +Rustdoc +----- +- [Stabilize `--emit` flag](https://github.com/rust-lang/rust/pull/146220) +- [Stabilize `--remap-path-prefix`](https://github.com/rust-lang/rust/pull/155307) + + + + +Compatibility Notes +------------------- +- [Emit a future-compatibility warning when relying on `f32: From<{float}>` to constrain `{float}`](https://github.com/rust-lang/rust/pull/139087) +- [Rust will use the v0 symbol mangling scheme by default.](https://github.com/rust-lang/rust/pull/151994) This may cause some tools (such as debuggers or profilers, especially with old versions) to fail to demangle symbols emitted by Rust. It may also cause the formatting of text in backtraces to change. +- [Prevent deref coercions in `pin!`, in order to prevent unsoundness.](https://github.com/rust-lang/rust/pull/153457) The most likely case where this might impact users is: writing `pin!(x)` where `x` has type `&mut T` will now always correctly produce a value of type `Pin<&mut &mut T>`, instead of sometimes allowing a coercion that produces a value of type `Pin<&mut T>`. This coercion was previously incorrectly allowed since Rust 1.88.0. +- [Deprecate `std::char` constants and functions](https://github.com/rust-lang/rust/pull/153873) +- [Warn on linker output by default](https://github.com/rust-lang/rust/pull/153968) +- [Remove hidden `f64` methods which have been deprecated since 1.0](https://github.com/rust-lang/rust/pull/153975) +- [report the `varargs_without_pattern` lint in deps](https://github.com/rust-lang/rust/pull/154599) +- [Forbid passing generic arguments to module path segments even if the module reexports a generic enum variant](https://github.com/rust-lang/rust/pull/154971) +- [Error on invalid macho `link_section` specifier](https://github.com/rust-lang/rust/pull/155065) +- The encoding of certain `enum`s [have changed](https://github.com/rust-lang/rust/pull/155473). This is not a breaking change, as it only applies to `enum`s without layout guarantees, but is noted here as we've seen people impacted from having made assumptions about the layout algorithm. +- [Error on `#[export_name = "..."]` where the name is empty](https://github.com/rust-lang/rust/pull/155515) +- [Syntactically reject tuple index shorthands in struct patterns](https://github.com/rust-lang/rust/pull/155698) +- [validate `#[link_name = "..."]` & `#[link(name = "...")]` parameters](https://github.com/rust-lang/rust/pull/155817) +- On Windows, after calling `shutdown` on a socket to shut down the write side, attempting to write to the socket will now produce a `BrokenPipe` error rather than `Other`. [Map `WSAESHUTDOWN` to `io::ErrorKind::BrokenPipe`](https://github.com/rust-lang/rust/pull/156063) + + Version 1.96.1 (2026-06-30) =========================== diff --git a/compiler/rustc_abi/src/callconv.rs b/compiler/rustc_abi/src/callconv.rs index 41e87caf40c7d..4fda4735b613c 100644 --- a/compiler/rustc_abi/src/callconv.rs +++ b/compiler/rustc_abi/src/callconv.rs @@ -89,7 +89,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { unreachable!("`homogeneous_aggregate` should not be called for scalable vectors") } - BackendRepr::ScalarPair(..) | BackendRepr::Memory { sized: true } => { + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { sized: true } => { // Helper for computing `homogeneous_aggregate`, allowing a custom // starting offset (used below for handling variants). let from_fields_at = diff --git a/compiler/rustc_abi/src/layout.rs b/compiler/rustc_abi/src/layout.rs index 003e811dc7855..ad48e14430952 100644 --- a/compiler/rustc_abi/src/layout.rs +++ b/compiler/rustc_abi/src/layout.rs @@ -476,7 +476,7 @@ impl LayoutCalculator { Err(AbiMismatch) | Ok(None) => BackendRepr::Memory { sized: true }, Ok(Some((repr, _))) => match repr { // Mismatched alignment (e.g. union is #[repr(packed)]): disable opt - BackendRepr::Scalar(_) | BackendRepr::ScalarPair(_, _) + BackendRepr::Scalar(_) | BackendRepr::ScalarPair { .. } if repr.scalar_platform_align(dl).unwrap() != align => { BackendRepr::Memory { sized: true } @@ -489,7 +489,7 @@ impl LayoutCalculator { } // the alignment tests passed and we can use this BackendRepr::Scalar(..) - | BackendRepr::ScalarPair(..) + | BackendRepr::ScalarPair { .. } | BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } | BackendRepr::Memory { .. } => repr, @@ -558,7 +558,7 @@ impl LayoutCalculator { }; match &mut st.backend_repr { BackendRepr::Scalar(scalar) => hide_niches(scalar), - BackendRepr::ScalarPair(a, b) => { + BackendRepr::ScalarPair { a, b, b_offset: _ } => { hide_niches(a); hide_niches(b); } @@ -701,13 +701,21 @@ impl LayoutCalculator { // When the total alignment and size match, we can use the // same ABI as the scalar variant with the reserved niche. BackendRepr::Scalar(_) => BackendRepr::Scalar(niche_scalar), - BackendRepr::ScalarPair(first, second) => { + BackendRepr::ScalarPair { a: first, b: second, b_offset } => { // Only the niche is guaranteed to be initialised, // so use union layouts for the other primitive. if niche_offset == Size::ZERO { - BackendRepr::ScalarPair(niche_scalar, second.to_union()) + BackendRepr::ScalarPair { + a: niche_scalar, + b: second.to_union(), + b_offset, + } } else { - BackendRepr::ScalarPair(first.to_union(), niche_scalar) + BackendRepr::ScalarPair { + a: first.to_union(), + b: niche_scalar, + b_offset, + } } } _ => BackendRepr::Memory { sized: true }, @@ -1037,7 +1045,7 @@ impl LayoutCalculator { // If we pick a "clever" (by-value) ABI, we might have to adjust the ABI of the // variants to ensure they are consistent. This is because a downcast is // semantically a NOP, and thus should not affect layout. - if matches!(abi, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) { + if matches!(abi, BackendRepr::Scalar(..) | BackendRepr::ScalarPair { .. }) { for variant in &mut layout_variants { // We only do this for variants with fields; the others are not accessed anyway. // Also do not overwrite any already existing "clever" ABIs. @@ -1354,7 +1362,7 @@ impl LayoutCalculator { } // But scalar pairs are Rust-specific and get // treated as aggregates by C ABIs anyway. - BackendRepr::ScalarPair(..) => { + BackendRepr::ScalarPair { .. } => { abi = field.backend_repr; } _ => {} diff --git a/compiler/rustc_abi/src/layout/simple.rs b/compiler/rustc_abi/src/layout/simple.rs index 5fd504def384a..1fffb84ec21cb 100644 --- a/compiler/rustc_abi/src/layout/simple.rs +++ b/compiler/rustc_abi/src/layout/simple.rs @@ -110,7 +110,7 @@ impl LayoutData { offsets: [Size::ZERO, b_offset].into(), in_memory_order: [FieldIdx::new(0), FieldIdx::new(1)].into(), }, - backend_repr: BackendRepr::ScalarPair(a, b), + backend_repr: BackendRepr::ScalarPair { a, b, b_offset }, largest_niche, uninhabited: false, align: AbiAlign::new(align), diff --git a/compiler/rustc_abi/src/lib.rs b/compiler/rustc_abi/src/lib.rs index c2069432e6f8e..b8cd152e30288 100644 --- a/compiler/rustc_abi/src/lib.rs +++ b/compiler/rustc_abi/src/lib.rs @@ -1796,14 +1796,18 @@ impl IntoDiagArg for NumScalableVectors { pub enum BackendRepr { Scalar(Scalar), /// The data contained in this type can be entirely represented by two scalars. - /// The two scalars are listed in *memory* order, so the first is at offset zero - /// and the second at a non-zero offset. + /// The two scalars are listed in *memory* order, so `a` is at offset zero + /// and `b` is at non-zero offset `b_offset`. /// These need not be `FieldIdx(0)` and `FieldIdx(1)`. /// - /// As of June 2026 the offset to the second scalar is the size of the first - /// scalar rounded up to the platform alignment of the second scalar. + /// As of June 2026 the `b_offset` is always the size of the `a` + /// scalar rounded up to the platform alignment of the `b` scalar. /// That may soon change, however; see MCP#1007. - ScalarPair(Scalar, Scalar), + ScalarPair { + a: Scalar, + b: Scalar, + b_offset: Size, + }, SimdScalableVector { element: Scalar, count: u64, @@ -1826,7 +1830,7 @@ impl BackendRepr { pub fn is_unsized(&self) -> bool { match *self { BackendRepr::Scalar(_) - | BackendRepr::ScalarPair(..) + | BackendRepr::ScalarPair { .. } // FIXME(rustc_scalable_vector): Scalable vectors are `Sized` while the // `sized_hierarchy` feature is not yet fully implemented. After `sized_hierarchy` is // fully implemented, scalable vectors will remain `Sized`, they just won't be @@ -1875,7 +1879,7 @@ impl BackendRepr { pub fn scalar_platform_align(&self, cx: &C) -> Option { match *self { BackendRepr::Scalar(s) => Some(s.default_align(cx).abi), - BackendRepr::ScalarPair(s1, s2) => { + BackendRepr::ScalarPair { a: s1, b: s2, b_offset: _ } => { Some(s1.default_align(cx).max(s2.default_align(cx)).abi) } // The align of a Vector can vary in surprising ways @@ -1893,8 +1897,7 @@ impl BackendRepr { // No padding in scalars. BackendRepr::Scalar(s) => Some(s.size(cx)), // May have some padding between the pair. - BackendRepr::ScalarPair(s1, s2) => { - let field2_offset = s1.size(cx).align_to(s2.default_align(cx).abi); + BackendRepr::ScalarPair { a: _, b: s2, b_offset: field2_offset } => { let size = (field2_offset + s2.size(cx)).align_to( self.scalar_platform_align(cx) // We absolutely must have an answer here or everything is FUBAR. @@ -1913,8 +1916,8 @@ impl BackendRepr { pub fn to_union(&self) -> Self { match *self { BackendRepr::Scalar(s) => BackendRepr::Scalar(s.to_union()), - BackendRepr::ScalarPair(s1, s2) => { - BackendRepr::ScalarPair(s1.to_union(), s2.to_union()) + BackendRepr::ScalarPair { a: s1, b: s2, b_offset } => { + BackendRepr::ScalarPair { a: s1.to_union(), b: s2.to_union(), b_offset } } BackendRepr::SimdVector { element, count } => { BackendRepr::SimdVector { element: element.to_union(), count } @@ -1939,8 +1942,13 @@ impl BackendRepr { BackendRepr::SimdVector { element: element_l, count: count_l }, BackendRepr::SimdVector { element: element_r, count: count_r }, ) => element_l.primitive() == element_r.primitive() && count_l == count_r, - (BackendRepr::ScalarPair(l1, l2), BackendRepr::ScalarPair(r1, r2)) => { - l1.primitive() == r1.primitive() && l2.primitive() == r2.primitive() + ( + BackendRepr::ScalarPair { a: l1, b: l2, b_offset: l_offset }, + BackendRepr::ScalarPair { a: r1, b: r2, b_offset: r_offset }, + ) => { + l1.primitive() == r1.primitive() + && l2.primitive() == r2.primitive() + && l_offset == r_offset } // Everything else must be strictly identical. _ => self == other, @@ -2180,7 +2188,7 @@ impl LayoutData { BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => false, - BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => true, + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => true, } } @@ -2294,7 +2302,7 @@ impl LayoutData { pub fn is_zst(&self) -> bool { match self.backend_repr { BackendRepr::Scalar(_) - | BackendRepr::ScalarPair(..) + | BackendRepr::ScalarPair { .. } | BackendRepr::SimdScalableVector { .. } | BackendRepr::SimdVector { .. } => false, BackendRepr::Memory { sized } => sized && self.size.bytes() == 0, diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 632f138a4c798..bde6614b48548 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1378,15 +1378,6 @@ pub enum UnsafeSource { UserProvided, } -/// Track whether under `feature(min_generic_const_args)` this anon const -/// was explicitly disambiguated as an anon const or not through the use of -/// `const { ... }` syntax. -#[derive(Clone, PartialEq, Encodable, Decodable, Debug, Copy, Walkable)] -pub enum MgcaDisambiguation { - AnonConst, - Direct, -} - /// A constant (expression) that's not an item or associated item, /// but needs its own `DefId` for type-checking, const-eval, etc. /// These are usually found nested inside types (e.g., array lengths) @@ -1396,7 +1387,6 @@ pub enum MgcaDisambiguation { pub struct AnonConst { pub id: NodeId, pub value: Box, - pub mgca_disambiguation: MgcaDisambiguation, } /// An expression. @@ -1627,6 +1617,7 @@ impl Expr { | ExprKind::UnsafeBinderCast(..) | ExprKind::While(..) | ExprKind::Yield(YieldKind::Postfix(..)) + | ExprKind::DirectConstArg(..) | ExprKind::Err(_) | ExprKind::Dummy => prefix_attrs_precedence(&self.attrs), } @@ -1920,6 +1911,9 @@ pub enum ExprKind { UnsafeBinderCast(UnsafeBinderCastKind, Box, Option>), + /// An mGCA `direct_const_arg!()` expression. + DirectConstArg(Box), + /// Placeholder for an expression that wasn't syntactically well formed in some way. Err(ErrorGuaranteed), @@ -2566,6 +2560,8 @@ pub enum TyKind { FieldOf(Box, Option, Ident), /// A view of a type. `T.{ field_1, field_2 }`. View(Box, #[visitable(ignore)] ThinVec), + /// An mGCA `direct_const_arg!()` expression. + DirectConstArg(Box), /// Sometimes we need a dummy value when no error has occurred. Dummy, /// Placeholder for a kind that has failed to be defined. @@ -3066,6 +3062,9 @@ impl FnDecl { /// Must have the same value as `FnSigKind::NO_SPLATTED_ARG_INDEX` and `FnDeclFlags::NO_SPLATTED_ARG_INDEX`. pub const NO_SPLATTED_ARG_INDEX: u8 = u8::MAX; + /// The maximum valid splatted argument index. + pub const MAX_VALID_SPLATTED_ARG_INDEX: u8 = Self::NO_SPLATTED_ARG_INDEX - 1; + /// Returns a splatted argument index, if any are present. pub fn splatted(&self) -> Option { self.inputs.iter().enumerate().find_map(|(index, arg)| { diff --git a/compiler/rustc_ast/src/util/classify.rs b/compiler/rustc_ast/src/util/classify.rs index 56f96f9a8a279..0c2218e557f23 100644 --- a/compiler/rustc_ast/src/util/classify.rs +++ b/compiler/rustc_ast/src/util/classify.rs @@ -155,6 +155,7 @@ pub fn leading_labeled_expr(mut expr: &ast::Expr) -> bool { | Yeet(..) | Yield(..) | UnsafeBinderCast(..) + | DirectConstArg(..) | Err(..) | Dummy => return false, } @@ -240,6 +241,7 @@ pub fn expr_trailing_brace(mut expr: &ast::Expr) -> Option> { | Try(_) | Yeet(None) | UnsafeBinderCast(..) + | DirectConstArg(..) | Err(_) | Dummy => { break None; @@ -301,9 +303,10 @@ fn type_trailing_braced_mac_call(mut ty: &ast::Ty) -> Option<&ast::MacCall> { | ast::TyKind::CVarArgs | ast::TyKind::Pat(..) | ast::TyKind::FieldOf(..) + | ast::TyKind::View(..) + | ast::TyKind::DirectConstArg(..) | ast::TyKind::Dummy - | ast::TyKind::Err(..) - | ast::TyKind::View(..) => break None, + | ast::TyKind::Err(..) => break None, } } } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index fb4e76321d150..e25c1a0b31937 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -418,7 +418,6 @@ macro_rules! common_visitor_and_walkers { UnsafeBinderCastKind, BinOpKind, BlockCheckMode, - MgcaDisambiguation, BorrowKind, BoundAsyncness, BoundConstness, @@ -1074,6 +1073,8 @@ macro_rules! common_visitor_and_walkers { visit_visitable!($($mut)? vis, bytes), ExprKind::UnsafeBinderCast(kind, expr, ty) => visit_visitable!($($mut)? vis, kind, expr, ty), + ExprKind::DirectConstArg(expr) => + visit_visitable!($($mut)? vis, expr), ExprKind::Err(_guar) => {} ExprKind::Dummy => {} } diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs index f66d1ac16c907..4ed23e032d234 100644 --- a/compiler/rustc_ast_lowering/src/expr.rs +++ b/compiler/rustc_ast_lowering/src/expr.rs @@ -498,6 +498,18 @@ impl<'hir> LoweringContext<'_, 'hir> { } ExprKind::MacCall(_) => panic!("{:?} shouldn't exist here", e.span), + + ExprKind::DirectConstArg(_) => { + let e = self + .tcx + .dcx() + .struct_span_err( + e.span, + "expected expression, found `direct_const_arg!()` constant", + ) + .emit(); + hir::ExprKind::Err(e) + } }; hir::Expr { hir_id: expr_hir_id, kind, span } @@ -599,11 +611,7 @@ impl<'hir> LoweringContext<'_, 'hir> { arg }; - let anon_const = AnonConst { - id: node_id, - value: const_value, - mgca_disambiguation: MgcaDisambiguation::AnonConst, - }; + let anon_const = AnonConst { id: node_id, value: const_value }; generic_args.push(AngleBracketedArg::Arg(GenericArg::Const(anon_const))); } else { real_args.push(arg); diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index dc1acade85ed5..4963a38ddfd45 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1478,6 +1478,16 @@ impl<'hir> LoweringContext<'_, 'hir> { } } } + TyKind::DirectConstArg(expr) + if self.tcx.features().min_generic_const_args() => + { + let ct = match self.can_lower_expr_to_const_arg_direct(expr) { + Ok(()) => self.lower_expr_to_const_arg_direct(expr, None), + Err(e) => e.emit(self), + }; + let ct = self.arena.alloc(ct); + return GenericArg::Const(ct.try_as_ambig_ct().unwrap()); + } _ => {} } GenericArg::Type(self.lower_ty_alloc(ty, itctx).try_as_ambig_ty().unwrap()) @@ -1754,6 +1764,14 @@ impl<'hir> LoweringContext<'_, 'hir> { // FIXME(scrabsha): lower view types to HIR. return self.lower_ty(ty, itctx); } + TyKind::DirectConstArg(_) => { + let e = self + .tcx + .dcx() + .struct_span_err(t.span, "expected type, found `direct_const_arg!()` constant") + .emit(); + hir::TyKind::Err(e) + } TyKind::Dummy => panic!("`TyKind::Dummy` should never be lowered"), }; @@ -2672,23 +2690,86 @@ impl<'hir> LoweringContext<'_, 'hir> { } #[instrument(level = "debug", skip(self), ret)] - fn lower_expr_to_const_arg_direct(&mut self, expr: &Expr) -> hir::ConstArg<'hir> { - let span = self.lower_span(expr.span); - - let overly_complex_const = |this: &mut Self| { - let msg = "complex const arguments must be placed inside of a `const` block"; - let e = if expr::WillCreateDefIdsVisitor.visit_expr(expr).is_break() { - // FIXME(mgca): make this non-fatal once we have a better way to handle - // nested items in const args - // Issue: https://github.com/rust-lang/rust/issues/154539 - this.dcx().struct_span_fatal(expr.span, msg).emit() - } else { - this.dcx().struct_span_err(expr.span, msg).emit() - }; + fn can_lower_expr_to_const_arg_direct( + &mut self, + expr: &Expr, + ) -> Result<(), UnrepresentableConstArgError> { + let is_mgca = self.tcx.features().min_generic_const_args(); + // Note the only stable case is currently ExprKind::Path. All others have an is_mgca guard. + match &expr.kind { + ExprKind::Call(func, args) + if is_mgca && let ExprKind::Path(_qself, _path) = &func.kind => + { + for arg in args { + self.can_lower_expr_to_const_arg_direct(arg)?; + } + Ok(()) + } + ExprKind::Tup(exprs) if is_mgca => { + for expr in exprs { + self.can_lower_expr_to_const_arg_direct(expr)?; + } + Ok(()) + } + ExprKind::Path(qself, path) + if is_mgca + || path.is_potential_trivial_const_arg() + && matches!( + self.get_partial_res(expr.id) + .and_then(|partial_res| partial_res.full_res()), + Some(Res::Def(DefKind::ConstParam, _)) + ) => + { + Ok(()) + } + ExprKind::Struct(se) if is_mgca => { + for f in &se.fields { + self.can_lower_expr_to_const_arg_direct(&f.expr)?; + } + Ok(()) + } + ExprKind::Array(elements) if is_mgca => { + for element in elements { + self.can_lower_expr_to_const_arg_direct(element)?; + } + Ok(()) + } + ExprKind::Underscore if is_mgca => Ok(()), + ExprKind::Block(block, _) + if is_mgca + && let [stmt] = block.stmts.as_slice() + && let StmtKind::Expr(expr) = &stmt.kind => + { + self.can_lower_expr_to_const_arg_direct(expr) + } + ExprKind::Lit(literal) if is_mgca => Ok(()), + ExprKind::Unary(UnOp::Neg, inner_expr) + if is_mgca && let ExprKind::Lit(_) = &inner_expr.kind => + { + Ok(()) + } + ExprKind::ConstBlock(anon) if is_mgca => Ok(()), + ExprKind::DirectConstArg(expr) if is_mgca => { + // Always report this as able to be represented directly. If it turns out not to be, + // `lower_expr_to_const_arg_direct` will report an error. + Ok(()) + } + _ => Err(UnrepresentableConstArgError::new(expr)), + } + } - ConstArg { hir_id: this.next_id(), kind: hir::ConstArgKind::Error(e), span } - }; + /// It is not allowed to call this function without checking can_lower_expr_to_const_arg_direct + /// first, as we assume all feature gates/etc. have been checked already. + #[instrument(level = "debug", skip(self), ret)] + fn lower_expr_to_const_arg_direct( + &mut self, + expr: &Expr, + id_override: Option, + ) -> hir::ConstArg<'hir> { + debug_assert!(self.can_lower_expr_to_const_arg_direct(expr).is_ok()); + let span = self.lower_span(expr.span); + let node_id = id_override.unwrap_or(expr.id); match &expr.kind { ExprKind::Call(func, args) if let ExprKind::Path(qself, path) = &func.kind => { let qpath = self.lower_qpath( @@ -2702,23 +2783,27 @@ impl<'hir> LoweringContext<'_, 'hir> { ); let lowered_args = self.arena.alloc_from_iter(args.iter().map(|arg| { - let const_arg = self.lower_expr_to_const_arg_direct(arg); + let const_arg = self.lower_expr_to_const_arg_direct(arg, None); &*self.arena.alloc(const_arg) })); ConstArg { - hir_id: self.next_id(), + hir_id: self.lower_node_id(node_id), kind: hir::ConstArgKind::TupleCall(qpath, lowered_args), span, } } ExprKind::Tup(exprs) => { let exprs = self.arena.alloc_from_iter(exprs.iter().map(|expr| { - let expr = self.lower_expr_to_const_arg_direct(&expr); + let expr = self.lower_expr_to_const_arg_direct(expr, None); &*self.arena.alloc(expr) })); - ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Tup(exprs), span } + ConstArg { + hir_id: self.lower_node_id(node_id), + kind: hir::ConstArgKind::Tup(exprs), + span, + } } ExprKind::Path(qself, path) => { let qpath = self.lower_qpath( @@ -2732,7 +2817,11 @@ impl<'hir> LoweringContext<'_, 'hir> { None, ); - ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Path(qpath), span } + ConstArg { + hir_id: self.lower_node_id(node_id), + kind: hir::ConstArgKind::Path(qpath), + span, + } } ExprKind::Struct(se) => { let path = self.lower_qpath( @@ -2754,7 +2843,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // then go unused as the `Target::ExprField` is not actually // corresponding to `Node::ExprField`. self.lower_attrs(hir_id, &f.attrs, f.span, Target::ExprField); - let expr = self.lower_expr_to_const_arg_direct(&f.expr); + let expr = self.lower_expr_to_const_arg_direct(&f.expr, None); &*self.arena.alloc(hir::ConstArgExprField { hir_id, @@ -2765,14 +2854,14 @@ impl<'hir> LoweringContext<'_, 'hir> { })); ConstArg { - hir_id: self.next_id(), + hir_id: self.lower_node_id(node_id), kind: hir::ConstArgKind::Struct(path, fields), span, } } ExprKind::Array(elements) => { let lowered_elems = self.arena.alloc_from_iter(elements.iter().map(|element| { - let const_arg = self.lower_expr_to_const_arg_direct(element); + let const_arg = self.lower_expr_to_const_arg_direct(element, None); &*self.arena.alloc(const_arg) })); let array_expr = self.arena.alloc(hir::ConstArgArrayExpr { @@ -2781,31 +2870,28 @@ impl<'hir> LoweringContext<'_, 'hir> { }); ConstArg { - hir_id: self.next_id(), + hir_id: self.lower_node_id(node_id), kind: hir::ConstArgKind::Array(array_expr), span, } } ExprKind::Underscore => ConstArg { - hir_id: self.lower_node_id(expr.id), + hir_id: self.lower_node_id(node_id), kind: hir::ConstArgKind::Infer(()), span, }, - ExprKind::Block(block, _) => { + ExprKind::Block(block, _) if let [stmt] = block.stmts.as_slice() - && let StmtKind::Expr(expr) = &stmt.kind - { - return self.lower_expr_to_const_arg_direct(expr); - } - - overly_complex_const(self) + && let StmtKind::Expr(expr) = &stmt.kind => + { + return self.lower_expr_to_const_arg_direct(expr, id_override); } ExprKind::Lit(literal) => { let span = self.lower_span(expr.span); let literal = self.lower_lit(literal, span); ConstArg { - hir_id: self.lower_node_id(expr.id), + hir_id: self.lower_node_id(node_id), kind: hir::ConstArgKind::Literal { lit: literal.node, negated: false }, span, } @@ -2816,29 +2902,45 @@ impl<'hir> LoweringContext<'_, 'hir> { let span = self.lower_span(expr.span); let literal = self.lower_lit(literal, span); - if !matches!(literal.node, LitKind::Int(..)) { + let kind = if !matches!(literal.node, LitKind::Int(..)) { let err = self.dcx().struct_span_err(expr.span, "negated literal must be an integer"); - - return ConstArg { - hir_id: self.next_id(), - kind: hir::ConstArgKind::Error(err.emit()), - span, - }; - } - - ConstArg { - hir_id: self.lower_node_id(expr.id), - kind: hir::ConstArgKind::Literal { lit: literal.node, negated: true }, - span, - } + hir::ConstArgKind::Error(err.emit()) + } else { + hir::ConstArgKind::Literal { lit: literal.node, negated: true } + }; + ConstArg { hir_id: self.lower_node_id(node_id), kind, span } } ExprKind::ConstBlock(anon_const) => { + // Do not use lower_anon_const_to_const_arg, as that attempts to represent the body + // directly. Instead, force an anon const. let def_id = self.local_def_id(anon_const.id); assert_eq!(DefKind::InlineConst, self.tcx.def_kind(def_id)); - self.lower_anon_const_to_const_arg(anon_const, span) + let lowered_anon = self.lower_anon_const_to_anon_const(anon_const, span); + ConstArg { + hir_id: self.lower_node_id(node_id), + kind: hir::ConstArgKind::Anon(lowered_anon), + span, + } + } + ExprKind::DirectConstArg(expr) => { + // `can_lower_expr_to_const_arg_direct` always returns success upon encountering a + // ExprKind::DirectConstArg, which effectively forces the expression to be lowered + // as a direct arg. If it actually turns out to not be possible, emit an error + // instead. + match self.can_lower_expr_to_const_arg_direct(expr) { + Ok(()) => self.lower_expr_to_const_arg_direct(expr, id_override), + Err(err) => err.emit(self), + } + } + _ => { + span_bug!( + expr.span, + "lower_expr_to_const_arg_direct encountered an unlowerable expression, either \ + can_lower_expr_to_const_arg_direct returned Ok() on something it shouldn't \ + have, or you forgot to check can_lower_expr_to_const_arg_direct first" + ); } - _ => overly_complex_const(self), } } @@ -2857,67 +2959,23 @@ impl<'hir> LoweringContext<'_, 'hir> { anon: &AnonConst, span: Span, ) -> hir::ConstArg<'hir> { - let tcx = self.tcx; - - // We cannot change parsing depending on feature gates available, - // we can only require feature gates to be active as a delayed check. - // Thus we just parse anon consts generally and make the real decision - // making in ast lowering. - // FIXME(min_generic_const_args): revisit once stable - if tcx.features().min_generic_const_args() { - return match anon.mgca_disambiguation { - MgcaDisambiguation::AnonConst => { - let lowered_anon = self.lower_anon_const_to_anon_const(anon, span); - ConstArg { - hir_id: self.next_id(), - kind: hir::ConstArgKind::Anon(lowered_anon), - span: lowered_anon.span, - } - } - MgcaDisambiguation::Direct => self.lower_expr_to_const_arg_direct(&anon.value), - }; - } - - // Unwrap a block, so that e.g. `{ P }` is recognised as a parameter. Const arguments - // currently have to be wrapped in curly brackets, so it's necessary to special-case. - let expr = if let ExprKind::Block(block, _) = &anon.value.kind - && let [stmt] = block.stmts.as_slice() - && let StmtKind::Expr(expr) = &stmt.kind - && let ExprKind::Path(..) = &expr.kind - { - expr - } else { + // Stable only allows one nesting of blocks for directly represented paths. mGCA allows + // arbitrarily many, and are handled inside lower_expr_to_const_arg_direct for consistency. + let expr = if self.tcx.features().min_generic_const_args() { &anon.value + } else { + anon.value.maybe_unwrap_block() }; - let maybe_res = - self.get_partial_res(expr.id).and_then(|partial_res| partial_res.full_res()); - if let ExprKind::Path(qself, path) = &expr.kind - && path.is_potential_trivial_const_arg() - && matches!(maybe_res, Some(Res::Def(DefKind::ConstParam, _))) - { - let qpath = self.lower_qpath( - expr.id, - qself, - path, - ParamMode::Explicit, - AllowReturnTypeNotation::No, - ImplTraitContext::Disallowed(ImplTraitPosition::Path), - None, - ); - - return ConstArg { - hir_id: self.lower_node_id(anon.id), - kind: hir::ConstArgKind::Path(qpath), - span: self.lower_span(expr.span), - }; + if self.can_lower_expr_to_const_arg_direct(expr).is_ok() { + return self.lower_expr_to_const_arg_direct(expr, Some(anon.id)); } let lowered_anon = self.lower_anon_const_to_anon_const(anon, anon.value.span); ConstArg { hir_id: self.next_id(), kind: hir::ConstArgKind::Anon(lowered_anon), - span: self.lower_span(expr.span), + span: self.lower_span(anon.value.span), } } @@ -3213,3 +3271,36 @@ impl<'hir> GenericArgsCtor<'hir> { this.arena.alloc(ga) } } + +#[derive(Debug)] +struct UnrepresentableConstArgError { + span: Span, + will_create_def_ids: bool, +} + +impl UnrepresentableConstArgError { + fn new(expr: &Expr) -> Self { + Self { + span: expr.span, + will_create_def_ids: expr::WillCreateDefIdsVisitor.visit_expr(expr).is_break(), + } + } + + fn emit<'hir>(self, lowering_context: &mut LoweringContext<'_, 'hir>) -> ConstArg<'hir> { + let msg = "complex const arguments must be placed inside of a `const` block"; + let e = if self.will_create_def_ids { + // FIXME(mgca): make this non-fatal once we have a better way to handle + // nested items in const args + // Issue: https://github.com/rust-lang/rust/issues/154539 + lowering_context.dcx().struct_span_fatal(self.span, msg).emit() + } else { + lowering_context.dcx().struct_span_err(self.span, msg).emit() + }; + + ConstArg { + hir_id: lowering_context.next_id(), + kind: hir::ConstArgKind::Error(e), + span: self.span, + } + } +} diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 06925994b052c..281f417500c55 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -16,6 +16,7 @@ //! constructions produced by proc macros. This pass is only intended for simple checks that do not //! require name resolution or type checking, or other kinds of complex analysis. +use std::collections::BTreeMap; use std::mem; use std::str::FromStr; @@ -34,7 +35,7 @@ use rustc_session::lint::builtin::{ DEPRECATED_WHERE_CLAUSE_LOCATION, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN, PATTERNS_IN_FNS_WITHOUT_BODY, UNUSED_VISIBILITIES, }; -use rustc_span::{Ident, Span, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; use crate::diagnostics::{self, TildeConstReason}; @@ -45,6 +46,41 @@ enum SelfSemantic { No, } +/// Is `#[splat]` allowed semantically in a function or closure? +/// Only applies to the function kind and header, the parameters are checked elsewhere. +enum SplatSemantic { + Yes, + NoClosures(Span), + NoAbiCall { span: Span, abi: Symbol }, +} + +impl SplatSemantic { + /// Returns if splatting is semantically allowed for the given `FnKind`, + /// Only checks the function kind and header, not the parameters. + fn from_fn_kind(fk: &FnKind<'_>) -> Self { + match fk { + FnKind::Fn(_, _, f) => Self::from_extern(f.sig.header.ext), + // Splatting closures is banned, because closure arguments are already de-tupled. + FnKind::Closure(_, _, _, expr) => SplatSemantic::NoClosures(expr.span), + } + } + + fn from_extern(ext: Extern) -> Self { + match ext { + Extern::None => SplatSemantic::Yes, + // FIXME(splat): should splatting extern "C" or other ABIs be allowed? + Extern::Implicit(_) => SplatSemantic::Yes, + // For now, splatting rust-call is banned, because it already de-tuples args. + Extern::Explicit(abi_str, span) => match abi_str.symbol_unescaped { + sym::rust_dash_call => { + SplatSemantic::NoAbiCall { span, abi: abi_str.symbol_unescaped } + } + _ => SplatSemantic::Yes, + }, + } + } +} + enum TraitOrImpl { Trait { vis: Span, constness: Const }, TraitImpl { constness: Const, polarity: ImplPolarity, trait_ref_span: Span }, @@ -350,10 +386,15 @@ impl<'a> AstValidator<'a> { }); } - fn check_fn_decl(&self, fn_decl: &FnDecl, self_semantic: SelfSemantic) { + fn check_fn_decl( + &self, + fn_decl: &FnDecl, + self_semantic: SelfSemantic, + splat_semantic: SplatSemantic, + ) { self.check_decl_num_args(fn_decl); let c_variadic_span = self.check_decl_cvariadic_pos(fn_decl); - self.check_decl_splatting(fn_decl, c_variadic_span); + self.check_decl_splatting(fn_decl, c_variadic_span, splat_semantic); self.check_decl_attrs(fn_decl); self.check_decl_self_param(fn_decl, self_semantic); } @@ -399,42 +440,76 @@ impl<'a> AstValidator<'a> { /// Emits an error if a function declaration has more than one splatted argument, with a /// C-variadic parameter, or a splat at an unsupported index (for performance). /// Example: `fn foo(#[splat] x: (), #[splat] y: ())` will emit an error. - fn check_decl_splatting(&self, fn_decl: &FnDecl, c_variadic_span: Option) { - let (splatted_arg_indexes, mut splatted_spans): (Vec, Vec) = fn_decl + fn check_decl_splatting( + &self, + fn_decl: &FnDecl, + c_variadic_span: Option, + splat_semantic: SplatSemantic, + ) { + let mut splatted_arg_spans: BTreeMap> = fn_decl .inputs .iter() .enumerate() .filter_map(|(index, arg)| { - arg.attrs + let splat_arg_spans: Vec = arg + .attrs .iter() - .any(|attr| attr.has_name(sym::splat)) - .then_some((u16::try_from(index).unwrap(), arg.span)) + .filter_map(|attr| attr.has_name(sym::splat).then_some(attr.span)) + .collect(); + if splat_arg_spans.is_empty() { + None + } else { + Some((u16::try_from(index).unwrap(), splat_arg_spans)) + } }) - .unzip(); + .collect(); // A splatted argument greater than or equal to the "no splatted" marker index is not - // supported. - if let (Some(&splatted_arg_index), Some(&splatted_span)) = - (splatted_arg_indexes.last(), splatted_spans.last()) - && splatted_arg_index >= u16::from(FnDecl::NO_SPLATTED_ARG_INDEX) - { - self.dcx().emit_err(diagnostics::InvalidSplattedArg { - splatted_arg_index, - span: splatted_span, + // supported. It is ok to drop these spans after issuing this error, because they are + // always invalid. + let out_of_range_spans = + splatted_arg_spans.split_off(&u16::from(FnDecl::NO_SPLATTED_ARG_INDEX)); + if !out_of_range_spans.is_empty() { + self.dcx().emit_err(diagnostics::InvalidSplattedArgs { + max_valid_splatted_arg_index: u16::from(FnDecl::MAX_VALID_SPLATTED_ARG_INDEX), + first_invalid_splatted_arg_index: *out_of_range_spans.keys().next().unwrap(), + spans: out_of_range_spans.values().flatten().copied().collect(), }); } - // Multiple splatted arguments are invalid: we can't know which arguments go in each splat. - if splatted_arg_indexes.len() > 1 { - self.dcx() - .emit_err(diagnostics::DuplicateSplattedArgs { spans: splatted_spans.clone() }); - } + if !splatted_arg_spans.is_empty() { + let splatted_spans = || splatted_arg_spans.values().flatten().copied().collect(); - if let Some(c_variadic_span) = c_variadic_span - && !splatted_spans.is_empty() - { - splatted_spans.push(c_variadic_span); - self.dcx().emit_err(diagnostics::CVarArgsAndSplat { spans: splatted_spans }); + // Multiple splatted arguments are invalid: we can't know which arguments go in each splat. + if splatted_arg_spans.len() > 1 { + self.dcx().emit_err(diagnostics::DuplicateSplattedArgs { spans: splatted_spans() }); + } + + // C-variadic parameters and splats are not allowed together. + if let Some(c_variadic_span) = c_variadic_span { + let mut splatted_spans = splatted_spans(); + splatted_spans.push(c_variadic_span); + self.dcx().emit_err(diagnostics::CVarArgsAndSplat { spans: splatted_spans }); + } + + // Splatting is not allowed on closures, or some function ABIs. + match splat_semantic { + SplatSemantic::NoClosures(closure_span) => { + let mut splatted_spans = splatted_spans(); + splatted_spans.push(closure_span); + self.dcx() + .emit_err(diagnostics::SplatNotAllowedOnClosures { spans: splatted_spans }); + } + SplatSemantic::NoAbiCall { span, abi } => { + let mut splatted_spans = splatted_spans(); + splatted_spans.push(span); + self.dcx().emit_err(diagnostics::SplatNotAllowedOnAbiCall { + spans: splatted_spans, + abi, + }); + } + SplatSemantic::Yes => {} + } } } @@ -1055,7 +1130,11 @@ impl<'a> AstValidator<'a> { match &ty.kind { TyKind::FnPtr(bfty) => { self.check_fn_ptr_safety(bfty.decl_span, bfty.safety); - self.check_fn_decl(&bfty.decl, SelfSemantic::No); + self.check_fn_decl( + &bfty.decl, + SelfSemantic::No, + SplatSemantic::from_extern(bfty.ext), + ); Self::check_decl_no_pat(&bfty.decl, |span, _, _| { self.dcx().emit_err(diagnostics::PatternFnPointer { span }); }); @@ -1746,7 +1825,8 @@ impl Visitor<'_> for AstValidator<'_> { Some(FnCtxt::Assoc(_)) => SelfSemantic::Yes, _ => SelfSemantic::No, }; - self.check_fn_decl(fk.decl(), self_semantic); + let splat_semantic = SplatSemantic::from_fn_kind(&fk); + self.check_fn_decl(fk.decl(), self_semantic, splat_semantic); if let Some(&FnHeader { safety, .. }) = fk.header() { self.check_item_safety(span, safety); diff --git a/compiler/rustc_ast_passes/src/diagnostics.rs b/compiler/rustc_ast_passes/src/diagnostics.rs index 7a78e8e6213a5..0b610431cadd8 100644 --- a/compiler/rustc_ast_passes/src/diagnostics.rs +++ b/compiler/rustc_ast_passes/src/diagnostics.rs @@ -124,18 +124,22 @@ pub(crate) struct FnParamCVarArgsNotLast { } #[derive(Diagnostic)] -#[diag("`#[splat]` is not supported on argument index {$splatted_arg_index}")] +#[diag( + "`#[splat]` is only supported on argument index {$max_valid_splatted_arg_index} or less, this `#[splat]` is on index {$first_invalid_splatted_arg_index}" +)] #[help("remove `#[splat]`, or use it on an argument closer to the start of the argument list")] -pub(crate) struct InvalidSplattedArg { - pub splatted_arg_index: u16, +pub(crate) struct InvalidSplattedArgs { + pub max_valid_splatted_arg_index: u16, + + pub first_invalid_splatted_arg_index: u16, #[primary_span] #[label("`#[splat]` is not supported here")] - pub span: Span, + pub spans: Vec, } #[derive(Diagnostic)] -#[diag("multiple `#[splat]`s are not allowed in the same function")] +#[diag("multiple `#[splat]`s are not allowed in the same function argument list")] #[help("remove `#[splat]` from all but one argument")] pub(crate) struct DuplicateSplattedArgs { #[primary_span] @@ -143,13 +147,30 @@ pub(crate) struct DuplicateSplattedArgs { } #[derive(Diagnostic)] -#[diag("`...` and `#[splat]` are not allowed in the same function")] +#[diag("`...` and `#[splat]` are not allowed in the same function argument list")] #[help("remove `#[splat]` or remove `...`")] pub(crate) struct CVarArgsAndSplat { #[primary_span] pub spans: Vec, } +#[derive(Diagnostic)] +#[diag("`#[splat]` is not allowed on closure arguments")] +#[help("remove `#[splat]` or turn the closure into a function")] +pub(crate) struct SplatNotAllowedOnClosures { + #[primary_span] + pub spans: Vec, +} + +#[derive(Diagnostic)] +#[diag("`#[splat]` is not allowed in the arguments of functions with the `{$abi}` ABI")] +#[help("remove `#[splat]` or change the ABI")] +pub(crate) struct SplatNotAllowedOnAbiCall { + #[primary_span] + pub spans: Vec, + pub abi: Symbol, +} + #[derive(Diagnostic)] #[diag("documentation comments cannot be applied to function parameters")] pub(crate) struct FnParamDocComment { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 106606877e110..bfca18a42635e 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1459,6 +1459,12 @@ impl<'a> State<'a> { self.print_type(ty); self.print_view(fields); } + ast::TyKind::DirectConstArg(expr) => { + self.word_nbsp("core::direct_const_arg!"); + self.popen(); + self.print_expr(expr, FixupContext::default()); + self.pclose(); + } } self.end(ib); } diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index 176e91e544ec5..4f3281641359d 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -883,6 +883,12 @@ impl<'a> State<'a> { self.word("/*DUMMY*/"); self.pclose(); } + ast::ExprKind::DirectConstArg(expr) => { + self.word_nbsp("core::direct_const_arg!"); + self.popen(); + self.print_expr(expr, FixupContext::default()); + self.pclose() + } } self.ann.post(self, AnnNode::Expr(expr)); diff --git a/compiler/rustc_builtin_macros/src/assert/context.rs b/compiler/rustc_builtin_macros/src/assert/context.rs index f15acc154baf3..1bc2bc8342559 100644 --- a/compiler/rustc_builtin_macros/src/assert/context.rs +++ b/compiler/rustc_builtin_macros/src/assert/context.rs @@ -323,6 +323,7 @@ impl<'cx, 'a> Context<'cx, 'a> { | ExprKind::Yeet(_) | ExprKind::Become(_) | ExprKind::Yield(_) + | ExprKind::DirectConstArg(_) | ExprKind::UnsafeBinderCast(..) => {} } } diff --git a/compiler/rustc_builtin_macros/src/autodiff.rs b/compiler/rustc_builtin_macros/src/autodiff.rs index 311a24280cfb7..bd5bf3a687d92 100644 --- a/compiler/rustc_builtin_macros/src/autodiff.rs +++ b/compiler/rustc_builtin_macros/src/autodiff.rs @@ -16,7 +16,7 @@ mod llvm_enzyme { use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemKind, BindingMode, FnRetTy, FnSig, GenericArg, GenericArgs, GenericParamKind, Generics, ItemKind, - MetaItemInner, MgcaDisambiguation, PatKind, Path, PathSegment, TyKind, Visibility, + MetaItemInner, PatKind, Path, PathSegment, TyKind, Visibility, }; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_hir::attrs::RustcAutodiff; @@ -602,11 +602,7 @@ mod llvm_enzyme { } GenericParamKind::Const { .. } => { let expr = ecx.expr_path(ast::Path::from_ident(p.ident)); - let anon_const = AnonConst { - id: ast::DUMMY_NODE_ID, - value: expr, - mgca_disambiguation: MgcaDisambiguation::Direct, - }; + let anon_const = AnonConst { id: ast::DUMMY_NODE_ID, value: expr }; Some(AngleBracketedArg::Arg(GenericArg::Const(anon_const))) } GenericParamKind::Lifetime { .. } => None, @@ -861,7 +857,6 @@ mod llvm_enzyme { let anon_const = rustc_ast::AnonConst { id: ast::DUMMY_NODE_ID, value: ecx.expr_usize(span, 1 + x.width as usize), - mgca_disambiguation: MgcaDisambiguation::Direct, }; TyKind::Array(ty.clone(), anon_const) }; @@ -876,7 +871,6 @@ mod llvm_enzyme { let anon_const = rustc_ast::AnonConst { id: ast::DUMMY_NODE_ID, value: ecx.expr_usize(span, x.width as usize), - mgca_disambiguation: MgcaDisambiguation::Direct, }; let kind = TyKind::Array(ty.clone(), anon_const); let ty = diff --git a/compiler/rustc_builtin_macros/src/direct_const_arg.rs b/compiler/rustc_builtin_macros/src/direct_const_arg.rs new file mode 100644 index 0000000000000..51c169134e03f --- /dev/null +++ b/compiler/rustc_builtin_macros/src/direct_const_arg.rs @@ -0,0 +1,39 @@ +use rustc_ast::ast; +use rustc_ast::tokenstream::TokenStream; +use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; +use rustc_span::Span; + +use crate::util::get_single_expr_from_tts; + +pub(crate) fn expand<'cx>( + cx: &'cx mut ExtCtxt<'_>, + span: Span, + tts: TokenStream, +) -> MacroExpanderResult<'cx> { + let ExpandResult::Ready(expr) = get_single_expr_from_tts(cx, span, tts, "direct_const_arg!") + else { + return ExpandResult::Retry(()); + }; + let expr = match expr { + Ok(expr) => expr, + Err(err) => return ExpandResult::Ready(DummyResult::any(span, err)), + }; + + let id = ast::DUMMY_NODE_ID; + ExpandResult::Ready(Box::new(base::MacEager { + expr: Some(Box::new(ast::Expr { + id, + kind: ast::ExprKind::DirectConstArg(expr.clone()), + span, + attrs: Default::default(), + tokens: None, + })), + ty: Some(Box::new(ast::Ty { + id, + kind: ast::TyKind::DirectConstArg(expr), + span, + tokens: None, + })), + ..Default::default() + })) +} diff --git a/compiler/rustc_builtin_macros/src/lib.rs b/compiler/rustc_builtin_macros/src/lib.rs index bd99b269ef5b6..78bf7d97bd7b8 100644 --- a/compiler/rustc_builtin_macros/src/lib.rs +++ b/compiler/rustc_builtin_macros/src/lib.rs @@ -34,6 +34,7 @@ mod define_opaque; mod derive; mod deriving; mod diagnostics; +mod direct_const_arg; mod edition_panic; mod eii; mod env; @@ -81,6 +82,7 @@ pub fn register_builtin_macros(resolver: &mut dyn ResolverExpand) { concat_bytes: concat_bytes::expand_concat_bytes, const_format_args: format::expand_format_args, core_panic: edition_panic::expand_panic, + direct_const_arg: direct_const_arg::expand, env: env::expand_env, file: source_util::expand_file, format_args: format::expand_format_args, diff --git a/compiler/rustc_builtin_macros/src/pattern_type.rs b/compiler/rustc_builtin_macros/src/pattern_type.rs index 53ab3fcd9b34b..065ba9f6a2096 100644 --- a/compiler/rustc_builtin_macros/src/pattern_type.rs +++ b/compiler/rustc_builtin_macros/src/pattern_type.rs @@ -1,5 +1,5 @@ use rustc_ast::tokenstream::TokenStream; -use rustc_ast::{AnonConst, DUMMY_NODE_ID, MgcaDisambiguation, Ty, TyPat, TyPatKind, ast, token}; +use rustc_ast::{AnonConst, DUMMY_NODE_ID, Ty, TyPat, TyPatKind, ast, token}; use rustc_errors::PResult; use rustc_expand::base::{self, DummyResult, ExpandResult, ExtCtxt, MacroExpanderResult}; use rustc_parse::exp; @@ -60,20 +60,8 @@ fn ty_pat(kind: TyPatKind, span: Span) -> TyPat { fn pat_to_ty_pat(cx: &mut ExtCtxt<'_>, pat: ast::Pat) -> TyPat { let kind = match pat.kind { ast::PatKind::Range(start, end, include_end) => TyPatKind::Range( - start.map(|value| { - Box::new(AnonConst { - id: DUMMY_NODE_ID, - value, - mgca_disambiguation: MgcaDisambiguation::Direct, - }) - }), - end.map(|value| { - Box::new(AnonConst { - id: DUMMY_NODE_ID, - value, - mgca_disambiguation: MgcaDisambiguation::Direct, - }) - }), + start.map(|value| Box::new(AnonConst { id: DUMMY_NODE_ID, value })), + end.map(|value| Box::new(AnonConst { id: DUMMY_NODE_ID, value })), include_end, ), ast::PatKind::Or(variants) => { diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index 012951098fa15..ec17e72900dfb 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -220,7 +220,7 @@ fn make_local_place<'tcx>( ); } let place = if is_ssa { - if let BackendRepr::ScalarPair(_, _) = layout.backend_repr { + if let BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } = layout.backend_repr { CPlace::new_var_pair(fx, local, layout) } else { CPlace::new_var(fx, local, layout) diff --git a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs index 612f89e6a4217..75d7e4d9fd500 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/pass_mode.rs @@ -112,7 +112,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { _ => unreachable!("{:?}", self.layout.backend_repr), }, PassMode::Pair(attrs_a, attrs_b) => match self.layout.backend_repr { - BackendRepr::ScalarPair(a, b) => { + BackendRepr::ScalarPair { a, b, b_offset: _ } => { let a = scalar_to_clif_type(tcx, a); let b = scalar_to_clif_type(tcx, b); smallvec![ @@ -167,7 +167,7 @@ impl<'tcx> ArgAbiExt<'tcx> for ArgAbi<'tcx, Ty<'tcx>> { _ => unreachable!("{:?}", self.layout.backend_repr), }, PassMode::Pair(attrs_a, attrs_b) => match self.layout.backend_repr { - BackendRepr::ScalarPair(a, b) => { + BackendRepr::ScalarPair { a, b, b_offset: _ } => { let a = scalar_to_clif_type(tcx, a); let b = scalar_to_clif_type(tcx, b); ( diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index b37fb213d945a..bf6a98866cd12 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -695,7 +695,7 @@ fn codegen_stmt<'tcx>(fx: &mut FunctionCx<'_, '_, 'tcx>, cur_block: Block, stmt: } UnOp::PtrMetadata => match layout.backend_repr { BackendRepr::Scalar(_) => CValue::zst(dest_layout), - BackendRepr::ScalarPair(_, _) => { + BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } => { CValue::by_val(operand.load_scalar_pair(fx).1, dest_layout) } _ => bug!("Unexpected `PtrToMetadata` operand: {operand:?}"), diff --git a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs index e6b8c537d8451..d4bb8bd019331 100644 --- a/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/intrinsics/mod.rs @@ -572,7 +572,9 @@ fn codegen_regular_intrinsic_call<'tcx>( let layout = fx.layout_of(generic_args.type_at(0)); // Note: Can't use is_unsized here as truly unsized types need to take the fixed size // branch - let meta = if let BackendRepr::ScalarPair(_, _) = ptr.layout().backend_repr { + let meta = if let BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } = + ptr.layout().backend_repr + { Some(ptr.load_scalar_pair(fx).1) } else { None @@ -586,7 +588,9 @@ fn codegen_regular_intrinsic_call<'tcx>( let layout = fx.layout_of(generic_args.type_at(0)); // Note: Can't use is_unsized here as truly unsized types need to take the fixed size // branch - let meta = if let BackendRepr::ScalarPair(_, _) = ptr.layout().backend_repr { + let meta = if let BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } = + ptr.layout().backend_repr + { Some(ptr.load_scalar_pair(fx).1) } else { None diff --git a/compiler/rustc_codegen_cranelift/src/value_and_place.rs b/compiler/rustc_codegen_cranelift/src/value_and_place.rs index 995a70f24240b..d7248acfafef1 100644 --- a/compiler/rustc_codegen_cranelift/src/value_and_place.rs +++ b/compiler/rustc_codegen_cranelift/src/value_and_place.rs @@ -55,8 +55,7 @@ fn codegen_field<'tcx>( } } -fn scalar_pair_calculate_b_offset(tcx: TyCtxt<'_>, a_scalar: Scalar, b_scalar: Scalar) -> Offset32 { - let b_offset = a_scalar.size(&tcx).align_to(b_scalar.default_align(&tcx).abi); +fn scalar_pair_convert_b_offset(b_offset: Size) -> Offset32 { Offset32::new(b_offset.bytes().try_into().unwrap()) } @@ -159,11 +158,11 @@ impl<'tcx> CValue<'tcx> { let layout = self.1; match self.0 { CValueInner::ByRef(ptr, None) => { - let (a_scalar, b_scalar) = match layout.backend_repr { - BackendRepr::ScalarPair(a, b) => (a, b), + let (a_scalar, b_scalar, b_offset) = match layout.backend_repr { + BackendRepr::ScalarPair { a, b, b_offset } => (a, b, b_offset), _ => unreachable!("load_scalar_pair({:?})", self), }; - let b_offset = scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar); + let b_offset = scalar_pair_convert_b_offset(b_offset); let clif_ty1 = scalar_to_clif_type(fx.tcx, a_scalar); let clif_ty2 = scalar_to_clif_type(fx.tcx, b_scalar); let mut flags = MemFlags::new(); @@ -189,7 +188,7 @@ impl<'tcx> CValue<'tcx> { match self.0 { CValueInner::ByVal(_) => unreachable!(), CValueInner::ByValPair(val1, val2) => match layout.backend_repr { - BackendRepr::ScalarPair(_, _) => { + BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } => { let val = match field.as_u32() { 0 => val1, 1 => val2, @@ -580,7 +579,7 @@ impl<'tcx> CPlace<'tcx> { } CPlaceInner::VarPair(_local, var1, var2) => { let (data1, data2) = match from.1.backend_repr { - BackendRepr::ScalarPair(_, _) => { + BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } => { CValue(from.0, dst_layout).load_scalar_pair(fx) } _ => { @@ -607,9 +606,8 @@ impl<'tcx> CPlace<'tcx> { to_ptr.store(fx, val, flags); } CValueInner::ByValPair(val1, val2) => match from.layout().backend_repr { - BackendRepr::ScalarPair(a_scalar, b_scalar) => { - let b_offset = - scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar); + BackendRepr::ScalarPair { a: _, b: _, b_offset } => { + let b_offset = scalar_pair_convert_b_offset(b_offset); to_ptr.store(fx, val1, flags); to_ptr.offset(fx, b_offset).store(fx, val2, flags); } @@ -627,9 +625,8 @@ impl<'tcx> CPlace<'tcx> { to_ptr.store(fx, val, flags); return; } - BackendRepr::ScalarPair(a_scalar, b_scalar) => { - let b_offset = - scalar_pair_calculate_b_offset(fx.tcx, a_scalar, b_scalar); + BackendRepr::ScalarPair { a: _, b: _, b_offset } => { + let b_offset = scalar_pair_convert_b_offset(b_offset); let (val1, val2) = from.load_scalar_pair(fx); to_ptr.store(fx, val1, flags); to_ptr.offset(fx, b_offset).store(fx, val2, flags); diff --git a/compiler/rustc_codegen_cranelift/src/vtable.rs b/compiler/rustc_codegen_cranelift/src/vtable.rs index b5d241d8f39f2..3310e4e3a2228 100644 --- a/compiler/rustc_codegen_cranelift/src/vtable.rs +++ b/compiler/rustc_codegen_cranelift/src/vtable.rs @@ -56,13 +56,14 @@ pub(crate) fn get_ptr_and_method_ref<'tcx>( } } - let (ptr, vtable) = if let BackendRepr::ScalarPair(_, _) = arg.layout().backend_repr { - let (ptr, vtable) = arg.load_scalar_pair(fx); - (Pointer::new(ptr), vtable) - } else { - let (ptr, vtable) = arg.try_to_ptr().unwrap(); - (ptr, vtable.unwrap()) - }; + let (ptr, vtable) = + if let BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } = arg.layout().backend_repr { + let (ptr, vtable) = arg.load_scalar_pair(fx); + (Pointer::new(ptr), vtable) + } else { + let (ptr, vtable) = arg.try_to_ptr().unwrap(); + (ptr, vtable.unwrap()) + }; let usize_size = fx.layout_of(fx.tcx.types.usize).size.bytes(); let func_ref = fx.bcx.ins().load( diff --git a/compiler/rustc_codegen_gcc/src/builder.rs b/compiler/rustc_codegen_gcc/src/builder.rs index f9358872299b7..0bef86b1ae8c1 100644 --- a/compiler/rustc_codegen_gcc/src/builder.rs +++ b/compiler/rustc_codegen_gcc/src/builder.rs @@ -1064,9 +1064,9 @@ impl<'a, 'gcc, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'gcc, 'tcx> { load }, ) - } else if let abi::BackendRepr::ScalarPair(ref a, ref b) = place.layout.backend_repr { - let b_offset = a.size(self).align_to(b.default_align(self).abi); - + } else if let abi::BackendRepr::ScalarPair { ref a, ref b, b_offset } = + place.layout.backend_repr + { let mut load = |i, scalar: &abi::Scalar, align| { let ptr = if i == 0 { place.val.llval diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 78a4c7e88c895..a5b8068e0f018 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -485,7 +485,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc let tp_ty = fn_args.type_at(0); let layout = self.layout_of(tp_ty).layout; let _use_integer_compare = match layout.backend_repr() { - Scalar(_) | ScalarPair(_, _) => true, + Scalar(_) | ScalarPair { a: _, b: _, b_offset: _ } => true, SimdVector { .. } | SimdScalableVector { .. } => false, Memory { .. } => { // For rusty ABIs, small aggregates are actually passed diff --git a/compiler/rustc_codegen_gcc/src/type_of.rs b/compiler/rustc_codegen_gcc/src/type_of.rs index 9807a84c0788d..227b513c0ff30 100644 --- a/compiler/rustc_codegen_gcc/src/type_of.rs +++ b/compiler/rustc_codegen_gcc/src/type_of.rs @@ -75,7 +75,7 @@ fn uncached_gcc_type<'gcc, 'tcx>( }; return cx.context.new_vector_type(element, count); } - BackendRepr::ScalarPair(..) => { + BackendRepr::ScalarPair { .. } => { return cx.type_struct( &[ layout.scalar_pair_element_gcc_type(cx, 0), @@ -182,13 +182,13 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } => true, // FIXME(rustc_scalable_vector): Not yet implemented in rustc_codegen_gcc. BackendRepr::SimdScalableVector { .. } => todo!(), - BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => false, + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => false, } } fn is_gcc_scalar_pair(&self) -> bool { match self.backend_repr { - BackendRepr::ScalarPair(..) => true, + BackendRepr::ScalarPair { .. } => true, BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } @@ -308,8 +308,8 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { // This must produce the same result for `repr(transparent)` wrappers as for the inner type! // In other words, this should generally not look at the type at all, but only at the // layout. - let (a, b) = match self.backend_repr { - BackendRepr::ScalarPair(ref a, ref b) => (a, b), + let (a, b, b_offset) = match self.backend_repr { + BackendRepr::ScalarPair { ref a, ref b, b_offset } => (a, b, b_offset), _ => bug!("TyAndLayout::scalar_pair_element_llty({:?}): not applicable", self), }; let scalar = [a, b][index]; @@ -325,8 +325,7 @@ impl<'tcx> LayoutGccExt<'tcx> for TyAndLayout<'tcx> { return cx.type_i1(); } - let offset = - if index == 0 { Size::ZERO } else { a.size(cx).align_to(b.default_align(cx).abi) }; + let offset = if index == 0 { Size::ZERO } else { b_offset }; self.scalar_gcc_type_at(cx, scalar, offset) } diff --git a/compiler/rustc_codegen_llvm/src/abi.rs b/compiler/rustc_codegen_llvm/src/abi.rs index bdaf72cb17ce6..65bb32ee666f2 100644 --- a/compiler/rustc_codegen_llvm/src/abi.rs +++ b/compiler/rustc_codegen_llvm/src/abi.rs @@ -551,7 +551,9 @@ impl<'ll, 'tcx> FnAbiLlvmExt<'ll, 'tcx> for FnAbi<'tcx, Ty<'tcx>> { PassMode::Pair(a, b) => { let i = apply(a); let ii = apply(b); - if let BackendRepr::ScalarPair(scalar_a, scalar_b) = arg.layout.backend_repr { + if let BackendRepr::ScalarPair { a: scalar_a, b: scalar_b, b_offset: _ } = + arg.layout.backend_repr + { apply_range_attr(llvm::AttributePlace::Argument(i), scalar_a); let primitive_b = scalar_b.primitive(); let scalar_b = if let rustc_abi::Primitive::Int(int, false) = primitive_b diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 23cf01a84f7fd..e753fff61aa41 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -793,9 +793,7 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { } }); OperandValue::Immediate(llval) - } else if let abi::BackendRepr::ScalarPair(a, b) = place.layout.backend_repr { - let b_offset = a.size(self).align_to(b.default_align(self).abi); - + } else if let abi::BackendRepr::ScalarPair { a, b, b_offset } = place.layout.backend_repr { let mut load = |i, scalar: abi::Scalar, layout, align, offset| { let llptr = if i == 0 { place.val.llval diff --git a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs index 10e152a7c8c63..b0ac5c096efdf 100644 --- a/compiler/rustc_codegen_llvm/src/builder/autodiff.rs +++ b/compiler/rustc_codegen_llvm/src/builder/autodiff.rs @@ -118,7 +118,9 @@ pub(crate) fn adjust_activity_to_abi<'tcx>( // If the argument is lowered as a `ScalarPair`, we need to duplicate its activity. // Otherwise, the number of activities won't match the number of LLVM arguments and // this will lead to errors when verifying the Enzyme call. - if let rustc_abi::BackendRepr::ScalarPair(_, _) = layout.backend_repr() { + if let rustc_abi::BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } = + layout.backend_repr() + { new_activities.push(da[i].clone()); new_positions.push(i + 1 - del_activities); } diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index bd7a4cfe38d48..54456ddea620b 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -574,7 +574,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let tp_ty = fn_args.type_at(0); let layout = self.layout_of(tp_ty).layout; let use_integer_compare = match layout.backend_repr() { - Scalar(_) | ScalarPair(_, _) => true, + Scalar(_) | ScalarPair { a: _, b: _, b_offset: _ } => true, SimdVector { .. } => false, SimdScalableVector { .. } => { let err = tcx.dcx().emit_err(InvalidMonomorphization::NonScalableType { diff --git a/compiler/rustc_codegen_llvm/src/type_of.rs b/compiler/rustc_codegen_llvm/src/type_of.rs index 6d0490e4a1f79..d1c2e1d567e40 100644 --- a/compiler/rustc_codegen_llvm/src/type_of.rs +++ b/compiler/rustc_codegen_llvm/src/type_of.rs @@ -73,7 +73,7 @@ fn uncached_llvm_type<'a, 'tcx>( _ => bug!("`#[rustc_scalable_vector]` tuple struct with too many fields"), }; } - BackendRepr::Memory { .. } | BackendRepr::ScalarPair(..) => {} + BackendRepr::Memory { .. } | BackendRepr::ScalarPair { .. } => {} } let name = match layout.ty.kind() { @@ -228,13 +228,13 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => true, - BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => false, + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => false, } } fn is_llvm_scalar_pair(&self) -> bool { match self.backend_repr { - BackendRepr::ScalarPair(..) => true, + BackendRepr::ScalarPair { .. } => true, BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } @@ -313,7 +313,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { return cx.type_i1(); } } - BackendRepr::ScalarPair(..) => { + BackendRepr::ScalarPair { .. } => { // An immediate pair always contains just the two elements, without any padding // filler, as it should never be stored to memory. return cx.type_struct( @@ -346,7 +346,7 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> { // This must produce the same result for `repr(transparent)` wrappers as for the inner type! // In other words, this should generally not look at the type at all, but only at the // layout. - let BackendRepr::ScalarPair(a, b) = self.backend_repr else { + let BackendRepr::ScalarPair { a, b, b_offset: _ } = self.backend_repr else { bug!("TyAndLayout::scalar_pair_element_llty({:?}): not applicable", self); }; let scalar = [a, b][index]; diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index b8d1cd3504517..d2583f94cf80c 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -564,7 +564,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( BackendRepr::Scalar(scalar) => { registers_for_primitive(scalar.primitive()); } - BackendRepr::ScalarPair(scalar1, scalar2) => { + BackendRepr::ScalarPair { a: scalar1, b: scalar2, b_offset: _ } => { registers_for_primitive(scalar1.primitive()); registers_for_primitive(scalar2.primitive()); } @@ -641,7 +641,7 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( } Primitive::Float(_) => bx.inbounds_ptradd(reg_save_area_v, fp_offset_v), }, - BackendRepr::ScalarPair(scalar1, scalar2) => { + BackendRepr::ScalarPair { a: scalar1, b: scalar2, b_offset: offset } => { let ty_lo = bx.cx().scalar_pair_element_backend_type(layout, 0, false); let ty_hi = bx.cx().scalar_pair_element_backend_type(layout, 1, false); @@ -665,9 +665,8 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo); let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi); - let offset = scalar1.size(bx.cx).align_to(align_hi).bytes(); let field0 = tmp; - let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset as u32)); + let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset.bytes() as u32)); bx.store(reg_lo, field0, align); bx.store(reg_hi, field1, align); @@ -688,9 +687,8 @@ fn emit_x86_64_sysv64_va_arg<'ll, 'tcx>( let reg_lo = bx.load(ty_lo, reg_lo_addr, align_lo); let reg_hi = bx.load(ty_hi, reg_hi_addr, align_hi); - let offset = scalar1.size(bx.cx).align_to(align_hi).bytes(); let field0 = tmp; - let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset as u32)); + let field1 = bx.inbounds_ptradd(tmp, bx.const_u32(offset.bytes() as u32)); bx.store(reg_lo, field0, align_lo); bx.store(reg_hi, field1, align_hi); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index 3d09b7aab8854..e56bc8ed7e82a 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -30,6 +30,7 @@ use rustc_metadata::{ walk_native_lib_search_dirs, }; use rustc_middle::bug; +use rustc_middle::error::DuplicateEiiImpls; use rustc_middle::lint::emit_lint_base; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; use rustc_middle::middle::dependency_format::Linkage; @@ -76,6 +77,64 @@ pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { } } +fn eii_impl_crate_name(crate_info: &CrateInfo, cnum: CrateNum) -> Symbol { + if cnum == LOCAL_CRATE { crate_info.local_crate_name } else { crate_info.crate_name[&cnum] } +} + +fn check_externally_implementable_item_linkage(sess: &Session, crate_info: &CrateInfo) { + if crate_info.eii_linkage.is_empty() { + return; + } + + // A crate can request multiple linked outputs with overlapping dependency + // formats, so report each underlying conflict once. + let mut emitted = FxHashSet::default(); + + // This needs the dependency formats selected for the final artifact. The + // earlier EII pass still handles missing impls and duplicate explicit impls. + for dependency_formats in crate_info.dependency_formats.values() { + for (eii_index, eii) in crate_info.eii_linkage.iter().enumerate() { + let Some(explicit_impl) = eii.impls.first() else { + continue; + }; + // If the explicit impl is already coming from a dylib, that dylib + // has already resolved the default-vs-explicit choice. + if matches!( + dependency_formats.get(explicit_impl.impl_crate), + Some(Linkage::Dynamic | Linkage::IncludedFromDylib) + ) { + continue; + } + + let Some(default_impl) = &eii.default_impl else { + continue; + }; + if !matches!( + dependency_formats.get(default_impl.impl_crate), + Some(Linkage::Dynamic | Linkage::IncludedFromDylib) + ) { + continue; + } + + if !emitted.insert(eii_index) { + continue; + } + + sess.dcx().emit_err(DuplicateEiiImpls { + name: eii.name, + first_span: explicit_impl.span, + first_crate: eii_impl_crate_name(crate_info, explicit_impl.impl_crate), + second_span: default_impl.span, + second_crate: eii_impl_crate_name(crate_info, default_impl.impl_crate), + help: (), + additional_crates: None, + num_additional_crates: 0, + additional_crate_names: String::new(), + }); + } + } +} + /// Performs the linkage portion of the compilation phase. This will generate all /// of the requested outputs for this compilation session. pub fn link_binary( @@ -91,6 +150,14 @@ pub fn link_binary( let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); let mut tempfiles_for_stdout_output: Vec = Vec::new(); let mut rmeta_link_cache = RmetaLinkCache::default(); + + if outputs.outputs.should_link() { + sess.time("check_externally_implementable_item_linkage", || { + check_externally_implementable_item_linkage(sess, &crate_info); + }); + sess.dcx().abort_if_errors(); + } + for &crate_type in &crate_info.crate_types { // Ignore executable crates if we have -Z no-codegen, as they will error. if (sess.opts.unstable_opts.no_codegen || !sess.opts.output_types.should_codegen()) diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index 8c20db5791774..e24e7bad2b945 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -1286,9 +1286,24 @@ impl<'a> Linker for EmLinker<'a> { self.cc_arg("-s"); + // Emscripten exposes the program entry point under the JS name `_main` + // regardless of the underlying wasm symbol (which is `__main_argc_argv` + // per the wasm C ABI in the tool-conventions BasicCABI spec), bridging + // the two internally. So the entry symbol must be requested as `_main` + // here rather than as a `_`-prefixed form of its wasm name. + let entry_name = self.sess.target.entry_name.as_ref(); let mut arg = OsString::from("EXPORTED_FUNCTIONS="); let encoded = serde_json::to_string( - &symbols.iter().map(|sym| "_".to_owned() + &sym.name).collect::>(), + &symbols + .iter() + .map(|sym| { + if sym.name == entry_name { + "_main".to_owned() + } else { + "_".to_owned() + &sym.name + } + }) + .collect::>(), ) .unwrap(); debug!("{encoded}"); diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 4e979df471318..c71def400f730 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -1,7 +1,7 @@ -use std::cmp; use std::collections::BTreeSet; use std::sync::Arc; use std::time::{Duration, Instant}; +use std::{cmp, iter}; use itertools::Itertools; use rustc_abi::FIRST_VARIANT; @@ -9,17 +9,17 @@ use rustc_ast::expand::allocator::{ ALLOC_ERROR_HANDLER, ALLOCATOR_METHODS, AllocatorKind, AllocatorMethod, AllocatorMethodInput, AllocatorTy, }; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet}; use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry}; use rustc_data_structures::sync::{IntoDynSyncSend, par_map}; use rustc_data_structures::unord::UnordMap; -use rustc_hir::attrs::{DebuggerVisualizerType, OptimizeAttr}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE}; +use rustc_hir::attrs::{DebuggerVisualizerType, EiiDecl, EiiImpl, OptimizeAttr}; +use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_hir::{ItemId, Target, find_attr}; use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::middle::debugger_visualizer::DebuggerVisualizerFile; -use rustc_middle::middle::dependency_format::Dependencies; +use rustc_middle::middle::dependency_format::{Dependencies, Linkage}; use rustc_middle::middle::exported_symbols::{self, SymbolExportKind}; use rustc_middle::middle::lang_items; use rustc_middle::mir::BinOp; @@ -50,7 +50,8 @@ use crate::mir::operand::OperandValue; use crate::mir::place::PlaceRef; use crate::traits::*; use crate::{ - CachedModuleCodegen, CodegenLintLevelSpecs, CrateInfo, ModuleCodegen, errors, meth, mir, + CachedModuleCodegen, CodegenLintLevelSpecs, CrateInfo, EiiLinkageImplInfo, EiiLinkageInfo, + ModuleCodegen, errors, meth, mir, }; pub(crate) fn bin_op_to_icmp_predicate(op: BinOp, signed: bool) -> IntPredicate { @@ -902,6 +903,71 @@ pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( && !tcx.should_codegen_locally(instance) } +fn collect_eii_linkage(tcx: TyCtxt<'_>) -> Vec { + #[derive(Debug)] + struct FoundImpl { + imp: EiiImpl, + impl_crate: CrateNum, + } + + #[derive(Debug)] + struct FoundEii { + decl: EiiDecl, + impls: FxIndexMap, + } + + let mut eiis = FxIndexMap::::default(); + + for &cnum in tcx.crates(()).iter().chain(iter::once(&LOCAL_CRATE)) { + for (&did, &(decl, ref impls)) in tcx.externally_implementable_items(cnum) { + eiis.entry(did) + .or_insert_with(|| FoundEii { decl, impls: Default::default() }) + .impls + .extend( + impls + .into_iter() + .map(|(&did, &imp)| (did, FoundImpl { imp, impl_crate: cnum })), + ); + } + } + + eiis.into_iter() + .filter_map(|(_, FoundEii { decl, impls })| { + let mut explicit_impls = Vec::new(); + let mut default_impl = None; + + for (impl_did, FoundImpl { imp, impl_crate }) in impls { + let impl_info = EiiLinkageImplInfo { span: tcx.def_span(impl_did), impl_crate }; + if imp.is_default { + default_impl = Some(impl_info); + } else { + explicit_impls.push(impl_info); + } + } + + // Link time check is only needed when there may be a default impl in a dylib. + // Other cases emit an error in `rustc_passes` already. + if let Some(default_impl) = default_impl { + Some(EiiLinkageInfo { + name: decl.name.name, + impls: explicit_impls, + default_impl: Some(default_impl), + }) + } else { + None + } + }) + .collect() +} + +fn eii_linkage_needed(dependency_formats: &Dependencies) -> bool { + dependency_formats.values().any(|formats| { + formats + .iter() + .any(|&linkage| matches!(linkage, Linkage::Dynamic | Linkage::IncludedFromDylib)) + }) +} + impl CrateInfo { pub fn new(tcx: TyCtxt<'_>, target_cpu: String) -> CrateInfo { let crate_types = tcx.crate_types().to_vec(); @@ -913,6 +979,12 @@ impl CrateInfo { crate_types.iter().map(|&c| (c, crate::back::linker::linked_symbols(tcx, c))).collect(); let local_crate_name = tcx.crate_name(LOCAL_CRATE); let windows_subsystem = find_attr!(tcx, crate, WindowsSubsystem(kind) => *kind); + let dependency_formats = Arc::clone(tcx.dependency_formats(())); + let eii_linkage = if eii_linkage_needed(&dependency_formats) { + collect_eii_linkage(tcx) + } else { + Vec::new() + }; // This list is used when generating the command line to pass through to // system linker. The linker expects undefined symbols on the left of the @@ -957,7 +1029,8 @@ impl CrateInfo { crate_name: UnordMap::with_capacity(n_crates), used_crates, used_crate_source: UnordMap::with_capacity(n_crates), - dependency_formats: Arc::clone(tcx.dependency_formats(())), + dependency_formats, + eii_linkage, windows_subsystem, natvis_debugger_visualizers: Default::default(), lint_level_specs: CodegenLintLevelSpecs::from_tcx(tcx), diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 200f6aac12502..14c74ab5fb69b 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -40,7 +40,7 @@ use rustc_session::Session; use rustc_session::config::{CrateType, OutputFilenames, OutputType}; use rustc_session::cstore::{self, CrateSource}; use rustc_session::lint::builtin::LINKER_MESSAGES; -use rustc_span::Symbol; +use rustc_span::{Span, Symbol}; pub mod assert_module_sources; pub mod back; @@ -256,6 +256,19 @@ impl SymbolExport { } } +#[derive(Clone, Debug, Encodable, Decodable)] +pub struct EiiLinkageImplInfo { + pub span: Span, + pub impl_crate: CrateNum, +} + +#[derive(Clone, Debug, Encodable, Decodable)] +pub struct EiiLinkageInfo { + pub name: Symbol, + pub impls: Vec, + pub default_impl: Option, +} + /// Misc info we load from metadata to persist beyond the tcx. /// /// Note: though `CrateNum` is only meaningful within the same tcx, information within `CrateInfo` @@ -282,6 +295,9 @@ pub struct CrateInfo { pub used_crate_source: UnordMap>, pub used_crates: Vec, pub dependency_formats: Arc, + /// EII implementations used by the link-time duplicate check, so `-Zno-link` can serialize the data needed by a + /// later `-Zlink-only` invocation. + pub eii_linkage: Vec, pub windows_subsystem: Option, pub natvis_debugger_visualizers: BTreeSet, pub lint_level_specs: CodegenLintLevelSpecs, diff --git a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs index 71315acc4e5db..c586b8080ef30 100644 --- a/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs +++ b/compiler/rustc_codegen_ssa/src/mir/debuginfo.rs @@ -611,7 +611,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // be marked as a `LocalVariable` for MSVC debuggers to visualize // their data correctly. (See #81894 & #88625) let var_ty_layout = self.cx.layout_of(var_ty); - if let BackendRepr::ScalarPair(_, _) = var_ty_layout.backend_repr { + if let BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } = + var_ty_layout.backend_repr + { VariableKind::LocalVariable } else { VariableKind::ArgumentVariable(arg_index) diff --git a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs index 0b00b22b1a992..9ce8e6e48bbd1 100644 --- a/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs +++ b/compiler/rustc_codegen_ssa/src/mir/naked_asm.rs @@ -441,7 +441,7 @@ fn wasm_type<'tcx>(signature: &mut String, arg_abi: &ArgAbi<'_, Ty<'tcx>>, ptr_t signature.push_str(direct_type); } PassMode::Pair(_, _) => match arg_abi.layout.backend_repr { - BackendRepr::ScalarPair(a, b) => { + BackendRepr::ScalarPair { a, b, b_offset: _ } => { signature.push_str(wasm_primitive(a.primitive(), ptr_type)); signature.push_str(", "); signature.push_str(wasm_primitive(b.primitive(), ptr_type)); diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index 0246da3f27829..f2594b712d8cd 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -168,7 +168,9 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { } ConstValue::ZeroSized => return OperandRef::zero_sized(layout), ConstValue::Slice { alloc_id, meta } => { - let BackendRepr::ScalarPair(a_scalar, _) = layout.backend_repr else { + let BackendRepr::ScalarPair { a: a_scalar, b: _, b_offset: _ } = + layout.backend_repr + else { bug!("from_const: invalid ScalarPair layout: {:#?}", layout); }; let a = Scalar::from_pointer(Pointer::new(alloc_id.into(), Size::ZERO), &bx.tcx()); @@ -222,12 +224,12 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { let val = read_scalar(offset, size, s, bx.immediate_backend_type(layout)); OperandRef { val: OperandValue::Immediate(val), layout, move_annotation: None } } - BackendRepr::ScalarPair( - a @ abi::Scalar::Initialized { .. }, - b @ abi::Scalar::Initialized { .. }, - ) => { + BackendRepr::ScalarPair { + a: a @ abi::Scalar::Initialized { .. }, + b: b @ abi::Scalar::Initialized { .. }, + b_offset, + } => { let (a_size, b_size) = (a.size(bx), b.size(bx)); - let b_offset = (offset + a_size).align_to(b.default_align(bx).abi); assert!(b_offset.bytes() > 0); let a_val = read_scalar( offset, @@ -345,7 +347,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { llval: V, layout: TyAndLayout<'tcx>, ) -> Self { - let val = if let BackendRepr::ScalarPair(..) = layout.backend_repr { + let val = if let BackendRepr::ScalarPair { .. } = layout.backend_repr { debug!("Operand::from_immediate_or_packed_pair: unpacking {:?} @ {:?}", llval, layout); // Deconstruct the immediate aggregate. @@ -383,12 +385,15 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { } else { let (in_scalar, imm) = match (self.val, self.layout.backend_repr) { // Extract a scalar component from a pair. - (OperandValue::Pair(a_llval, b_llval), BackendRepr::ScalarPair(a, b)) => { + ( + OperandValue::Pair(a_llval, b_llval), + BackendRepr::ScalarPair { a, b, b_offset }, + ) => { if offset.bytes() == 0 { assert_eq!(field.size, a.size(bx.cx())); (Some(a), a_llval) } else { - assert_eq!(offset, a.size(bx.cx()).align_to(b.default_align(bx.cx()).abi)); + assert_eq!(offset, b_offset); assert_eq!(field.size, b.size(bx.cx())); (Some(b), b_llval) } @@ -419,7 +424,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> { imm } } - BackendRepr::ScalarPair(_, _) + BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } | BackendRepr::Memory { .. } | BackendRepr::SimdScalableVector { .. } => bug!(), }) @@ -705,7 +710,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRefBuilder<'tcx, V> { let val = match layout.backend_repr { BackendRepr::Memory { .. } if layout.is_zst() => OperandValueBuilder::ZeroSized, BackendRepr::Scalar(s) => OperandValueBuilder::Immediate(Either::Right(s)), - BackendRepr::ScalarPair(a, b) => { + BackendRepr::ScalarPair { a, b, b_offset: _ } => { OperandValueBuilder::Pair(Either::Right(a), Either::Right(b)) } BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => { @@ -733,7 +738,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRefBuilder<'tcx, V> { (OperandValue::Immediate(v), BackendRepr::SimdVector { .. }) => { OperandValueBuilder::Vector(Either::Left(v)) } - (OperandValue::Pair(a, b), BackendRepr::ScalarPair(_, _)) => { + (OperandValue::Pair(a, b), BackendRepr::ScalarPair { a: _, b: _, b_offset: _ }) => { OperandValueBuilder::Pair(Either::Left(a), Either::Left(b)) } (_, BackendRepr::Memory { .. }) => { @@ -810,17 +815,18 @@ impl<'a, 'tcx, V: CodegenObject> OperandRefBuilder<'tcx, V> { bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}") } }, - (OperandValue::Pair(a, b), BackendRepr::ScalarPair(from_sa, from_sb)) => { - match &mut self.val { - OperandValueBuilder::Pair(fst @ Either::Right(_), snd @ Either::Right(_)) => { - update(fst, a, from_sa); - update(snd, b, from_sb); - } - _ => bug!( - "Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}" - ), + ( + OperandValue::Pair(a, b), + BackendRepr::ScalarPair { a: from_sa, b: from_sb, b_offset: _ }, + ) => match &mut self.val { + OperandValueBuilder::Pair(fst @ Either::Right(_), snd @ Either::Right(_)) => { + update(fst, a, from_sa); + update(snd, b, from_sb); } - } + _ => { + bug!("Tried to insert {field_operand:?} into {variant:?}.{field:?} of {self:?}") + } + }, (OperandValue::Ref(place), BackendRepr::Memory { .. }) => match &mut self.val { OperandValueBuilder::Vector(val @ Either::Right(())) => { let ibty = bx.cx().immediate_backend_type(self.layout); @@ -1008,10 +1014,10 @@ impl<'a, 'tcx, V: CodegenObject> OperandValue { bx.store_with_flags(val, dest.val.llval, dest.val.align, flags); } OperandValue::Pair(a, b) => { - let BackendRepr::ScalarPair(a_scalar, b_scalar) = dest.layout.backend_repr else { + let BackendRepr::ScalarPair { a: _, b: _, b_offset } = dest.layout.backend_repr + else { bug!("store_with_flags: invalid ScalarPair layout: {:#?}", dest.layout); }; - let b_offset = a_scalar.size(bx).align_to(b_scalar.default_align(bx).abi); let val = bx.from_immediate(a); let align = dest.val.align; diff --git a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs index 49f03fe1376e2..49b1f0a420962 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -1,5 +1,6 @@ use itertools::Itertools as _; use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT}; +use rustc_index::IndexVec; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutOf, TyAndLayout}; use rustc_middle::ty::{self, Instance, Mutability, Ty, TyCtxt}; @@ -15,6 +16,64 @@ use crate::traits::*; use crate::{MemFlags, base}; impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { + fn try_codegen_const_aggregate_as_immediate( + &mut self, + bx: &mut Bx, + dest: PlaceRef<'tcx, Bx::Value>, + kind: &mir::AggregateKind<'tcx>, + operands: &IndexVec>, + ) -> bool { + // Keep this allowlist limited to aggregate kinds with direct codegen coverage. + if !matches!(kind, mir::AggregateKind::Tuple | mir::AggregateKind::Adt(_, _, _, _, None)) { + return false; + } + if !matches!(dest.layout.fields, abi::FieldsShape::Arbitrary { .. }) { + return false; + } + if !matches!(dest.layout.variants, abi::Variants::Single { .. }) { + return false; + } + debug_assert_eq!(operands.len(), dest.layout.fields.count()); + + let size = dest.layout.size.bytes(); + let llty = match size { + 1 => bx.cx().type_i8(), + 2 => bx.cx().type_i16(), + 4 => bx.cx().type_i32(), + 8 => bx.cx().type_i64(), + 16 => bx.cx().type_i128(), + _ => return false, + }; + + let mut value = 0u128; + for (field_idx, operand) in operands.iter_enumerated() { + let field_layout = dest.layout.field(bx.cx(), field_idx.as_usize()); + if field_layout.is_zst() { + continue; + } + let mir::Operand::Constant(constant) = operand else { + return false; + }; + let Some(field_value) = self.eval_mir_constant(constant).try_to_bits(field_layout.size) + else { + return false; + }; + + let field_size = field_layout.size.bytes(); + let field_offset = dest.layout.fields.offset(field_idx.as_usize()).bytes(); + debug_assert!(field_offset + field_size <= size); + let shift = match bx.tcx().data_layout.endian { + abi::Endian::Little => field_offset * 8, + abi::Endian::Big => (size - field_offset - field_size) * 8, + }; + value |= field_value << shift; + } + + let value = bx.cx().const_uint_big(llty, value); + bx.store_to_place(value, dest.val); + true + } + #[instrument(level = "trace", skip(self, bx))] pub(crate) fn codegen_rvalue( &mut self, @@ -36,7 +95,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // semantics regarding when assignment operators allow overlap of LHS and RHS. if matches!( cg_operand.layout.backend_repr, - BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..), + BackendRepr::Scalar(..) | BackendRepr::ScalarPair { .. }, ) { debug_assert!(!matches!(cg_operand.val, OperandValue::Ref(..))); } @@ -179,6 +238,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { mir::Rvalue::Aggregate(ref kind, ref operands) if !matches!(**kind, mir::AggregateKind::RawPtr(..)) => { + if self.try_codegen_const_aggregate_as_immediate(bx, dest, kind, operands) { + return; + } + let (variant_index, variant_dest, active_field_index) = match **kind { mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => { let variant_dest = dest.project_downcast(bx, variant_index); @@ -323,9 +386,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } ( OperandValue::Pair(imm_a, imm_b), - abi::BackendRepr::ScalarPair(in_a, in_b), - abi::BackendRepr::ScalarPair(out_a, out_b), - ) if in_a.size(cx) == out_a.size(cx) && in_b.size(cx) == out_b.size(cx) => { + abi::BackendRepr::ScalarPair { a: in_a, b: in_b, b_offset: in_offset }, + abi::BackendRepr::ScalarPair { a: out_a, b: out_b, b_offset: out_offset }, + ) if in_a.size(cx) == out_a.size(cx) + && in_b.size(cx) == out_b.size(cx) + && in_offset == out_offset => + { OperandValue::Pair( transmute_scalar(bx, imm_a, in_a, out_a), transmute_scalar(bx, imm_b, in_b, out_b), diff --git a/compiler/rustc_const_eval/src/const_eval/valtrees.rs b/compiler/rustc_const_eval/src/const_eval/valtrees.rs index 1b6c948657e0d..7295df0ab2210 100644 --- a/compiler/rustc_const_eval/src/const_eval/valtrees.rs +++ b/compiler/rustc_const_eval/src/const_eval/valtrees.rs @@ -136,7 +136,7 @@ fn const_to_valtree_inner<'tcx>( let val = ecx.read_immediate(place).report_err()?; // We could allow wide raw pointers where both sides are integers in the future, // but for now we reject them. - if matches!(val.layout.backend_repr, BackendRepr::ScalarPair(..)) { + if matches!(val.layout.backend_repr, BackendRepr::ScalarPair { .. }) { Err(ValTreeCreationError::NonSupportedType(ty)) } else { let val = val.to_scalar(); diff --git a/compiler/rustc_const_eval/src/interpret/operand.rs b/compiler/rustc_const_eval/src/interpret/operand.rs index 1f67b896fc8ec..0c1e171c0edc9 100644 --- a/compiler/rustc_const_eval/src/interpret/operand.rs +++ b/compiler/rustc_const_eval/src/interpret/operand.rs @@ -84,7 +84,7 @@ impl Immediate { pub fn to_scalar(self) -> Scalar { match self { Immediate::Scalar(val) => val, - Immediate::ScalarPair(..) => bug!("Got a scalar pair where a scalar was expected"), + Immediate::ScalarPair { .. } => bug!("Got a scalar pair where a scalar was expected"), Immediate::Uninit => bug!("Got uninit where a scalar was expected"), } } @@ -129,7 +129,10 @@ impl Immediate { ); } } - (Immediate::ScalarPair(a_val, b_val), BackendRepr::ScalarPair(a, b)) => { + ( + Immediate::ScalarPair(a_val, b_val), + BackendRepr::ScalarPair { a, b, b_offset: _ }, + ) => { assert_eq!( a_val.size(), a.size(cx), @@ -263,7 +266,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { #[inline] pub fn from_scalar_pair(a: Scalar, b: Scalar, layout: TyAndLayout<'tcx>) -> Self { debug_assert!( - matches!(layout.backend_repr, BackendRepr::ScalarPair(..)), + matches!(layout.backend_repr, BackendRepr::ScalarPair { .. }), "`ImmTy::from_scalar_pair` on non-scalar-pair layout" ); let imm = Immediate::ScalarPair(a, b); @@ -276,7 +279,7 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { debug_assert!( match (imm, layout.backend_repr) { (Immediate::Scalar(..), BackendRepr::Scalar(..)) => true, - (Immediate::ScalarPair(..), BackendRepr::ScalarPair(..)) => true, + (Immediate::ScalarPair { .. }, BackendRepr::ScalarPair { .. }) => true, (Immediate::Uninit, _) if layout.is_sized() => true, _ => false, }, @@ -415,14 +418,15 @@ impl<'tcx, Prov: Provenance> ImmTy<'tcx, Prov> { **self } // extract fields from types with `ScalarPair` ABI - (Immediate::ScalarPair(a_val, b_val), BackendRepr::ScalarPair(a, b)) => { - Immediate::from(if offset.bytes() == 0 { - a_val - } else { - assert_eq!(offset, a.size(cx).align_to(b.default_align(cx).abi)); - b_val - }) - } + ( + Immediate::ScalarPair(a_val, b_val), + BackendRepr::ScalarPair { a: _, b: _, b_offset }, + ) => Immediate::from(if offset.bytes() == 0 { + a_val + } else { + assert_eq!(offset, b_offset); + b_val + }), // everything else is a bug _ => bug!( "invalid field access on immediate {} at offset {}, original layout {:#?}", @@ -606,15 +610,15 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { )?; Some(ImmTy::from_scalar(scalar, mplace.layout)) } - BackendRepr::ScalarPair( - abi::Scalar::Initialized { value: a, .. }, - abi::Scalar::Initialized { value: b, .. }, - ) => { + BackendRepr::ScalarPair { + a: abi::Scalar::Initialized { value: a, .. }, + b: abi::Scalar::Initialized { value: b, .. }, + b_offset, + } => { // We checked `ptr_align` above, so all fields will have the alignment they need. // We would anyway check against `ptr_align.restrict_for_offset(b_offset)`, // which `ptr.offset(b_offset)` cannot possibly fail to satisfy. let (a_size, b_size) = (a.size(self), b.size(self)); - let b_offset = a_size.align_to(b.default_align(self).abi); assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields let a_val = alloc.read_scalar( alloc_range(Size::ZERO, a_size), @@ -668,10 +672,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { if !matches!( op.layout().backend_repr, BackendRepr::Scalar(abi::Scalar::Initialized { .. }) - | BackendRepr::ScalarPair( - abi::Scalar::Initialized { .. }, - abi::Scalar::Initialized { .. } - ) + | BackendRepr::ScalarPair { + a: abi::Scalar::Initialized { .. }, + b: abi::Scalar::Initialized { .. }, + b_offset: _, + } ) { span_bug!(self.cur_span(), "primitive read not possible for type: {}", op.layout().ty); } diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs index d6d73b2d8da81..0c786c5831676 100644 --- a/compiler/rustc_const_eval/src/interpret/place.rs +++ b/compiler/rustc_const_eval/src/interpret/place.rs @@ -713,7 +713,6 @@ where // to handle padding properly, which is only correct if we never look at this data with the // wrong type. - let tcx = *self.tcx; let will_later_validate = M::enforce_validity(self, layout); let Some(mut alloc) = self.get_place_alloc_mut(&MPlaceTy { mplace: dest, layout })? else { // zero-sized access @@ -725,7 +724,7 @@ where alloc.write_scalar(alloc_range(Size::ZERO, scalar.size()), scalar)?; } Immediate::ScalarPair(a_val, b_val) => { - let BackendRepr::ScalarPair(_a, b) = layout.backend_repr else { + let BackendRepr::ScalarPair { a: _, b: _, b_offset } = layout.backend_repr else { span_bug!( self.cur_span(), "write_immediate_to_mplace: invalid ScalarPair layout: {:#?}", @@ -733,7 +732,7 @@ where ) }; let a_size = a_val.size(); - let b_offset = a_size.align_to(b.default_align(&tcx).abi); + let b_size = b_val.size(); assert!(b_offset.bytes() > 0); // in `operand_field` we use the offset to tell apart the fields // It is tempting to verify `b_offset` against `layout.fields.offset(1)`, @@ -744,12 +743,12 @@ where // destination now to ensure that no stray pointer fragments are being // preserved (see ). // We can skip this if there is no padding (e.g. for wide pointers). - if !will_later_validate && a_size + b_val.size() != layout.size { + if !will_later_validate && a_size + b_size != layout.size { alloc.write_uninit_full(); } alloc.write_scalar(alloc_range(Size::ZERO, a_size), a_val)?; - alloc.write_scalar(alloc_range(b_offset, b_val.size()), b_val)?; + alloc.write_scalar(alloc_range(b_offset, b_size), b_val)?; } Immediate::Uninit => alloc.write_uninit_full(), } @@ -902,17 +901,19 @@ where // padding in the target independent of layout choices. let src_has_padding = match src.layout().backend_repr { BackendRepr::Scalar(_) => false, - BackendRepr::ScalarPair(left, right) + BackendRepr::ScalarPair { a: left, b: right, b_offset: _ } if matches!(src.layout().ty.kind(), ty::Ref(..) | ty::RawPtr(..)) => { // Wide pointers never have padding, so we can avoid calling `size()`. debug_assert_eq!(left.size(self) + right.size(self), src.layout().size); false } - BackendRepr::ScalarPair(left, right) => { + BackendRepr::ScalarPair { a: left, b: right, b_offset: _ } => { let left_size = left.size(self); let right_size = right.size(self); // We have padding if the sizes don't add up to the total. + // (Why don't we need to check the offset? The scalars don't overlap so no padding + // implies `b_offset == left_size`, which would be superfluous to check explicitly.) left_size + right_size != src.layout().size } // Everything else can only exist in memory anyway, so it doesn't matter. diff --git a/compiler/rustc_const_eval/src/interpret/validity.rs b/compiler/rustc_const_eval/src/interpret/validity.rs index 262ef6ba74ed0..2d412e1124e5a 100644 --- a/compiler/rustc_const_eval/src/interpret/validity.rs +++ b/compiler/rustc_const_eval/src/interpret/validity.rs @@ -1397,7 +1397,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.path, Uninit { expected } ), - Immediate::Scalar(..) | Immediate::ScalarPair(..) => + Immediate::Scalar(..) | Immediate::ScalarPair { .. } => bug!("arrays/slices can never have Scalar/ScalarPair layout"), } }; @@ -1486,7 +1486,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, self.visit_scalar(scalar, scalar_layout)?; } } - BackendRepr::ScalarPair(a_layout, b_layout) => { + BackendRepr::ScalarPair { a: a_layout, b: b_layout, b_offset: _ } => { // We can only proceed if *both* scalars need to be initialized. // FIXME: find a way to also check ScalarPair when one side can be uninit but // the other must be init. @@ -1545,7 +1545,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValueVisitor<'tcx, M> for ValidityVisitor<'rt, .expect("the above checks should have fully handled this situation"); } } - BackendRepr::ScalarPair(a_layout, b_layout) => { + BackendRepr::ScalarPair { a: a_layout, b: b_layout, b_offset: _ } => { // We can only proceed if *both* scalars need to be initialized. // FIXME: find a way to also check ScalarPair when one side can be uninit but // the other must be init. diff --git a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs index 00835a3cc990a..64688f56fe763 100644 --- a/compiler/rustc_const_eval/src/util/check_validity_requirement.rs +++ b/compiler/rustc_const_eval/src/util/check_validity_requirement.rs @@ -121,7 +121,7 @@ fn check_validity_requirement_lax<'tcx>( let valid = !this.is_uninhabited() // definitely UB if uninhabited && match this.backend_repr { BackendRepr::Scalar(s) => scalar_allows_raw_init(s), - BackendRepr::ScalarPair(s1, s2) => { + BackendRepr::ScalarPair { a: s1, b: s2, b_offset: _ } => { scalar_allows_raw_init(s1) && scalar_allows_raw_init(s2) } BackendRepr::SimdVector { element: s, count } => count == 0 || scalar_allows_raw_init(s), diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index 01886a97f55a2..f3792d4d45235 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -2,8 +2,8 @@ use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::TokenStream; use rustc_ast::util::literal; use rustc_ast::{ - self as ast, AnonConst, AttrItem, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, - MgcaDisambiguation, PatKind, UnOp, attr, token, tokenstream, + self as ast, AnonConst, AttrItem, AttrVec, BlockCheckMode, Expr, LocalKind, MatchKind, PatKind, + UnOp, attr, token, tokenstream, }; use rustc_span::{DUMMY_SP, Ident, Span, Spanned, Symbol, kw, sym}; use thin_vec::{ThinVec, thin_vec}; @@ -100,7 +100,6 @@ impl<'a> ExtCtxt<'a> { attrs: AttrVec::new(), tokens: None, }), - mgca_disambiguation: MgcaDisambiguation::Direct, } } diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs index 811ed83e0bf48..d6a5db4c22f24 100644 --- a/compiler/rustc_hir_analysis/src/collect/generics_of.rs +++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs @@ -214,6 +214,17 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics { "synthetic HIR should have its `generics_of` explicitly fed" ), + Node::ConstArg(..) => { + // These can show up in mGCA when representing "direct" const arguments. The + // DefCollector cannot know whether an anon const will be represented by an actual HIR + // Node::AnonConst, or whether it will be represented directly, so it must generate a + // DefId. If it ends up being direct, this DefId is then attached to the top-level + // ConstArg, which is what we are seeing here. + debug_assert!(tcx.features().min_generic_const_args()); + // Forward to the real parent. + Some(tcx.local_parent(def_id)) + } + _ => span_bug!(tcx.def_span(def_id), "generics_of: unexpected node kind {node:?}"), }; diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index 58474f0ee2e38..40adfeadc4411 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -4,7 +4,7 @@ use std::debug_assert_matches; -use rustc_data_structures::fx::FxHashMap; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; use rustc_hir::{DelegationSelfTyPropagationKind, PathSegment}; @@ -21,6 +21,7 @@ type RemapTable = FxHashMap; struct ParamIndexRemapper<'tcx> { tcx: TyCtxt<'tcx>, remap_table: RemapTable, + delegation_parent_consts: FxHashSet, } impl<'tcx> TypeFolder> for ParamIndexRemapper<'tcx> { @@ -337,7 +338,7 @@ fn create_generic_args<'tcx>( delegation_id: LocalDefId, mut parent_args: &[ty::GenericArg<'tcx>], mut child_args: &[ty::GenericArg<'tcx>], -) -> Vec> { +) -> (Vec>, &'tcx [ty::GenericArg<'tcx>]) { let delegation_generics = tcx.generics_of(delegation_id); let delegation_args = ty::GenericArgs::identity_for_item(tcx, delegation_id); @@ -389,7 +390,7 @@ fn create_generic_args<'tcx>( let zero_self = zero_self.as_ref().into_iter(); let after_lifetimes_self = after_lifetimes_self.as_ref().into_iter(); - zero_self + let args = zero_self .chain(delegation_parent_args) .chain(parent_args.iter().filter(|a| a.as_region().is_some())) .chain(child_args.iter().filter(|a| a.as_region().is_some())) @@ -398,7 +399,9 @@ fn create_generic_args<'tcx>( .chain(child_args.iter().filter(|a| a.as_region().is_none())) .chain(synth_args) .copied() - .collect::>() + .collect::>(); + + (args, delegation_parent_args) } pub(crate) fn inherit_predicates_for_delegation_item<'tcx>( @@ -434,6 +437,44 @@ pub(crate) fn inherit_predicates_for_delegation_item<'tcx>( continue; } + // If we have a constant in parent or child args that came from delegation + // parent: + // ```rust + // trait Trait { /* .. */} + // impl S { + // reuse Trait::, N>::foo; + // } + // ``` + // Then if we inherit const predicate from `Trait` then we end up with + // two `ConstArgHasType` for `N` constant: + // 1) ConstArgHasType(N/#0, bool) from `Trait` + // 2) ConstArgHasType(N/#0, usize) from delegation parent + // So in case the constant came from delegation parent we will not inherit + // ConstArgHasType from signature. + // The check is so complicated because we build generic args for signature + // and predicates inheritance, for the example above it will be + // `args = [S, N/#0, S, N/#0]`, where + // args[0] - Self type, args[1] - delegation parent const, args[2] - first + // arg of callee path, args[3] - second arg of callee path. + // When processing predicate ConstArgHasType(B/#2, bool) + // from delegation signature (`Trait::foo`), we need to map `B/#2` into some + // arg from `args`. The mapping which is built by `create_mapping` function is: + // `{0: 0, 2: 3, 1: 2}`, so as `B/#2` has index `2` it is mapped into third + // arg from `args` - `N/#0`. After we obtained mapped const param, we check if + // it came from delegation parent, and if so we do not process its `ConstArgHasType` + // predicate. + // (Issue #158675). + if let ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) = + pred.0.as_predicate().fold_with(&mut self.folder).kind().skip_binder() + { + let unnorm_const = EarlyBinder::bind(self.tcx, ct).instantiate(self.tcx, args); + if let ty::ConstKind::Param(param) = unnorm_const.skip_norm_wip().kind() + && self.folder.delegation_parent_consts.contains(¶m) + { + continue; + } + } + let new_pred = pred.0.fold_with(&mut self.folder); self.preds.push(( EarlyBinder::bind(self.tcx, new_pred) @@ -497,10 +538,21 @@ fn create_folder_and_args<'tcx>( parent_args: &'tcx [ty::GenericArg<'tcx>], child_args: &'tcx [ty::GenericArg<'tcx>], ) -> (ParamIndexRemapper<'tcx>, Vec>) { - let args = create_generic_args(tcx, sig_id, def_id, parent_args, child_args); + let (args, delegation_parent_args) = + create_generic_args(tcx, sig_id, def_id, parent_args, child_args); + let remap_table = create_mapping(tcx, sig_id, def_id); - (ParamIndexRemapper { tcx, remap_table }, args) + let delegation_parent_consts = delegation_parent_args + .iter() + .filter_map(|a| { + a.as_const().and_then(|c| { + if let ty::ConstKind::Param(param) = c.kind() { Some(param) } else { None } + }) + }) + .collect(); + + (ParamIndexRemapper { tcx, remap_table, delegation_parent_consts }, args) } fn check_constraints<'tcx>( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 1b6f366b9d133..e7b25dec4db59 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -299,7 +299,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { formal_input_tys, provided_args, expected_input_tys, - c_variadic, tuple_arguments, fn_def_id, callee_generic_args, @@ -573,10 +572,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { provided_args: &'tcx [hir::Expr<'tcx>], // The expected input types from the context of the call site mut expected_input_tys: Option>>, - // Whether the function is variadic (e.g. from C) - c_variadic: bool, - // Whether all the arguments have been bundled in a tuple (ex: closures). - // Splatting is handled separately. + // Whether all the arguments have been bundled in a tuple (ex: closures), or one has been splatted tuple_arguments: TupleArgumentsFlag, // The DefId for the function being called, for better error messages fn_def_id: Option, @@ -592,6 +588,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { untupled_expected_input_tys: expected_input_tys, }; }; + let first_tupled_arg_index_usz = usize::from(first_tupled_arg_index); // The argument difference can range from -1 to u16::MAX - 1, so we count the number // of tupled arguments instead. @@ -604,28 +601,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let tupled_args_count = (1 + provided_args.len()).checked_sub(formal_input_tys.len()); debug!( ?first_tupled_arg_index, ?is_self_splatted, - ?tupled_args_count, ?tuple_arguments, ?c_variadic, + ?tupled_args_count, ?tuple_arguments, provided_args_len = ?provided_args.len(), formal_input_tys_len = ?formal_input_tys.len() ); // If earlier code has modified the FnSig argument list without adjusting the splatted // argument, indexing into the formal input types will panic. - if first_tupled_arg_index >= formal_input_tys.len() { + if first_tupled_arg_index_usz >= formal_input_tys.len() { span_bug!( call_span, "splatted argument index is out of bounds: {first_tupled_arg_index:?} >= {}, \ is_self_splatted = {is_self_splatted:?}, \ tupled_args_count = {tupled_args_count:?}, {tuple_arguments:?}, \ - c_variadic = {c_variadic:?}, provided_args: {}", + provided_args: {}", formal_input_tys.len(), provided_args.len(), ); } + let formal_input_tupled_ty = formal_input_tys[first_tupled_arg_index_usz]; // Keep the type variable if the argument is splatted, so we can force it to be a tuple later. let tuple_type = if tuple_arguments.is_splatted() { - let callee_tuple_type = - self.resolve_vars_with_obligations(formal_input_tys[first_tupled_arg_index]); + let callee_tuple_type = self.resolve_vars_with_obligations(formal_input_tupled_ty); if callee_tuple_type.is_ty_var() && let Some(tupled_args_count) = tupled_args_count { @@ -674,7 +671,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { callee_tuple_type } } else { - self.structurally_resolve_type(call_span, formal_input_tys[first_tupled_arg_index]) + self.structurally_resolve_type(call_span, formal_input_tupled_ty) }; // We expected a tuple and got a tuple (or made one ourselves). @@ -687,7 +684,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { err_code = Some(E0057); } if let Some(ref mut expected_input_tys) = expected_input_tys - && let Some(ty) = expected_input_tys.get(first_tupled_arg_index) + && let Some(ty) = expected_input_tys.get(first_tupled_arg_index_usz) && let ty::Tuple(detup_expected_arg_tys) = ty.kind() { let substitute_tys = if Some(detup_expected_arg_tys.len()) == tupled_args_count { @@ -697,26 +694,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { detup_formal_arg_tys.iter() }; - expected_input_tys - .splice(first_tupled_arg_index..=first_tupled_arg_index, substitute_tys); + expected_input_tys.splice( + first_tupled_arg_index_usz..=first_tupled_arg_index_usz, + substitute_tys, + ); } else { expected_input_tys = None; } - // If splatting, record this call in a side-table, so MIR lowering can tuple the caller's arguments - if tuple_arguments.is_splatted() { - // FIXME(const_trait_impl): does not enforce constness yet - self.write_splatted_call( - call_expr.hir_id, - call_span, - fn_def_id, - callee_generic_args, - first_tupled_arg_index.try_into().unwrap(), - tupled_args_count.unwrap().try_into().unwrap(), - ); - } - formal_input_tys.splice( - first_tupled_arg_index..=first_tupled_arg_index, + first_tupled_arg_index_usz..=first_tupled_arg_index_usz, detup_formal_arg_tys.iter(), ); if let Some(ref expected_input_tys) = expected_input_tys { @@ -724,7 +710,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { formal_input_tys.len(), expected_input_tys.len(), "incorrectly constructed input type tuples, argument counts must match: \ - tuple_arguments: {tuple_arguments:?}", + tuple_arguments: {tuple_arguments:?}, \ + first_tupled_arg_index: {first_tupled_arg_index}", ) } } @@ -751,7 +738,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let spans = if let Some(def_id) = fn_def_id && let Some(hir_node) = self.tcx.hir_get_if_local(def_id) && let Some(fn_decl) = hir_node.fn_decl() - && let Some(arg_ty) = fn_decl.inputs.get(first_tupled_arg_index) + && let Some(arg_ty) = fn_decl.inputs.get(first_tupled_arg_index_usz) { let arg_def_span = arg_ty.span; vec![call_span, arg_def_span] @@ -768,7 +755,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { tuple_type.kind(), self.structurally_resolve_type( call_span, - formal_input_tys[first_tupled_arg_index] + formal_input_tys[first_tupled_arg_index_usz] ) .kind(), ) @@ -803,6 +790,19 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { untupled_expected_input_tys: None, } } else { + // If splatting, record this call in a side-table, so MIR lowering can tuple the caller's arguments + if tuple_arguments.is_splatted() { + // FIXME(const_trait_impl): does not enforce constness yet + self.write_splatted_call( + call_expr.hir_id, + call_span, + fn_def_id, + callee_generic_args, + first_tupled_arg_index, + tupled_args_count.unwrap().try_into().unwrap(), + ); + } + TupledArgCheckOutcome { new_err_code: err_code, untupled_formal_input_tys: formal_input_tys, diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 6b5f084fc3dbe..85fdb9b6fe450 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -666,9 +666,9 @@ impl TupleArgumentsFlag { /// Returns the tupled argument index, and whether the `self` argument is splatted. /// Returns `None` if the arguments are not tupled, or if the `self` argument is splatted. - fn tupled_arg_index(self) -> (Option, bool /* is_self_splatted */) { + fn tupled_arg_index(self) -> (Option, bool /* is_self_splatted */) { match self { - Self::TupleSplattedArg(index) => (Some(usize::from(index)), false), + Self::TupleSplattedArg(index) => (Some(u16::from(index)), false), Self::TupleAllCallArgs => (Some(0), false), Self::TupleSplattedSelfArg => (None, true), Self::DontTupleArguments => (None, false), diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 383023aa24457..ea0f8f22ab187 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -1325,7 +1325,7 @@ impl<'tcx> LateLintPass<'tcx> for TypeAliasBounds { // FIXME(generic_const_exprs): Revisit this before stabilization. // See also `tests/ui/const-generics/generic_const_exprs/type-alias-bounds.rs`. let ty = cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip(); - if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) + if ty.has_type_flags(ty::TypeFlags::HAS_CONST_ALIAS) && cx.tcx.features().generic_const_exprs() { return; @@ -2438,7 +2438,7 @@ impl<'tcx> LateLintPass<'tcx> for InvalidValue { // Check if this ADT has a constrained layout (like `NonNull` and friends). if let Ok(layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(ty)) { - if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) = + if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair { a: scalar, .. } = &layout.backend_repr { let range = scalar.valid_range(cx); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 16a5a6c8877a5..6a7942a2bad29 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1108,7 +1108,7 @@ fn should_encode_mir( // instance_mir uses mir_for_ctfe rather than optimized_mir for constructors DefKind::Ctor(_, _) => (true, false), // Constants - DefKind::AnonConst { .. } + DefKind::AnonConst | DefKind::InlineConst | DefKind::AssocConst { .. } | DefKind::Const { .. } => (true, false), @@ -1438,27 +1438,11 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { // for trivial const arguments which are directly lowered to // `ConstArgKind::Path`. We never actually access this `DefId` // anywhere so we don't need to encode it for other crates. + // FIXME(mgca): This probably isn't true, they probably are accessed, but, test case? if def_kind == DefKind::AnonConst - && match tcx.hir_node_by_def_id(local_id) { - hir::Node::ConstArg(hir::ConstArg { kind, .. }) => match kind { - // Skip encoding defs for these as they should not have had a `DefId` created - hir::ConstArgKind::Error(..) - | hir::ConstArgKind::Struct(..) - | hir::ConstArgKind::Array(..) - | hir::ConstArgKind::TupleCall(..) - | hir::ConstArgKind::Tup(..) - | hir::ConstArgKind::Path(..) - | hir::ConstArgKind::Literal { .. } - | hir::ConstArgKind::Infer(..) => true, - hir::ConstArgKind::Anon(..) => false, - }, - _ => false, - } + && matches!(tcx.hir_node_by_def_id(local_id), hir::Node::ConstArg(_)) { - // MGCA doesn't have unnecessary DefIds - if !tcx.features().min_generic_const_args() { - continue; - } + continue; } if def_kind == DefKind::Field diff --git a/compiler/rustc_middle/src/error.rs b/compiler/rustc_middle/src/error.rs index ae2695987ff13..78bb57745e41f 100644 --- a/compiler/rustc_middle/src/error.rs +++ b/compiler/rustc_middle/src/error.rs @@ -169,3 +169,32 @@ pub(crate) struct IncrementCompilation { pub run_cmd: String, pub dep_node: String, } + +#[derive(Diagnostic)] +#[diag("multiple implementations of `#[{$name}]`")] +pub struct DuplicateEiiImpls { + pub name: Symbol, + + #[primary_span] + #[label("first implemented here in crate `{$first_crate}`")] + pub first_span: Span, + pub first_crate: Symbol, + + #[label("also implemented here in crate `{$second_crate}`")] + pub second_span: Span, + pub second_crate: Symbol, + + #[note("in addition to these two, { $num_additional_crates -> + [one] another implementation was found in crate {$additional_crate_names} + *[other] more implementations were also found in the following crates: {$additional_crate_names} + }")] + pub additional_crates: Option<()>, + + pub num_additional_crates: usize, + pub additional_crate_names: String, + + #[help( + "an \"externally implementable item\" can only have a single implementation in the final artifact. When multiple implementations are found, also in different crates, they conflict" + )] + pub help: (), +} diff --git a/compiler/rustc_middle/src/ty/abstract_const.rs b/compiler/rustc_middle/src/ty/abstract_const.rs index 43ed22927da56..f0692817fb60b 100644 --- a/compiler/rustc_middle/src/ty/abstract_const.rs +++ b/compiler/rustc_middle/src/ty/abstract_const.rs @@ -44,7 +44,7 @@ impl<'tcx> TyCtxt<'tcx> { self.tcx } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) { + if ty.has_type_flags(ty::TypeFlags::HAS_CONST_ALIAS) { ty.super_fold_with(self) } else { ty diff --git a/compiler/rustc_middle/src/ty/offload_meta.rs b/compiler/rustc_middle/src/ty/offload_meta.rs index 9d0fcc50ef224..a58e517e05f61 100644 --- a/compiler/rustc_middle/src/ty/offload_meta.rs +++ b/compiler/rustc_middle/src/ty/offload_meta.rs @@ -76,7 +76,7 @@ impl OffloadMetadata { Ty<'tcx>: TyAbiInterface<'tcx, C>, { match arg_abi.layout.backend_repr { - BackendRepr::ScalarPair(_, _) => (0..2) + BackendRepr::ScalarPair { a: _, b: _, b_offset: _ } => (0..2) .map(|i| { let ty = arg_abi.layout.field(cx, i).ty; (OffloadMetadata::from_ty(tcx, ty), ty) diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index c286c0819aea2..650dcf61c66ac 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -1225,26 +1225,50 @@ impl<'tcx> ThirBuildCx<'tcx> { self.typeck_results.splatted_def(expr.hir_id).unwrap_or_else(|| { span_bug!(expr.span, "no splatted def for function or method callee") }); - let def_id = def_id.unwrap_or_else(|| { - span_bug!(expr.span, "no splatted def for function or method callee") - }); - let def_kind = self.tcx.def_kind(def_id); - let user_ty = self.user_args_applied_to_res(expr.hir_id, Res::Def(def_kind, def_id)); - debug!( - "splatted_callee: user_ty={:?} def_kind={:?} def_id={:?} arg_index={:?} arg_count={:?}", - user_ty, def_kind, def_id, arg_index, arg_count - ); - ( + let expr = if let Some(def_id) = def_id { + // We're calling a function via a FnDef, and its possibly generic type + let def_kind = self.tcx.def_kind(def_id); + let user_ty = self.user_args_applied_to_res(expr.hir_id, Res::Def(def_kind, def_id)); + debug!( + "splatted_callee FnDef: user_ty={:?} def_kind={:?} def_id={:?} arg_index={:?} arg_count={:?}", + user_ty, def_kind, def_id, arg_index, arg_count, + ); + Expr { temp_scope_id: expr.hir_id.local_id, + // Create a new FnDef type, representing the splatted function arguments with + // user-supplied generic types applied ty: Ty::new_fn_def(self.tcx, def_id, self.typeck_results.node_args(expr.hir_id)), span, kind: ExprKind::ZstLiteral { user_ty }, - }, - arg_index, - arg_count, - ) + } + } else { + // We're calling a function via a FnPtr and its type + // FIXME(splat): populate the side-tables for FnPtrs, using liberated_fn_sigs if needed + let fn_ty = self.typeck_results.expr_ty_adjusted(expr); + let user_ty = + self.typeck_results.user_provided_types().get(expr.hir_id).copied().map(Box::new); + debug!( + "splatted_callee FnPtr: user_ty={:?} fn_ty={:?} arg_index={:?} arg_count={:?}", + user_ty, fn_ty, arg_index, arg_count, + ); + + if !fn_ty.is_fn() { + span_bug!(expr.span, "splatted FnPtr side-tables are not yet implemented") + } + + Expr { + temp_scope_id: expr.hir_id.local_id, + // Create a new FnPtr FnSig type, representing the splatted function arguments with + // user-supplied generic types applied + ty: Ty::new_fn_ptr(self.tcx, fn_ty.fn_sig(self.tcx)), + span, + kind: ExprKind::ZstLiteral { user_ty }, + } + }; + + (expr, arg_index, arg_count) } /// The callee has a splatted tuple argument. diff --git a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs index 8b6a92071a7d7..5a4bf1a68dd5d 100644 --- a/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs +++ b/compiler/rustc_mir_dataflow/src/impls/storage_liveness.rs @@ -155,7 +155,6 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { match &stmt.kind { StatementKind::StorageDead(l) => state.kill(*l), - // If a place is assigned to in a statement, it needs storage for that statement. StatementKind::Assign((place, _)) => { state.gen_(place.local); } @@ -180,12 +179,35 @@ impl<'tcx> Analysis<'tcx> for MaybeRequiresStorage<'_, 'tcx> { fn apply_primary_statement_effect( &self, state: &mut Self::Domain, - _: &Statement<'tcx>, + stmt: &Statement<'tcx>, loc: Location, ) { // If we move from a place then it only stops needing storage *after* // that statement. self.check_for_move(state, loc); + + match &stmt.kind { + // If a place is assigned to in a statement, it needs storage after that statement. + // Even if the place was moved from in the rvalue (e.g. `x = x + 1` or `x = f(move x)`), + // the assignment restores a valid value into the place. + StatementKind::Assign((place, _)) => { + state.gen_(place.local); + } + StatementKind::SetDiscriminant { place, .. } => { + state.gen_(place.local); + } + + StatementKind::StorageDead(_) + | StatementKind::AscribeUserType(..) + | StatementKind::PlaceMention(..) + | StatementKind::Coverage(..) + | StatementKind::FakeRead(..) + | StatementKind::ConstEvalCounter + | StatementKind::Nop + | StatementKind::Intrinsic(..) + | StatementKind::BackwardIncompatibleDropHint { .. } + | StatementKind::StorageLive(..) => {} + } } fn apply_early_terminator_effect( diff --git a/compiler/rustc_mir_transform/src/coroutine/mod.rs b/compiler/rustc_mir_transform/src/coroutine/mod.rs index 53283680c0966..a64d23b0b939f 100644 --- a/compiler/rustc_mir_transform/src/coroutine/mod.rs +++ b/compiler/rustc_mir_transform/src/coroutine/mod.rs @@ -79,6 +79,7 @@ use rustc_span::def_id::DefId; use tracing::{debug, instrument}; use crate::deref_separator::deref_finder; +use crate::patch::MirPatch; use crate::{abort_unwinding_calls, pass_manager as pm, simplify}; pub(super) struct StateTransform; @@ -199,6 +200,8 @@ struct TransformVisitor<'tcx> { old_yield_ty: Ty<'tcx>, old_ret_ty: Ty<'tcx>, + + patch: Option>, } impl<'tcx> TransformVisitor<'tcx> { @@ -408,11 +411,51 @@ impl<'tcx> MutVisitor<'tcx> for TransformVisitor<'tcx> { } #[tracing::instrument(level = "trace", skip(self), ret)] - fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, _location: Location) { + fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, location: Location) { // Replace an Local in the remap with a coroutine struct access if let Some(&Some((ty, variant_index, idx))) = self.remap.get(place.local) { replace_base(place, self.make_field(variant_index, idx, ty), self.tcx); } + if let Some(new_projection) = self.process_projection(&place.projection, location) { + place.projection = self.tcx.mk_place_elems(&new_projection); + } + } + + fn process_projection_elem( + &mut self, + elem: PlaceElem<'tcx>, + location: Location, + ) -> Option> { + match elem { + PlaceElem::Index(local) => { + if let Some(&Some((ty, variant, idx))) = self.remap.get(local) { + // `PlaceElem::Index` only accepts a `Local`, not an arbitrary `Place`. + // If the local in indexing was saved across a yield point and remapped to a + // coroutine struct field, we cannot inline the struct field access into + // the index projection. + // For example, an local storing the counter to track which element to drop in + // an array is one such case. + // + // Instead, we inject an assignment before this location to restore the + // saved local from the coroutine struct (`local = copy $projection`), + // and leave the `PlaceElem::Index(local)` projection unchanged. + let field = self.make_field(variant, idx, ty); + self.patch.as_mut().unwrap().add_assign( + location, + Place::from(local), + Rvalue::Use(Operand::Copy(field), WithRetag::No), + ); + } + None + } + PlaceElem::Field(..) + | PlaceElem::OpaqueCast(..) + | PlaceElem::UnwrapUnsafeBinder(..) + | PlaceElem::Deref + | PlaceElem::ConstantIndex { .. } + | PlaceElem::Subslice { .. } + | PlaceElem::Downcast(..) => None, + } } #[tracing::instrument(level = "trace", skip(self, stmt), ret)] @@ -1096,6 +1139,7 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { new_ret_local, old_ret_ty, old_yield_ty, + patch: Some(MirPatch::new(body)), }; transform.visit_body(body); @@ -1116,6 +1160,7 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { Some(Statement::new(source_info, assign)) }), ); + transform.patch.take().unwrap().apply(body); // Remove the context argument within generator bodies. if matches!(coroutine_kind, CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) { diff --git a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs index c7d18bd1cc92f..7e128ad72454e 100644 --- a/compiler/rustc_mir_transform/src/dataflow_const_prop.rs +++ b/compiler/rustc_mir_transform/src/dataflow_const_prop.rs @@ -647,7 +647,7 @@ impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> { // a pair and sometimes not. But as a hack we always return a pair // and just make the 2nd component `Bottom` when it does not exist. Some(val) => { - if matches!(val.layout.backend_repr, BackendRepr::ScalarPair(..)) { + if matches!(val.layout.backend_repr, BackendRepr::ScalarPair { .. }) { let (val, overflow) = val.to_scalar_pair(); (FlatSet::Elem(val), FlatSet::Elem(overflow)) } else { @@ -814,7 +814,7 @@ impl<'a, 'tcx> Collector<'a, 'tcx> { return Some(Const::Val(ConstValue::Scalar(value), ty)); } - if matches!(layout.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) { + if matches!(layout.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair { .. }) { let alloc_id = ecx .intern_with_temp_alloc(layout, |ecx, dest| { try_write_constant(ecx, dest, place, ty, state, map) diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 2db4502ecc6b6..f00dd3fde5aac 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -608,7 +608,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { let fields = fields.iter().map(|&f| self.eval_to_const(f)).collect::>>()?; let variant = if ty.ty.is_enum() { Some(variant) } else { None }; - let (BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) = ty.backend_repr + let (BackendRepr::Scalar(..) | BackendRepr::ScalarPair { .. }) = ty.backend_repr else { return None; }; @@ -639,7 +639,7 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { ImmTy::from_immediate(Immediate::Uninit, ty).into() } else if matches!( ty.backend_repr, - BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..) + BackendRepr::Scalar(..) | BackendRepr::ScalarPair { .. } ) { let dest = self.ecx.allocate(ty, MemoryKind::Stack).discard_err()?; let field_dest = self.ecx.project_field(&dest, active_field).discard_err()?; @@ -738,11 +738,15 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { s1.size(&self.ecx) == s2.size(&self.ecx) && !matches!(s1.primitive(), Primitive::Pointer(..)) } - (BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => { + ( + BackendRepr::ScalarPair { a: a1, b: b1, b_offset: b1_offset }, + BackendRepr::ScalarPair { a: a2, b: b2, b_offset: b2_offset }, + ) => { a1.size(&self.ecx) == a2.size(&self.ecx) && b1.size(&self.ecx) == b2.size(&self.ecx) - // The alignment of the second component determines its offset, so that also needs to match. - && b1.default_align(&self.ecx) == b2.default_align(&self.ecx) + // The first component is always at offset zero, but the offset to the second + // component needs to match as well for us to be able to transmute. + && b1_offset == b2_offset // None of the inputs may be a pointer. && !matches!(a1.primitive(), Primitive::Pointer(..)) && !matches!(b1.primitive(), Primitive::Pointer(..)) @@ -1804,7 +1808,9 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> { true } } - BackendRepr::ScalarPair(a, b) => { + BackendRepr::ScalarPair { a, b, b_offset: _ } => { + // The offset is irrelevant to niches since it can only cause padding, + // which can never have a niche since it's uninitialized. !a.is_always_valid(&self.ecx) || !b.is_always_valid(&self.ecx) } BackendRepr::SimdVector { .. } @@ -1902,7 +1908,10 @@ fn op_to_prop_const<'tcx>( // But we *do* want to synthesize any size constant if it is entirely uninit because that // benefits codegen, which has special handling for them. if !op.is_immediate_uninit() - && !matches!(op.layout.backend_repr, BackendRepr::Scalar(..) | BackendRepr::ScalarPair(..)) + && !matches!( + op.layout.backend_repr, + BackendRepr::Scalar(..) | BackendRepr::ScalarPair { .. } + ) { return None; } diff --git a/compiler/rustc_mir_transform/src/impossible_predicates.rs b/compiler/rustc_mir_transform/src/impossible_predicates.rs index cc216c39b8599..cf3e22212f95a 100644 --- a/compiler/rustc_mir_transform/src/impossible_predicates.rs +++ b/compiler/rustc_mir_transform/src/impossible_predicates.rs @@ -45,7 +45,7 @@ pub(crate) fn has_impossible_predicates(tcx: TyCtxt<'_>, def_id: DefId) -> bool // Only consider global clauses to simplify. TypeFlags::HAS_FREE_LOCAL_NAMES // Clauses that refer to alias constants as they cause cycles. - | TypeFlags::HAS_CT_PROJECTION, + | TypeFlags::HAS_CONST_ALIAS, ) }); let predicates: Vec<_> = traits::elaborate(tcx, predicates).collect(); diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index cd8dc963eb33c..e762821724231 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -563,7 +563,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { let right = self.use_ecx(|this| this.ecx.read_immediate(&right))?; let val = self.use_ecx(|this| this.ecx.binary_op(bin_op, &left, &right))?; - if matches!(val.layout.backend_repr, BackendRepr::ScalarPair(..)) { + if matches!(val.layout.backend_repr, BackendRepr::ScalarPair { .. }) { // FIXME `Value` should properly support pairs in `Immediate`... but currently // it does not. let (val, overflow) = val.to_pair(&self.ecx); @@ -629,7 +629,7 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> { // so bail out if the target is not one. match (value.layout.backend_repr, to.backend_repr) { (BackendRepr::Scalar(..), BackendRepr::Scalar(..)) => {} - (BackendRepr::ScalarPair(..), BackendRepr::ScalarPair(..)) => {} + (BackendRepr::ScalarPair { .. }, BackendRepr::ScalarPair { .. }) => {} _ => return None, } diff --git a/compiler/rustc_parse/src/parser/asm.rs b/compiler/rustc_parse/src/parser/asm.rs index 3fab234adaad4..11460e775d36d 100644 --- a/compiler/rustc_parse/src/parser/asm.rs +++ b/compiler/rustc_parse/src/parser/asm.rs @@ -1,4 +1,4 @@ -use rustc_ast::{self as ast, AsmMacro, MgcaDisambiguation}; +use rustc_ast::{self as ast, AsmMacro}; use rustc_span::{Span, Symbol, kw}; use super::{ExpKeywordPair, ForceCollect, IdentIsRaw, Trailing, UsePreAttrPos}; @@ -149,7 +149,7 @@ fn parse_asm_operand<'a>( let block = p.parse_block()?; ast::InlineAsmOperand::Label { block } } else if p.eat_keyword(exp!(Const)) { - let anon_const = p.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst)?; + let anon_const = p.parse_expr_anon_const()?; ast::InlineAsmOperand::Const { anon_const } } else if p.eat_keyword(exp!(Sym)) { let expr = p.parse_expr()?; diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 28e2841b9f1fe..a91ee634f3d47 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -7,7 +7,7 @@ use rustc_ast::util::parser::AssocOp; use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block, BlockCheckMode, Expr, ExprKind, GenericArg, GenericArgs, Generics, Item, ItemKind, - MgcaDisambiguation, Param, Pat, PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind, + Param, Pat, PatKind, Path, PathSegment, QSelf, Recovered, Ty, TyKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashSet; @@ -2585,11 +2585,7 @@ impl<'a> Parser<'a> { self.dcx().emit_err(UnexpectedConstParamDeclaration { span: param.span(), sugg }); let value = self.mk_expr_err(param.span(), guar); - Some(GenericArg::Const(AnonConst { - id: ast::DUMMY_NODE_ID, - value, - mgca_disambiguation: MgcaDisambiguation::Direct, - })) + Some(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })) } pub(super) fn recover_const_param_declaration( @@ -2673,11 +2669,7 @@ impl<'a> Parser<'a> { ); let guar = err.emit(); let value = self.mk_expr_err(start.to(expr.span), guar); - return Ok(GenericArg::Const(AnonConst { - id: ast::DUMMY_NODE_ID, - value, - mgca_disambiguation: MgcaDisambiguation::Direct, - })); + return Ok(GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value })); } else if snapshot.token == token::Colon && expr.span.lo() == snapshot.token.span.hi() && matches!(expr.kind, ExprKind::Path(..)) @@ -2746,11 +2738,7 @@ impl<'a> Parser<'a> { ); let guar = err.emit(); let value = self.mk_expr_err(span, guar); - GenericArg::Const(AnonConst { - id: ast::DUMMY_NODE_ID, - value, - mgca_disambiguation: MgcaDisambiguation::Direct, - }) + GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value }) } /// Some special error handling for the "top-level" patterns in a match arm, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index df1877b82cb93..e151899a57228 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -15,8 +15,8 @@ use rustc_ast::visit::{Visitor, walk_expr}; use rustc_ast::{ self as ast, AnonConst, Arm, AssignOp, AssignOpKind, AttrStyle, AttrVec, BinOp, BinOpKind, BlockCheckMode, CaptureBy, ClosureBinder, DUMMY_NODE_ID, Expr, ExprField, ExprKind, FnDecl, - FnRetTy, Guard, Label, MacCall, MetaItemLit, MgcaDisambiguation, Movability, Param, - RangeLimits, StmtKind, Ty, TyKind, UnOp, UnsafeBinderCastKind, YieldKind, + FnRetTy, Guard, Label, MacCall, MetaItemLit, Movability, Param, RangeLimits, StmtKind, Ty, + TyKind, UnOp, UnsafeBinderCastKind, YieldKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -83,15 +83,8 @@ impl<'a> Parser<'a> { ) } - pub fn parse_expr_anon_const( - &mut self, - mgca_disambiguation: impl FnOnce(&Self, &Expr) -> MgcaDisambiguation, - ) -> PResult<'a, AnonConst> { - self.parse_expr().map(|value| AnonConst { - id: DUMMY_NODE_ID, - mgca_disambiguation: mgca_disambiguation(self, &value), - value, - }) + pub fn parse_expr_anon_const(&mut self) -> PResult<'a, AnonConst> { + self.parse_expr().map(|value| AnonConst { id: DUMMY_NODE_ID, value }) } fn parse_expr_catch_underscore( @@ -1672,7 +1665,7 @@ impl<'a> Parser<'a> { let first_expr = self.parse_expr()?; if self.eat(exp!(Semi)) { // Repeating array syntax: `[ 0; 512 ]` - let count = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?; + let count = self.parse_expr_anon_const()?; self.expect(close)?; ExprKind::Repeat(first_expr, count) } else if self.eat(exp!(Comma)) { @@ -4508,6 +4501,7 @@ impl MutVisitor for CondChecker<'_> { | ExprKind::IncludedBytes(_) | ExprKind::FormatArgs(_) | ExprKind::Err(_) + | ExprKind::DirectConstArg(_) | ExprKind::Dummy => { // These would forbid any let expressions they contain already. } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 98b2ed1642582..90bfc9ece6b18 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -1696,9 +1696,9 @@ impl<'a> Parser<'a> { if self.may_recover() { self.parse_where_clause()? } else { WhereClause::default() }; let rhs = match (self.eat(exp!(Eq)), const_arg) { - (true, true) => ConstItemRhsKind::TypeConst { - rhs: Some(self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?), - }, + (true, true) => { + ConstItemRhsKind::TypeConst { rhs: Some(self.parse_expr_anon_const()?) } + } (true, false) => ConstItemRhsKind::Body { rhs: Some(self.parse_expr()?) }, (false, true) => ConstItemRhsKind::TypeConst { rhs: None }, (false, false) => ConstItemRhsKind::Body { rhs: None }, @@ -1918,11 +1918,8 @@ impl<'a> Parser<'a> { VariantData::Unit(DUMMY_NODE_ID) }; - let disr_expr = if this.eat(exp!(Eq)) { - Some(this.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst)?) - } else { - None - }; + let disr_expr = + if this.eat(exp!(Eq)) { Some(this.parse_expr_anon_const()?) } else { None }; let span = vlo.to(this.prev_token.span); if ident.name == kw::Underscore { @@ -2143,7 +2140,7 @@ impl<'a> Parser<'a> { if p.token == token::Eq { let mut snapshot = p.create_snapshot_for_diagnostic(); snapshot.bump(); - match snapshot.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst) { + match snapshot.parse_expr_anon_const() { Ok(const_expr) => { let sp = ty.span.shrink_to_hi().to(const_expr.value.span); p.psess.gated_spans.gate(sym::default_field_values, sp); @@ -2372,7 +2369,7 @@ impl<'a> Parser<'a> { } let default = if self.token == token::Eq { self.bump(); - let const_expr = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst)?; + let const_expr = self.parse_expr_anon_const()?; let sp = ty.span.shrink_to_hi().to(const_expr.value.span); self.psess.gated_spans.gate(sym::default_field_values, sp); Some(const_expr) diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 04a9b1ff212a7..8b57968413d3d 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -36,8 +36,8 @@ use rustc_ast::util::classify; use rustc_ast::{ self as ast, AnonConst, AttrArgs, AttrId, BinOpKind, ByRef, Const, CoroutineKind, DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, ImplRestriction, - MgcaDisambiguation, MutRestriction, Mutability, Recovered, RestrictionKind, Safety, StrLit, - Visibility, VisibilityKind, + MutRestriction, Mutability, Recovered, RestrictionKind, Safety, StrLit, Visibility, + VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; @@ -1298,7 +1298,6 @@ impl<'a> Parser<'a> { let anon_const = AnonConst { id: DUMMY_NODE_ID, value: self.mk_expr(blk.span, ExprKind::Block(blk, None)), - mgca_disambiguation: MgcaDisambiguation::AnonConst, }; let blk_span = anon_const.value.span; let kind = if pat { diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index ea86c08915b31..d3839579d1311 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -4,8 +4,8 @@ use ast::token::IdentIsRaw; use rustc_ast::token::{self, MetaVarKind, Token, TokenKind}; use rustc_ast::{ self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocItemConstraint, - AssocItemConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, MgcaDisambiguation, - ParenthesizedArgs, Path, PathSegment, QSelf, + AssocItemConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs, + Path, PathSegment, QSelf, }; use rustc_errors::{Applicability, Diag, PResult}; use rustc_span::{BytePos, Ident, Span, kw, sym}; @@ -876,13 +876,12 @@ impl<'a> Parser<'a> { /// the caller. pub(super) fn parse_const_arg(&mut self) -> PResult<'a, AnonConst> { // Parse const argument. - let (value, mgca_disambiguation) = if self.token.kind == token::OpenBrace { - let value = self.parse_expr_block(None, self.token.span, BlockCheckMode::Default)?; - (value, MgcaDisambiguation::Direct) + let value = if self.token.kind == token::OpenBrace { + self.parse_expr_block(None, self.token.span, BlockCheckMode::Default)? } else { self.parse_unambiguous_unbraced_const_arg()? }; - Ok(AnonConst { id: ast::DUMMY_NODE_ID, value, mgca_disambiguation }) + Ok(AnonConst { id: ast::DUMMY_NODE_ID, value }) } /// Attempt to parse a const argument that has not been enclosed in braces. @@ -892,9 +891,7 @@ impl<'a> Parser<'a> { /// - Single-segment paths (i.e. standalone generic const parameters). /// All other expressions that can be parsed will emit an error suggesting the expression be /// wrapped in braces. - pub(super) fn parse_unambiguous_unbraced_const_arg( - &mut self, - ) -> PResult<'a, (Box, MgcaDisambiguation)> { + pub(super) fn parse_unambiguous_unbraced_const_arg(&mut self) -> PResult<'a, Box> { let start = self.token.span; let attrs = self.parse_outer_attributes()?; let (expr, _) = @@ -915,7 +912,7 @@ impl<'a> Parser<'a> { }); } - Ok((expr, MgcaDisambiguation::Direct)) + Ok(expr) } /// Parse a generic argument in a path segment. @@ -1019,11 +1016,7 @@ impl<'a> Parser<'a> { GenericArg::Type(_) => GenericArg::Type(self.mk_ty(attr_span, TyKind::Err(guar))), GenericArg::Const(_) => { let error_expr = self.mk_expr(attr_span, ExprKind::Err(guar)); - GenericArg::Const(AnonConst { - id: ast::DUMMY_NODE_ID, - value: error_expr, - mgca_disambiguation: MgcaDisambiguation::Direct, - }) + GenericArg::Const(AnonConst { id: ast::DUMMY_NODE_ID, value: error_expr }) } GenericArg::Lifetime(lt) => GenericArg::Lifetime(lt), })); diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 349c8d1bea9e8..7c91c15f34034 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -2,9 +2,9 @@ use rustc_ast::token::{self, IdentIsRaw, MetaVarKind, Token, TokenKind}; use rustc_ast::util::case::Case; use rustc_ast::{ self as ast, BoundAsyncness, BoundConstness, BoundPolarity, DUMMY_NODE_ID, FnPtrTy, FnRetTy, - GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MgcaDisambiguation, - MutTy, Mutability, Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, - TraitObjectSyntax, Ty, TyKind, UnsafeBinderTy, + GenericBound, GenericBounds, GenericParam, Generics, Lifetime, MacCall, MutTy, Mutability, + Pinnedness, PolyTraitRef, PreciseCapturingArg, TraitBoundModifiers, TraitObjectSyntax, Ty, + TyKind, UnsafeBinderTy, }; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::{Applicability, Diag, E0516, PResult}; @@ -660,7 +660,7 @@ impl<'a> Parser<'a> { }; let ty = if self.eat(exp!(Semi)) { - let mut length = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct)?; + let mut length = self.parse_expr_anon_const()?; if let Err(e) = self.expect(exp!(CloseBracket)) { // Try to recover from `X` when `X::` works @@ -704,7 +704,7 @@ impl<'a> Parser<'a> { // FIXME(mgca): recovery is broken for `const {` args // we first try to parse pattern like `[u8 5]` - let length = match self.parse_expr_anon_const(|_, _| MgcaDisambiguation::Direct) { + let length = match self.parse_expr_anon_const() { Ok(length) => length, Err(e) => { e.cancel(); @@ -794,7 +794,7 @@ impl<'a> Parser<'a> { /// an error type. fn parse_typeof_ty(&mut self, lo: Span) -> PResult<'a, TyKind> { self.expect(exp!(OpenParen))?; - let _expr = self.parse_expr_anon_const(|_, _| MgcaDisambiguation::AnonConst)?; + let _expr = self.parse_expr_anon_const()?; self.expect(exp!(CloseParen))?; let span = lo.to(self.prev_token.span); let guar = self diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 7ae6005e9bc98..863e4d88872c9 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -233,8 +233,8 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::OnUnimplemented { directive } => { self.check_diagnostic_on_unimplemented(hir_id, directive.as_deref()) } - AttributeKind::OnConst { span, .. } => { - self.check_diagnostic_on_const(*span, hir_id, target, item) + AttributeKind::OnConst { span, directive } => { + self.check_diagnostic_on_const(*span, hir_id, target, item, directive.as_deref()) } AttributeKind::OnMove { directive } => { self.check_diagnostic_on_move(hir_id, directive.as_deref()) @@ -545,10 +545,36 @@ impl<'tcx> CheckAttrVisitor<'tcx> { hir_id: HirId, target: Target, item: Option>, + directive: Option<&Directive>, ) { // We only check the non-constness here. A diagnostic for use // on not-trait impl items is issued during attribute parsing. if target == (Target::Impl { of_trait: true }) { + if let Some(directive) = directive + && let Node::Item(Item { kind: ItemKind::Impl(hir::Impl { generics, .. }), .. }) = + self.tcx.hir_node(hir_id) + { + directive.visit_params(&mut |argument_name, span| { + let has_generic = generics.params.iter().any(|p| { + if !matches!(p.kind, GenericParamKind::Lifetime { .. }) + && let ParamName::Plain(name) = p.name + && name.name == argument_name + { + true + } else { + false + } + }); + if !has_generic { + self.tcx.emit_node_span_lint( + MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, + hir_id, + span, + diagnostics::OnConstMalformedFormatLiterals { name: argument_name }, + ) + } + }); + } match item.unwrap() { ItemLike::Item(it) => match it.expect_impl().constness { Constness::Const { .. } => { @@ -566,9 +592,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { ItemLike::ForeignItem => {} } } - // FIXME(#155570) Can we do something with generic args here? - // regardless, we don't check the validity of generic args here - // ...whose generics would that be, anyway? The traits' or the impls'? } /// Checks use of generic formatting parameters in `#[diagnostic::on_move]` diff --git a/compiler/rustc_passes/src/diagnostics.rs b/compiler/rustc_passes/src/diagnostics.rs index 92562cc462b87..2589941d7dae1 100644 --- a/compiler/rustc_passes/src/diagnostics.rs +++ b/compiler/rustc_passes/src/diagnostics.rs @@ -1104,35 +1104,6 @@ pub(crate) struct EiiWithoutImpl { pub help: (), } -#[derive(Diagnostic)] -#[diag("multiple implementations of `#[{$name}]`")] -pub(crate) struct DuplicateEiiImpls { - pub name: Symbol, - - #[primary_span] - #[label("first implemented here in crate `{$first_crate}`")] - pub first_span: Span, - pub first_crate: Symbol, - - #[label("also implemented here in crate `{$second_crate}`")] - pub second_span: Span, - pub second_crate: Symbol, - - #[note("in addition to these two, { $num_additional_crates -> - [one] another implementation was found in crate {$additional_crate_names} - *[other] more implementations were also found in the following crates: {$additional_crate_names} - }")] - pub additional_crates: Option<()>, - - pub num_additional_crates: usize, - pub additional_crate_names: String, - - #[help( - "an \"externally implementable item\" can only have a single implementation in the final artifact. When multiple implementations are found, also in different crates, they conflict" - )] - pub help: (), -} - #[derive(Diagnostic)] #[diag("function doesn't have a default implementation")] pub(crate) struct FunctionNotHaveDefaultImplementation { @@ -1196,6 +1167,13 @@ pub(crate) struct OnMoveMalformedFormatLiterals { pub name: Symbol, } +#[derive(Diagnostic)] +#[diag("unknown parameter `{$name}`")] +#[help(r#"expect either a generic argument name or {"`{Self}`"} as format argument"#)] +pub(crate) struct OnConstMalformedFormatLiterals { + pub name: Symbol, +} + #[derive(Diagnostic)] #[diag("unused target expression is specified for glob or list delegation")] pub(crate) struct GlobOrListDelegationUnusedTargetExpr { diff --git a/compiler/rustc_passes/src/eii.rs b/compiler/rustc_passes/src/eii.rs index c9cfd1e050313..439450fa80bd4 100644 --- a/compiler/rustc_passes/src/eii.rs +++ b/compiler/rustc_passes/src/eii.rs @@ -6,10 +6,11 @@ use std::iter; use rustc_data_structures::fx::FxIndexMap; use rustc_hir::attrs::{EiiDecl, EiiImpl}; use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc_middle::error::DuplicateEiiImpls; use rustc_middle::ty::TyCtxt; use rustc_session::config::CrateType; -use crate::diagnostics::{DuplicateEiiImpls, EiiWithoutImpl}; +use crate::diagnostics::EiiWithoutImpl; #[derive(Clone, Copy, Debug)] enum CheckingMode { diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index d2bcf0359342c..8a7104c175f39 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -660,7 +660,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { If, While, ForLoop, Loop, Match, Closure, Block, Await, Move, Use, TryBlock, Assign, AssignOp, Field, Index, Range, Underscore, Path, AddrOf, Break, Continue, Ret, InlineAsm, FormatArgs, OffsetOf, MacCall, Struct, Repeat, Paren, Try, Yield, Yeet, - Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy + Become, IncludedBytes, Gen, UnsafeBinderCast, Err, Dummy, DirectConstArg ] ); ast_visit::walk_expr(self, e) @@ -688,9 +688,10 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { ImplicitSelf, MacCall, CVarArgs, - Dummy, FieldOf, View, + DirectConstArg, + Dummy, Err ] ); diff --git a/compiler/rustc_public/src/abi.rs b/compiler/rustc_public/src/abi.rs index cde885def8a41..02674e4107c77 100644 --- a/compiler/rustc_public/src/abi.rs +++ b/compiler/rustc_public/src/abi.rs @@ -241,7 +241,11 @@ pub struct NumScalableVectors(pub(crate) u8); #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)] pub enum ValueAbi { Scalar(Scalar), - ScalarPair(Scalar, Scalar), + ScalarPair { + a: Scalar, + b: Scalar, + b_offset: Size, + }, Vector { element: Scalar, count: u64, @@ -262,7 +266,7 @@ impl ValueAbi { pub fn is_unsized(&self) -> bool { match *self { ValueAbi::Scalar(_) - | ValueAbi::ScalarPair(..) + | ValueAbi::ScalarPair { .. } | ValueAbi::Vector { .. } // FIXME(rustc_scalable_vector): Scalable vectors are `Sized` while the // `sized_hierarchy` feature is not yet fully implemented. After `sized_hierarchy` is diff --git a/compiler/rustc_public/src/unstable/convert/stable/abi.rs b/compiler/rustc_public/src/unstable/convert/stable/abi.rs index 7e0b04f8a7f61..bbc7435a6c596 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/abi.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/abi.rs @@ -271,8 +271,12 @@ impl<'tcx> Stable<'tcx> for rustc_abi::BackendRepr { ) -> Self::T { match *self { rustc_abi::BackendRepr::Scalar(scalar) => ValueAbi::Scalar(scalar.stable(tables, cx)), - rustc_abi::BackendRepr::ScalarPair(first, second) => { - ValueAbi::ScalarPair(first.stable(tables, cx), second.stable(tables, cx)) + rustc_abi::BackendRepr::ScalarPair { a: first, b: second, b_offset: second_offset } => { + ValueAbi::ScalarPair { + a: first.stable(tables, cx), + b: second.stable(tables, cx), + b_offset: second_offset.stable(tables, cx), + } } rustc_abi::BackendRepr::SimdVector { element, count } => { ValueAbi::Vector { element: element.stable(tables, cx), count } diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index de3f8c380fc44..81c6db6a9d6d7 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -425,26 +425,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { fn visit_anon_const(&mut self, constant: &'a AnonConst) { // Note that `visit_anon_const` is skipped for AnonConst nodes wrapped in an // ExprKind::ConstBlock - these are handled in visit_expr, and are DefKind::InlineConst. - - // `MgcaDisambiguation::Direct` is set even when MGCA is disabled, so - // to avoid affecting stable we have to feature gate the not creating - // anon consts - if !self.r.features.min_generic_const_args() { - let parent = self - .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) - .def_id(); - return self.with_parent(parent, |this| visit::walk_anon_const(this, constant)); - } - - match constant.mgca_disambiguation { - MgcaDisambiguation::Direct => visit::walk_anon_const(self, constant), - MgcaDisambiguation::AnonConst => { - let parent = self - .create_def(constant.id, None, DefKind::AnonConst, constant.value.span) - .def_id(); - self.with_parent(parent, |this| visit::walk_anon_const(this, constant)); - } - }; + let parent = + self.create_def(constant.id, None, DefKind::AnonConst, constant.value.span).def_id(); + self.with_parent(parent, |this| visit::walk_anon_const(this, constant)); } #[instrument(level = "debug", skip(self))] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index db2804a3f83bf..f3034dbd16794 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -822,6 +822,7 @@ symbols! { diagnostic_on_unmatched_args, dialect, direct, + direct_const_arg, discriminant_kind, discriminant_type, discriminant_value, @@ -1739,6 +1740,7 @@ symbols! { rust_analyzer, rust_begin_unwind, rust_cold_cc, + rust_dash_call: "rust-call", rust_eh_personality, rust_future, rust_logo, diff --git a/compiler/rustc_target/src/callconv/aarch64.rs b/compiler/rustc_target/src/callconv/aarch64.rs index ce69427cbdd59..ec2c30756ddc0 100644 --- a/compiler/rustc_target/src/callconv/aarch64.rs +++ b/compiler/rustc_target/src/callconv/aarch64.rs @@ -56,7 +56,7 @@ fn softfloat_float_abi(target: &Target, arg: &mut ArgAbi<'_, Ty>) { && let Primitive::Float(f) = s.primitive() { arg.cast_to(Reg { kind: RegKind::Integer, size: f.size() }); - } else if let BackendRepr::ScalarPair(s1, s2) = arg.layout.backend_repr + } else if let BackendRepr::ScalarPair { a: s1, b: s2, b_offset: _ } = arg.layout.backend_repr && (matches!(s1.primitive(), Primitive::Float(_)) || matches!(s2.primitive(), Primitive::Float(_))) { diff --git a/compiler/rustc_target/src/callconv/loongarch.rs b/compiler/rustc_target/src/callconv/loongarch.rs index 6d3826abf27a8..3e9ff2b672a07 100644 --- a/compiler/rustc_target/src/callconv/loongarch.rs +++ b/compiler/rustc_target/src/callconv/loongarch.rs @@ -89,7 +89,7 @@ where return Err(CannotUseFpConv); } BackendRepr::SimdScalableVector { .. } => panic!("scalable vectors are unsupported"), - BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => match arg_layout.fields { + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => match arg_layout.fields { FieldsShape::Primitive => { unreachable!("aggregates can't have `FieldsShape::Primitive`") } diff --git a/compiler/rustc_target/src/callconv/mod.rs b/compiler/rustc_target/src/callconv/mod.rs index 4bc88cc4f9705..54f4ff77627b9 100644 --- a/compiler/rustc_target/src/callconv/mod.rs +++ b/compiler/rustc_target/src/callconv/mod.rs @@ -390,17 +390,15 @@ impl<'a, Ty: fmt::Display> fmt::Debug for ArgAbi<'a, Ty> { impl<'a, Ty> ArgAbi<'a, Ty> { /// This defines the "default ABI" for that type, that is then later adjusted in `fn_abi_adjust_for_abi`. pub fn new( - cx: &impl HasDataLayout, layout: TyAndLayout<'a, Ty>, scalar_attrs: impl Fn(Scalar, Size) -> ArgAttributes, ) -> Self { let mode = match layout.backend_repr { _ if layout.is_zst() => PassMode::Ignore, BackendRepr::Scalar(scalar) => PassMode::Direct(scalar_attrs(scalar, Size::ZERO)), - BackendRepr::ScalarPair(a, b) => PassMode::Pair( - scalar_attrs(a, Size::ZERO), - scalar_attrs(b, a.size(cx).align_to(b.default_align(cx).abi)), - ), + BackendRepr::ScalarPair { a, b, b_offset } => { + PassMode::Pair(scalar_attrs(a, Size::ZERO), scalar_attrs(b, b_offset)) + } BackendRepr::SimdVector { .. } => PassMode::Direct(ArgAttributes::new()), BackendRepr::Memory { .. } => Self::indirect_pass_mode(&layout), BackendRepr::SimdScalableVector { .. } => PassMode::Direct(ArgAttributes::new()), @@ -877,7 +875,7 @@ where { match layout.backend_repr { BackendRepr::Scalar(scalar) => !scalar.is_uninit_valid(), - BackendRepr::ScalarPair(s1, s2) => { + BackendRepr::ScalarPair { a: s1, b: s2, b_offset: _ } => { !s1.is_uninit_valid() && !s2.is_uninit_valid() // Ensure there is no padding. diff --git a/compiler/rustc_target/src/callconv/riscv.rs b/compiler/rustc_target/src/callconv/riscv.rs index bc81ec95c86e3..e79ce0557ce80 100644 --- a/compiler/rustc_target/src/callconv/riscv.rs +++ b/compiler/rustc_target/src/callconv/riscv.rs @@ -94,7 +94,7 @@ where BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => { return Err(CannotUseFpConv); } - BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => match arg_layout.fields { + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => match arg_layout.fields { FieldsShape::Primitive => { unreachable!("aggregates can't have `FieldsShape::Primitive`") } diff --git a/compiler/rustc_target/src/callconv/sparc64.rs b/compiler/rustc_target/src/callconv/sparc64.rs index b5e5c3e2a88b3..6b19f8ebd76ce 100644 --- a/compiler/rustc_target/src/callconv/sparc64.rs +++ b/compiler/rustc_target/src/callconv/sparc64.rs @@ -67,7 +67,7 @@ fn classify<'a, Ty, C>( }, BackendRepr::SimdVector { .. } => {} BackendRepr::SimdScalableVector { .. } => {} - BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => match arg_layout.fields { + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => match arg_layout.fields { FieldsShape::Primitive => { unreachable!("aggregates can't have `FieldsShape::Primitive`") } diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index 81ff1a2a45900..a80088e41cd31 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -92,7 +92,7 @@ where Ty: TyAbiInterface<'a, C> + Copy, { match layout.backend_repr { - BackendRepr::Scalar(_) | BackendRepr::ScalarPair(..) => false, + BackendRepr::Scalar(_) | BackendRepr::ScalarPair { .. } => false, BackendRepr::SimdVector { .. } => true, BackendRepr::Memory { .. } => { for i in 0..layout.fields.count() { @@ -211,7 +211,7 @@ where if !fn_abi.ret.is_ignore() { let has_float = match fn_abi.ret.layout.backend_repr { BackendRepr::Scalar(s) => matches!(s.primitive(), Primitive::Float(_)), - BackendRepr::ScalarPair(s1, s2) => { + BackendRepr::ScalarPair { a: s1, b: s2, b_offset: _ } => { matches!(s1.primitive(), Primitive::Float(_)) || matches!(s2.primitive(), Primitive::Float(_)) } diff --git a/compiler/rustc_target/src/callconv/x86_64.rs b/compiler/rustc_target/src/callconv/x86_64.rs index 3055d18ffa014..5ab2834807d67 100644 --- a/compiler/rustc_target/src/callconv/x86_64.rs +++ b/compiler/rustc_target/src/callconv/x86_64.rs @@ -61,7 +61,7 @@ where BackendRepr::SimdScalableVector { .. } => panic!("scalable vectors are unsupported"), - BackendRepr::ScalarPair(..) | BackendRepr::Memory { .. } => { + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { .. } => { for i in 0..layout.fields.count() { let field_off = off + layout.fields.offset(i); classify(cx, layout.field(cx, i), cls, field_off)?; diff --git a/compiler/rustc_target/src/callconv/x86_win64.rs b/compiler/rustc_target/src/callconv/x86_win64.rs index cece9d032b53a..4aaf34e56ce4b 100644 --- a/compiler/rustc_target/src/callconv/x86_win64.rs +++ b/compiler/rustc_target/src/callconv/x86_win64.rs @@ -12,7 +12,7 @@ where let fixup = |a: &mut ArgAbi<'_, Ty>, is_ret: bool| { match a.layout.backend_repr { BackendRepr::Memory { sized: false } => {} - BackendRepr::ScalarPair(..) | BackendRepr::Memory { sized: true } => { + BackendRepr::ScalarPair { .. } | BackendRepr::Memory { sized: true } => { match a.layout.size.bits() { 8 => a.cast_to(Reg::i8()), 16 => a.cast_to(Reg::i16()), diff --git a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs index 2104286ec8684..bda2681605814 100644 --- a/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs +++ b/compiler/rustc_target/src/spec/targets/wasm32_unknown_emscripten.rs @@ -29,6 +29,11 @@ pub(crate) fn target() -> Target { crt_static_default: true, crt_static_allows_dylibs: true, main_needs_argc_argv: true, + // Use the wasm C-ABI entry name from the tool-conventions BasicCABI + // spec rather than a raw `main`, as referenced by emscripten's crt/libc. + // Required for entry paths like `-sPROXY_TO_PTHREAD`, whose + // `crt1_proxy_main` links against `__main_argc_argv`. + entry_name: "__main_argc_argv".into(), panic_strategy: PanicStrategy::Unwind, no_default_libraries: false, families: cvs!["unix", "wasm"], diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 617ec6c4ac229..b38c933151eb2 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -29,8 +29,8 @@ use rustc_middle::ty::print::{ PrintTraitRefExt as _, with_forced_trimmed_paths, }; use rustc_middle::ty::{ - self, GenericArgKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, - TypeVisitableExt, Unnormalized, Upcast, + self, GenericArgKind, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable, TypeFolder, + TypeSuperFoldable, TypeVisitableExt, Unnormalized, Upcast, }; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::CrateNum; @@ -916,11 +916,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { find_attr!(self.tcx, impl_did, OnConst {directive, ..} => directive.as_deref()) .flatten() { - let (_, format_args) = self.on_unimplemented_components( + let (_, mut format_args) = self.on_unimplemented_components( trait_ref, main_obligation, diag.long_ty_path(), + false, ); + if let ty::Adt(def, args) = trait_ref.self_ty().skip_binder().kind() { + for param in self.tcx.generics_of(def.did()).own_params.iter() { + match param.kind { + GenericParamDefKind::Type { .. } + | GenericParamDefKind::Const { .. } => { + format_args + .generic_args + .push((param.name, args[param.index as usize].to_string())); + } + _ => continue, + } + } + } let CustomDiagnostic { message, label, notes, parent_label: _ } = command.eval(None, &format_args); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index 138c53d013ebc..f7e4ec6164c99 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -46,7 +46,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { return CustomDiagnostic::default(); } let (filter_options, format_args) = - self.on_unimplemented_components(trait_pred, obligation, long_ty_path); + self.on_unimplemented_components(trait_pred, obligation, long_ty_path, true); if let Some(command) = find_attr!(self.tcx, trait_pred.def_id(), OnUnimplemented {directive, ..} => directive.as_deref()).flatten() { command.eval( Some(&filter_options), @@ -62,6 +62,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { trait_pred: ty::PolyTraitPredicate<'tcx>, obligation: &PredicateObligation<'tcx>, long_ty_path: &mut Option, + print_infer_ty_var: bool, ) -> (FilterOptions, FormatArgs) { let (def_id, args) = (trait_pred.def_id(), trait_pred.skip_binder().trait_ref.args); let trait_pred = trait_pred.skip_binder(); @@ -244,7 +245,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { if let Some(ty) = trait_pred.trait_ref.args[param.index as usize].as_type() { - self.tcx.short_string(ty, long_ty_path) + if print_infer_ty_var == false + && let ty::Infer(ty::TyVar(_)) = ty.kind() + { + format!("{}", param.name) + } else { + self.tcx.short_string(ty, long_ty_path) + } } else { trait_pred.trait_ref.args[param.index as usize].to_string() } diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index e8371a3c0f257..ac6100747970e 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -376,7 +376,7 @@ impl<'a, 'tcx> QueryNormalizer<'a, 'tcx> { // of type/const and we need to continue folding it to reveal the TAIT behind it // or further normalize nested alias consts. if res != term.to_term(tcx, ty::IsRigid::No) - && (res.has_type_flags(ty::TypeFlags::HAS_CT_PROJECTION) + && (res.has_type_flags(ty::TypeFlags::HAS_CONST_ALIAS) || matches!( term.kind, ty::AliasTermKind::FreeTy { .. } | ty::AliasTermKind::FreeConst { .. } diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 51db1b8efde2f..8873fb13efe03 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -486,7 +486,7 @@ fn fn_abi_sanity_check<'tcx>( BackendRepr::Scalar(_) | BackendRepr::SimdVector { .. } | BackendRepr::SimdScalableVector { .. } => {} - BackendRepr::ScalarPair(..) => { + BackendRepr::ScalarPair { .. } => { panic!("`PassMode::Direct` used for ScalarPair type {}", arg.layout.ty) } BackendRepr::Memory { sized } => { @@ -513,7 +513,7 @@ fn fn_abi_sanity_check<'tcx>( // Similar to `Direct`, we need to make sure that backends use `layout.backend_repr` // and ignore the rest of the layout. assert!( - matches!(arg.layout.backend_repr, BackendRepr::ScalarPair(..)), + matches!(arg.layout.backend_repr, BackendRepr::ScalarPair { .. }), "PassMode::Pair for type {}", arg.layout.ty ); @@ -612,7 +612,7 @@ fn fn_abi_new_uncached<'tcx>( layout }; - Ok(ArgAbi::new(cx, layout, |scalar, offset| { + Ok(ArgAbi::new(layout, |scalar, offset| { arg_attrs_for_rust_scalar(*cx, scalar, layout, offset, is_return, determined_fn_def_id) })) }; @@ -741,7 +741,7 @@ fn make_thin_self_ptr<'tcx>( Ty::new_mut_ptr(tcx, layout.ty) } else { match layout.backend_repr { - BackendRepr::ScalarPair(..) | BackendRepr::Scalar(..) => (), + BackendRepr::ScalarPair { .. } | BackendRepr::Scalar(..) => (), _ => bug!("receiver type has unsupported layout: {:?}", layout), } diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index be0042f697e5d..0defe2e0ac38f 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -291,7 +291,8 @@ fn layout_of_uncached<'tcx>( } } ty::PatternKind::NotNull => { - if let BackendRepr::Scalar(scalar) | BackendRepr::ScalarPair(scalar, _) = + if let BackendRepr::Scalar(scalar) + | BackendRepr::ScalarPair { a: scalar, b: _, b_offset: _ } = &mut layout.backend_repr { scalar.valid_range_mut().start = 1; diff --git a/compiler/rustc_ty_utils/src/layout/invariant.rs b/compiler/rustc_ty_utils/src/layout/invariant.rs index decf1ffb5570d..70e980490f36b 100644 --- a/compiler/rustc_ty_utils/src/layout/invariant.rs +++ b/compiler/rustc_ty_utils/src/layout/invariant.rs @@ -158,15 +158,21 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou } } } - BackendRepr::ScalarPair(scalar1, scalar2) => { + BackendRepr::ScalarPair { a: scalar1, b: scalar2, b_offset } => { // Check that the underlying pair of fields matches. let inner = skip_newtypes(cx, layout); assert!( - matches!(inner.layout.backend_repr(), BackendRepr::ScalarPair(..)), + matches!(inner.layout.backend_repr(), BackendRepr::ScalarPair { .. }), "`ScalarPair` type {} is newtype around non-`ScalarPair` type {}", layout.ty, inner.ty ); + // `a` is at memory offset zero, so to keep them from overlapping the offset + // to `b` must be at least as much as the size of `a`. + assert!( + b_offset >= scalar1.size(cx), + "`ScalarPair` scalars are overlapping in {layout:?}", + ); if matches!(inner.layout.variants(), Variants::Multiple { .. }) { // FIXME: ScalarPair for enums is enormously complicated and it is very hard // to check anything about them. @@ -234,6 +240,10 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou offset2, field2_offset, "`ScalarPair` second field at bad offset in {inner:#?}", ); + assert_eq!( + b_offset, field2_offset, + "`ScalarPair` with inconsistent b_offset in {inner:#?}", + ); assert_eq!( field2.size, size2, "`ScalarPair` second field with bad size in {inner:#?}", @@ -325,8 +335,11 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou }; let abi_coherent = match (layout.backend_repr, variant.backend_repr) { (BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => scalar_coherent(s1, s2), - (BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => { - scalar_coherent(a1, a2) && scalar_coherent(b1, b2) + ( + BackendRepr::ScalarPair { a: a1, b: b1, b_offset: b1_offset }, + BackendRepr::ScalarPair { a: a2, b: b2, b_offset: b2_offset }, + ) => { + scalar_coherent(a1, a2) && scalar_coherent(b1, b2) && b1_offset == b2_offset } (BackendRepr::Memory { .. }, _) => true, _ => false, diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 03f90215ad1aa..214a83a9b491b 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -80,7 +80,7 @@ bitflags::bitflags! { /// Does this have `Inherent`? const HAS_TY_INHERENT = 1 << 13; /// Does this have `ConstKind::Alias`? - const HAS_CT_PROJECTION = 1 << 14; + const HAS_CONST_ALIAS = 1 << 14; /// Does this have `Alias` or `ConstKind::Alias`? /// @@ -89,7 +89,7 @@ bitflags::bitflags! { | TypeFlags::HAS_TY_FREE_ALIAS.bits() | TypeFlags::HAS_TY_OPAQUE.bits() | TypeFlags::HAS_TY_INHERENT.bits() - | TypeFlags::HAS_CT_PROJECTION.bits(); + | TypeFlags::HAS_CONST_ALIAS.bits(); /// Is a type or const error reachable? const HAS_NON_REGION_ERROR = 1 << 15; @@ -476,7 +476,7 @@ impl FlagComputation { ty::ConstKind::Alias(is_rigid, alias_const) => { self.add_is_rigid(is_rigid); self.add_args(alias_const.args.as_slice()); - self.add_flags(TypeFlags::HAS_CT_PROJECTION); + self.add_flags(TypeFlags::HAS_CONST_ALIAS); } ty::ConstKind::Infer(infer) => match infer { ty::InferConst::Fresh(_) => self.add_flags(TypeFlags::HAS_CT_FRESH), diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 065cc128101ae..44211354f520e 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1430,6 +1430,7 @@ impl VecDeque { /// buf.truncate(1); /// assert_eq!(buf, [5]); /// ``` + #[doc(alias = "retain_front")] #[stable(feature = "deque_extras", since = "1.16.0")] pub fn truncate(&mut self, len: usize) { /// Runs the destructor for all items in the slice when it gets dropped (normally or @@ -1484,7 +1485,6 @@ impl VecDeque { /// # Examples /// /// ``` - /// # #![feature(vec_deque_truncate_front)] /// use std::collections::VecDeque; /// /// let mut buf = VecDeque::new(); @@ -1493,11 +1493,12 @@ impl VecDeque { /// buf.push_front(15); /// assert_eq!(buf, [15, 10, 5]); /// assert_eq!(buf.as_slices(), (&[15, 10, 5][..], &[][..])); - /// buf.truncate_front(1); + /// buf.retain_back(1); /// assert_eq!(buf.as_slices(), (&[5][..], &[][..])); /// ``` - #[unstable(feature = "vec_deque_truncate_front", issue = "140667")] - pub fn truncate_front(&mut self, len: usize) { + #[doc(alias = "truncate_front")] + #[stable(feature = "vec_deque_truncate_front", since = "CURRENT_RUSTC_VERSION")] + pub fn retain_back(&mut self, len: usize) { /// Runs the destructor for all items in the slice when it gets dropped (normally or /// during unwinding). struct Dropper<'a, T>(&'a mut [T]); diff --git a/library/alloc/src/io/cursor.rs b/library/alloc/src/io/cursor.rs new file mode 100644 index 0000000000000..21f6b8b5b3ffd --- /dev/null +++ b/library/alloc/src/io/cursor.rs @@ -0,0 +1,260 @@ +use crate::alloc::Allocator; +use crate::boxed::Box; +use crate::io::{ + self, Cursor, ErrorKind, IoSlice, WriteThroughCursor, slice_write, slice_write_all, + slice_write_all_vectored, slice_write_vectored, +}; +use crate::vec::Vec; + +/// Reserves the required space, and pads the vec with 0s if necessary. +fn reserve_and_pad( + pos_mut: &mut u64, + vec: &mut Vec, + buf_len: usize, +) -> io::Result { + let pos: usize = (*pos_mut).try_into().map_err(|_| { + io::const_error!( + ErrorKind::InvalidInput, + "cursor position exceeds maximum possible vector length", + ) + })?; + + // For safety reasons, we don't want these numbers to overflow + // otherwise our allocation won't be enough + let desired_cap = pos.saturating_add(buf_len); + if desired_cap > vec.capacity() { + // We want our vec's total capacity + // to have room for (pos+buf_len) bytes. Reserve allocates + // based on additional elements from the length, so we need to + // reserve the difference + cfg_select! { + no_global_oom_handling => { + vec.try_reserve(desired_cap - vec.len())?; + } + _ => { + vec.reserve(desired_cap - vec.len()); + } + } + } + // Pad if pos is above the current len. + if pos > vec.len() { + let diff = pos - vec.len(); + // Unfortunately, `resize()` would suffice but the optimiser does not + // realise the `reserve` it does can be eliminated. So we do it manually + // to eliminate that extra branch + let spare = vec.spare_capacity_mut(); + debug_assert!(spare.len() >= diff); + // Safety: we have allocated enough capacity for this. + // And we are only writing, not reading + unsafe { + spare.get_unchecked_mut(..diff).fill(core::mem::MaybeUninit::new(0)); + vec.set_len(pos); + } + } + + Ok(pos) +} + +/// Writes the slice to the vec without allocating. +/// +/// # Safety +/// +/// `vec` must have `buf.len()` spare capacity. +unsafe fn vec_write_all_unchecked(pos: usize, vec: &mut Vec, buf: &[u8]) -> usize +where + A: Allocator, +{ + debug_assert!(vec.capacity() >= pos + buf.len()); + unsafe { vec.as_mut_ptr().add(pos).copy_from(buf.as_ptr(), buf.len()) }; + pos + buf.len() +} + +/// Resizing `write_all` implementation for [`Cursor`]. +/// +/// Cursor is allowed to have a pre-allocated and initialised +/// vector body, but with a position of 0. This means the [`Write`] +/// will overwrite the contents of the vec. +/// +/// This also allows for the vec body to be empty, but with a position of N. +/// This means that [`Write`] will pad the vec with 0 initially, +/// before writing anything from that point +/// +/// [`Write`]: crate::io::Write +fn vec_write_all(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result +where + A: Allocator, +{ + let buf_len = buf.len(); + let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; + + // Write the buf then progress the vec forward if necessary + // Safety: we have ensured that the capacity is available + // and that all bytes get written up to pos + unsafe { + pos = vec_write_all_unchecked(pos, vec, buf); + if pos > vec.len() { + vec.set_len(pos); + } + }; + + // Bump us forward + *pos_mut += buf_len as u64; + Ok(buf_len) +} + +/// Resizing `write_all_vectored` implementation for [`Cursor`]. +/// +/// Cursor is allowed to have a pre-allocated and initialised +/// vector body, but with a position of 0. This means the [`Write`] +/// will overwrite the contents of the vec. +/// +/// This also allows for the vec body to be empty, but with a position of N. +/// This means that [`Write`] will pad the vec with 0 initially, +/// before writing anything from that point +/// +/// [`Write`]: crate::io::Write +fn vec_write_all_vectored( + pos_mut: &mut u64, + vec: &mut Vec, + bufs: &[IoSlice<'_>], +) -> io::Result +where + A: Allocator, +{ + // For safety reasons, we don't want this sum to overflow ever. + // If this saturates, the reserve should panic to avoid any unsound writing. + let buf_len = bufs.iter().fold(0usize, |a, b| a.saturating_add(b.len())); + let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; + + // Write the buf then progress the vec forward if necessary + // Safety: we have ensured that the capacity is available + // and that all bytes get written up to the last pos + unsafe { + for buf in bufs { + pos = vec_write_all_unchecked(pos, vec, buf); + } + if pos > vec.len() { + vec.set_len(pos); + } + } + + // Bump us forward + *pos_mut += buf_len as u64; + Ok(buf_len) +} + +#[stable(feature = "cursor_mut_vec", since = "1.25.0")] +impl WriteThroughCursor for &mut Vec +where + A: Allocator, +{ + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf) + } + + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(_this: &Cursor) -> bool { + true + } + + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf)?; + Ok(()) + } + + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs)?; + Ok(()) + } + + #[inline] + fn flush(_this: &mut Cursor) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl WriteThroughCursor for Vec +where + A: Allocator, +{ + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf) + } + + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(_this: &Cursor) -> bool { + true + } + + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all(pos, inner, buf)?; + Ok(()) + } + + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + vec_write_all_vectored(pos, inner, bufs)?; + Ok(()) + } + + #[inline] + fn flush(_this: &mut Cursor) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "cursor_box_slice", since = "1.5.0")] +impl WriteThroughCursor for Box<[u8], A> +where + A: Allocator, +{ + #[inline] + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + slice_write(pos, inner, buf) + } + + #[inline] + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = this.into_parts_mut(); + slice_write_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(_this: &Cursor) -> bool { + true + } + + #[inline] + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + slice_write_all(pos, inner, buf) + } + + #[inline] + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = this.into_parts_mut(); + slice_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn flush(_this: &mut Cursor) -> io::Result<()> { + Ok(()) + } +} diff --git a/library/alloc/src/io/impls.rs b/library/alloc/src/io/impls.rs index 732842bc65df7..1351d3e3ef3a1 100644 --- a/library/alloc/src/io/impls.rs +++ b/library/alloc/src/io/impls.rs @@ -1,7 +1,12 @@ +use crate::alloc::Allocator; use crate::boxed::Box; -use crate::io::{self, Seek, SeekFrom, SizeHint}; +#[cfg(not(no_global_oom_handling))] +use crate::collections::VecDeque; +use crate::fmt; +use crate::io::{self, IoSlice, Seek, SeekFrom, SizeHint, Write}; #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] use crate::sync::Arc; +use crate::vec::Vec; // ============================================================================= // Forwarding implementations @@ -20,6 +25,43 @@ impl SizeHint for Box { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Box { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + (**self).write(buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (**self).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + (**self).is_write_vectored() + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (**self).write_all(buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (**self).write_all_vectored(bufs) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + (**self).write_fmt(fmt) + } +} #[stable(feature = "rust1", since = "1.0.0")] impl Seek for Box { #[inline] @@ -51,6 +93,148 @@ impl Seek for Box { // ============================================================================= // In-memory buffer implementations +/// Write is implemented for `Vec` by appending to the vector. +/// The vector will grow as needed. +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Vec { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + ::write_all(self, buf)?; + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let len = bufs.iter().map(|b| b.len()).sum(); + cfg_select! { + no_global_oom_handling => { + self.try_reserve(len)?; + } + _ => { + self.reserve(len); + } + } + for buf in bufs { + ::write_all(self, buf)?; + } + Ok(len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + cfg_select! { + no_global_oom_handling => { + self.try_extend_from_slice_of_bytes(buf)?; + } + _ => { + self.extend_from_slice(buf); + } + } + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.write_vectored(bufs)?; + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +/// Write is implemented for `VecDeque` by appending to the `VecDeque`, growing it as needed. +#[cfg(not(no_global_oom_handling))] +#[stable(feature = "vecdeque_read_write", since = "1.63.0")] +impl Write for VecDeque { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + self.extend(buf); + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let len = bufs.iter().map(|b| b.len()).sum(); + self.reserve(len); + for buf in bufs { + self.extend(&**buf); + } + Ok(len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.extend(buf); + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + self.write_vectored(bufs)?; + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] +#[stable(feature = "io_traits_arc", since = "1.73.0")] +impl Write for Arc +where + for<'a> &'a W: Write, + W: crate::io::IoHandle, +{ + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + (&**self).write(buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (&**self).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + (&**self).is_write_vectored() + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (&**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (&**self).write_all(buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (&**self).write_all_vectored(bufs) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + (&**self).write_fmt(fmt) + } +} #[cfg(all(not(no_rc), not(no_sync), target_has_atomic = "ptr"))] #[stable(feature = "io_traits_arc", since = "1.73.0")] impl Seek for Arc diff --git a/library/alloc/src/io/mod.rs b/library/alloc/src/io/mod.rs index 44a1d1b71d164..ef7e95ed5bd7c 100644 --- a/library/alloc/src/io/mod.rs +++ b/library/alloc/src/io/mod.rs @@ -1,5 +1,6 @@ //! Traits, helpers, and type definitions for core I/O functionality. +mod cursor; mod error; mod impls; @@ -14,8 +15,12 @@ pub use core::io::{BorrowedBuf, BorrowedCursor}; #[unstable(feature = "alloc_io", issue = "154046")] pub use core::io::{ Chain, Cursor, Empty, Error, ErrorKind, IoSlice, IoSliceMut, Repeat, Result, Seek, SeekFrom, - Sink, Take, empty, repeat, sink, + Sink, Take, Write, empty, repeat, sink, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] -pub use core::io::{IoHandle, OsFunctions, SizeHint, chain, stream_len_default, take}; +pub use core::io::{ + IoHandle, OsFunctions, SizeHint, WriteThroughCursor, chain, default_write_vectored, + slice_write, slice_write_all, slice_write_all_vectored, slice_write_vectored, + stream_len_default, take, +}; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 01b4e6f938616..9f82f5be82a68 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -96,6 +96,7 @@ #![feature(async_iterator)] #![feature(bstr)] #![feature(bstr_internals)] +#![feature(can_vector)] #![feature(case_ignorable)] #![feature(cast_maybe_uninit)] #![feature(cell_get_cloned)] @@ -180,6 +181,7 @@ #![feature(unicode_internals)] #![feature(unsize)] #![feature(unwrap_infallible)] +#![feature(write_all_vectored)] #![feature(wtf8_internals)] // tidy-alphabetical-end // diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index c3b86a635bd0f..a5d03ef76ade6 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -2938,8 +2938,26 @@ impl Vec { #[cfg(not(no_global_oom_handling))] #[inline] unsafe fn append_elements(&mut self, other: *const [T]) { + self.reserve(other.len()); + unsafe { + self.append_elements_unreserved(other); + } + } + + /// Appends elements to `self` from other buffer, returning [`TryReserveError`] on OOM. + #[inline] + unsafe fn try_append_elements(&mut self, other: *const [T]) -> Result<(), TryReserveError> { + self.try_reserve(other.len())?; + unsafe { + self.append_elements_unreserved(other); + } + Ok(()) + } + + /// Appends elements to `self` from other buffer without reserving additional capacity. + #[inline] + unsafe fn append_elements_unreserved(&mut self, other: *const [T]) { let count = other.len(); - self.reserve(count); let len = self.len(); if count > 0 { unsafe { @@ -3609,6 +3627,22 @@ impl Vec { } } +impl Vec { + #[cfg_attr( + not(no_global_oom_handling), + expect( + dead_code, + reason = "currently only used in IO module when global OOM handling is disabled" + ) + )] + pub(crate) fn try_extend_from_slice_of_bytes( + &mut self, + other: &[u8], + ) -> Result<(), TryReserveError> { + unsafe { self.try_append_elements(other) } + } +} + impl Vec<[T; N], A> { /// Takes a `Vec<[T; N]>` and flattens it into a `Vec`. /// diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index f89b974cd290c..2ede9730d12dc 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -36,7 +36,6 @@ #![feature(str_as_str)] #![feature(strict_provenance_lints)] #![feature(string_replace_in_place)] -#![feature(vec_deque_truncate_front)] #![feature(unique_rc_arc)] #![feature(macro_metavar_expr_concat)] #![feature(vec_peek_mut)] diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs index 91843dfd00585..2e75b7b07e63f 100644 --- a/library/alloctests/tests/vec_deque.rs +++ b/library/alloctests/tests/vec_deque.rs @@ -1635,7 +1635,7 @@ fn truncate_leak() { #[test] #[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] -fn truncate_front_leak() { +fn retain_back_leak() { struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } ); let mut q = VecDeque::new(); @@ -1648,7 +1648,7 @@ fn truncate_front_leak() { q.push_front(D(false)); q.push_front(D(false)); - catch_unwind(AssertUnwindSafe(|| q.truncate_front(1))).ok(); + catch_unwind(AssertUnwindSafe(|| q.retain_back(1))).ok(); assert_eq!(DROPS.get(), 7); } @@ -1817,20 +1817,20 @@ fn test_collect_from_into_iter_keeps_allocation() { } #[test] -fn test_truncate_front() { +fn test_retain_back() { let mut v = VecDeque::with_capacity(13); v.extend(0..7); assert_eq!(v.as_slices(), ([0, 1, 2, 3, 4, 5, 6].as_slice(), [].as_slice())); - v.truncate_front(10); + v.retain_back(10); assert_eq!(v.len(), 7); assert_eq!(v.as_slices(), ([0, 1, 2, 3, 4, 5, 6].as_slice(), [].as_slice())); - v.truncate_front(7); + v.retain_back(7); assert_eq!(v.len(), 7); assert_eq!(v.as_slices(), ([0, 1, 2, 3, 4, 5, 6].as_slice(), [].as_slice())); - v.truncate_front(3); + v.retain_back(3); assert_eq!(v.as_slices(), ([4, 5, 6].as_slice(), [].as_slice())); assert_eq!(v.len(), 3); - v.truncate_front(0); + v.retain_back(0); assert_eq!(v.as_slices(), ([].as_slice(), [].as_slice())); assert_eq!(v.len(), 0); @@ -1841,13 +1841,13 @@ fn test_truncate_front() { v.push_front(8); v.push_front(7); assert_eq!(v.as_slices(), ([7, 8, 9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); - v.truncate_front(12); + v.retain_back(12); assert_eq!(v.as_slices(), ([7, 8, 9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); - v.truncate_front(10); + v.retain_back(10); assert_eq!(v.as_slices(), ([7, 8, 9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); - v.truncate_front(8); + v.retain_back(8); assert_eq!(v.as_slices(), ([9].as_slice(), [0, 1, 2, 3, 4, 5, 6].as_slice())); - v.truncate_front(5); + v.retain_back(5); assert_eq!(v.as_slices(), ([2, 3, 4, 5, 6].as_slice(), [].as_slice())); } @@ -1855,7 +1855,7 @@ fn test_truncate_front() { fn test_extend_from_within() { let mut v = VecDeque::with_capacity(8); v.extend(0..6); - v.truncate_front(4); + v.retain_back(4); assert_eq!(v, [2, 3, 4, 5]); v.extend_from_within(1..4); assert_eq!(v, [2, 3, 4, 5, 3, 4, 5]); @@ -1915,7 +1915,7 @@ fn test_extend_from_within_clone() { panic: false, })); // remove the dummy elements - v.truncate_front(4); + v.retain_back(4); assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [0, 1, 2, 3]); v.extend_from_within(2..); @@ -1947,7 +1947,7 @@ fn test_extend_from_within_clone_panic() { panic: false, })); // remove the dummy elements - v.truncate_front(4); + v.retain_back(4); assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [0, 1, 2, 3]); // panic after wrapping @@ -1963,7 +1963,7 @@ fn test_extend_from_within_clone_panic() { // nothing should have been dropped assert_eq!(drop_count.get(), 0); - v.truncate_front(2); + v.retain_back(2); assert_eq!(drop_count.get(), 4); assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [0, 1]); @@ -1985,7 +1985,7 @@ fn test_extend_from_within_clone_panic() { fn test_prepend_from_within() { let mut v = VecDeque::with_capacity(8); v.extend(0..6); - v.truncate_front(4); + v.retain_back(4); v.prepend_from_within(..=0); assert_eq!(v.as_slices(), ([2, 2, 3, 4, 5].as_slice(), [].as_slice())); v.prepend_from_within(2..); @@ -2007,7 +2007,7 @@ fn test_prepend_from_within_clone() { panic: false, })); // remove the dummy elements - v.truncate_front(4); + v.retain_back(4); assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [0, 1, 2, 3]); v.prepend_from_within(..2); @@ -2034,7 +2034,7 @@ fn test_prepend_from_within_clone_panic() { panic: false, })); // remove the dummy elements - v.truncate_front(4); + v.retain_back(4); assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [0, 1, 2, 3]); // panic after wrapping @@ -2050,7 +2050,7 @@ fn test_prepend_from_within_clone_panic() { // nothing should have been dropped assert_eq!(drop_count.get(), 0); - v.truncate_front(2); + v.retain_back(2); assert_eq!(drop_count.get(), 4); assert_eq!(v.iter().map(|tr| tr.id).collect::>(), [2, 3]); @@ -2071,7 +2071,7 @@ fn test_prepend_from_within_clone_panic() { #[test] fn test_extend_and_prepend_from_within() { let mut v = ('0'..='9').map(String::from).collect::>(); - v.truncate_front(5); + v.retain_back(5); v.extend_from_within(4..); v.prepend_from_within(..2); assert_eq!(v.iter().map(|s| &**s).collect::(), "56567899"); @@ -2095,7 +2095,7 @@ fn test_extend_front() { let mut v = VecDeque::with_capacity(8); let cap = v.capacity(); v.extend(0..4); - v.truncate_front(2); + v.retain_back(2); v.extend_front(4..8); assert_eq!(v.as_slices(), ([7, 6].as_slice(), [5, 4, 2, 3].as_slice())); assert_eq!(v.capacity(), cap); diff --git a/library/core/src/alloc/global.rs b/library/core/src/alloc/global.rs index e97398aa5dc4a..44a8ab6e54196 100644 --- a/library/core/src/alloc/global.rs +++ b/library/core/src/alloc/global.rs @@ -1,4 +1,7 @@ +use super::{AllocError, GlobalAllocator}; use crate::alloc::Layout; +use crate::hint::assert_unchecked; +use crate::ptr::NonNull; use crate::{cmp, ptr}; /// A memory allocator that can be registered as the standard library’s default @@ -301,3 +304,72 @@ pub unsafe trait GlobalAlloc { new_ptr } } + +/// Allows all [`GlobalAllocator`]s to be used with the legacy [`GlobalAlloc`] interface. +#[stable(feature = "global_alloc", since = "1.28.0")] +unsafe impl GlobalAlloc for A +where + A: GlobalAllocator + ?Sized, +{ + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + // SAFETY: guaranteed by the caller. + // This might lead to the removal of zero-size checks inside the + // `Allocator` implementation. + unsafe { assert_unchecked(layout.size() != 0) }; + match self.allocate(layout) { + Ok(ptr) => ptr.cast().as_ptr(), + Err(AllocError) => ptr::null_mut(), + } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + // SAFETY: guaranteed by the caller. + unsafe { assert_unchecked(layout.size() != 0) }; + // SAFETY: only non-null pointers can be currently allocated. + let ptr = unsafe { NonNull::new_unchecked(ptr) }; + // SAFETY: guaranteed by caller. + unsafe { self.deallocate(ptr, layout) }; + } + + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + // SAFETY: guaranteed by the caller. + unsafe { assert_unchecked(layout.size() != 0) }; + match self.allocate_zeroed(layout) { + Ok(ptr) => ptr.cast().as_ptr(), + Err(AllocError) => ptr::null_mut(), + } + } + + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: guaranteed by the caller. + unsafe { assert_unchecked(layout.size() != 0) }; + // SAFETY: guaranteed by the caller. + unsafe { assert_unchecked(new_size != 0) }; + + // SAFETY: only non-null pointers can be currently allocated. + let ptr = unsafe { NonNull::new_unchecked(ptr) }; + let alignment = layout.alignment(); + // SAFETY: the caller must ensure that the `new_size` does not overflow + // when rounded up to the next multiple of `alignment`. + let new_layout = unsafe { Layout::from_size_alignment_unchecked(new_size, alignment) }; + + // SAFETY: + // Two preconditions are guaranteed by the caller: + // * `ptr` is currently allocated with this allocator. + // * `layout` fits the block of memory. + // The size precondition is upheld by selecting between `grow` and `shrink` + // based on the size. + let ptr = unsafe { + if new_size >= layout.size() { + self.grow(ptr, layout, new_layout) + } else { + self.shrink(ptr, layout, new_layout) + } + }; + + match ptr { + Ok(ptr) => ptr.cast().as_ptr(), + Err(AllocError) => ptr::null_mut(), + } + } +} diff --git a/library/core/src/alloc/mod.rs b/library/core/src/alloc/mod.rs index 806f67728efb8..339bc310c0b61 100644 --- a/library/core/src/alloc/mod.rs +++ b/library/core/src/alloc/mod.rs @@ -447,6 +447,96 @@ pub const unsafe trait Allocator { } } +/// An [`Allocator`] that can be registered as the standard library’s default +/// through the `#[global_allocator]` attribute. +/// +/// Types implementing this trait can be used as the default allocator for +/// memory allocations through `Box`, `Vec` and the collection types. For +/// instance, the `System` allocator implements this trait, and thus can be +/// explicitly set as the default like so: +/// ``` +/// use std::alloc::System; +/// +/// #[global_allocator] +/// static ALLOCATOR: System = System; +/// ``` +/// +/// The `Global` allocator forwards all memory allocation requests to the +/// `static` annotated with `#[global_allocator]`. Hence, `Global` does not +/// implement `GlobalAllocator` itself, as that would lead to infinite recursion. +/// +/// # Note to implementors +/// +/// This trait is used to prevent the infinite recursion that would occur if the +/// default allocator were to attempt to allocate memory through `Global` (and +/// thus from itself). +/// +/// When to implement this trait: +/// * for custom global allocators that only use system memory allocation +/// services. +/// * for allocators that wrap another allocator that implements `GlobalAllocator`. +/// +/// When **not** to implement this trait: +/// * for wrappers of arbitrary allocators (which might end up being `Global`, +/// leading to infinite recursion). +/// +/// # Safety +/// +/// In addition to the safety requirements of `Allocator`, global allocators are +/// subject to some additional constraints: +/// +/// * It's undefined behavior if global allocators unwind. This restriction may +/// be lifted in the future, but currently a panic from any of these +/// functions may lead to memory unsafety. +/// +/// * You must not rely on allocations actually happening, even if there are explicit +/// heap allocations in the source. The optimizer may detect unused allocations that it can either +/// eliminate entirely or move to the stack and thus never invoke the allocator. The +/// optimizer may further assume that allocation is infallible, so code that used to fail due +/// to allocator failures may now suddenly work because the optimizer worked around the +/// need for an allocation. More concretely, the following code example is unsound, irrespective +/// of whether your custom allocator allows counting how many allocations have happened. +/// +/// ```rust,ignore (unsound and has placeholders) +/// drop(Box::new(42)); +/// let number_of_heap_allocs = /* call private allocator API */; +/// unsafe { std::hint::assert_unchecked(number_of_heap_allocs > 0); } +/// ``` +/// +/// Note that the optimizations mentioned above are not the only +/// optimization that can be applied. You may generally not rely on heap allocations +/// happening if they can be removed without changing program behavior. +/// Whether allocations happen or not is not part of the program behavior, even if it +/// could be detected via an allocator that tracks allocations by printing or otherwise +/// having side effects. +/// +/// # Re-entrance +/// +/// When implementing a global allocator, one has to be careful not to create an infinitely recursive +/// implementation by accident, as many constructs in the Rust standard library may allocate in +/// their implementation. For example, on some platforms, [`std::sync::Mutex`] may allocate, so using +/// it is highly problematic in a global allocator. +/// +/// For this reason, one should generally stick to library features available through +/// [`core`], and avoid using [`std`] in a global allocator. A few features from [`std`] are +/// guaranteed to not use `#[global_allocator]` to allocate: +/// +/// - [`std::thread_local`], +/// - [`std::thread::current`], +/// - [`std::thread::park`] and [`std::thread::Thread`]'s [`unpark`] method and +/// [`Clone`] implementation. +/// +/// [`std`]: ../../std/index.html +/// [`std::sync::Mutex`]: ../../std/sync/struct.Mutex.html +/// [`std::thread_local`]: ../../std/macro.thread_local.html +/// [`std::thread::current`]: ../../std/thread/fn.current.html +/// [`std::thread::park`]: ../../std/thread/fn.park.html +/// [`std::thread::Thread`]: ../../std/thread/struct.Thread.html +/// [`unpark`]: ../../std/thread/struct.Thread.html#method.unpark +#[unstable(feature = "allocator_api", issue = "32838")] +#[expect(multiple_supertrait_upcastable)] +pub unsafe trait GlobalAllocator: Allocator + Sync + 'static {} + #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_const_unstable(feature = "const_heap", issue = "79597")] const unsafe impl Allocator for &A diff --git a/library/core/src/cmp.rs b/library/core/src/cmp.rs index 3371b2cecbd79..328e9b47966c9 100644 --- a/library/core/src/cmp.rs +++ b/library/core/src/cmp.rs @@ -248,14 +248,26 @@ use crate::ops::ControlFlow; #[rustc_diagnostic_item = "PartialEq"] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] pub const trait PartialEq: PointeeSized { - /// Tests for `self` and `other` values to be equal, and is used by `==`. + /// Equality operator `==`. + /// + /// Implementation of the "is equal to" operator `==`: + /// tests whether its arguments are equal. #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "cmp_partialeq_eq"] fn eq(&self, other: &Rhs) -> bool; - /// Tests for `!=`. The default implementation is almost always sufficient, - /// and should not be overridden without very good reason. + /// Inequality operator `!=`. + /// + /// Implementation of the "is not equal to" or "is different from" operator `!=`: + /// tests whether its arguments are different. + /// + /// # Default implementation + /// The default implementation of the inequality operator simply calls + /// the implementation of the equality operator and negates the result. + /// + /// This default shouldn't be overridden without good reason, + /// such as when forwarding to another PartialEq implementation. #[inline] #[must_use] #[stable(feature = "rust1", since = "1.0.0")] @@ -1869,19 +1881,31 @@ mod impls { use crate::ops::ControlFlow::{self, Break, Continue}; use crate::panic::const_assert; - macro_rules! partial_eq_impl { + /// Implements `PartialEq` for primitive types. + /// + /// Primitive types have a compiler-defined primitive implementation of `==` and `!=`. + /// This implements the `PartialEq` trait in terms of those primitive implementations. + /// + /// NOTE: Calling this on a non-primitive type (such as `()`) + /// leads to an infinitely-looping self-recursive implementation. + macro_rules! impl_partial_eq_for_primitive { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl PartialEq for $t { #[inline] fn eq(&self, other: &Self) -> bool { *self == *other } + // Override the default to use the primitive implementation for `!=`. #[inline] fn ne(&self, other: &Self) -> bool { *self != *other } } )*) } + impl_partial_eq_for_primitive! { + bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 + } + #[stable(feature = "rust1", since = "1.0.0")] #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] const impl PartialEq for () { @@ -1895,10 +1919,6 @@ mod impls { } } - partial_eq_impl! { - bool char usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f16 f32 f64 f128 - } - macro_rules! eq_impl { ($($t:ty)*) => ($( #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/io/cursor.rs b/library/core/src/io/cursor.rs index c104b426f48df..fe4def531a84a 100644 --- a/library/core/src/io/cursor.rs +++ b/library/core/src/io/cursor.rs @@ -1,4 +1,5 @@ -use crate::io::{self, ErrorKind, SeekFrom}; +use crate::cmp; +use crate::io::{self, ErrorKind, IoSlice, SeekFrom, Write}; /// A `Cursor` wraps an in-memory buffer and provides it with a /// [`Seek`] implementation. @@ -22,7 +23,7 @@ use crate::io::{self, ErrorKind, SeekFrom}; /// [bytes]: crate::slice "slice" /// [`File`]: ../../std/fs/struct.File.html /// [`Read`]: ../../std/io/trait.Read.html -/// [`Write`]: ../../std/io/trait.Write.html +/// [`Write`]: crate::io::Write /// [`Seek`]: crate::io::Seek /// [Vec]: ../../alloc/vec/struct.Vec.html /// @@ -294,6 +295,144 @@ where } } +/// Non-resizing [`Write::write`] implementation for slices. +/// Exported for `Cursor>`'s implementation of [`Write`]. +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { + let pos = cmp::min(*pos_mut, slice.len() as u64); + let dst = &mut slice[(pos as usize)..]; + let amt = cmp::min(buf.len(), dst.len()); + dst[..amt].copy_from_slice(&buf[..amt]); + *pos_mut += amt as u64; + Ok(amt) +} + +/// Non-resizing [`Write::write_vectored`] implementation for slices. +/// Exported for `Cursor>`'s implementation of [`Write`]. +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn slice_write_vectored( + pos_mut: &mut u64, + slice: &mut [u8], + bufs: &[IoSlice<'_>], +) -> io::Result { + let mut nwritten = 0; + for buf in bufs { + let n = slice_write(pos_mut, slice, buf)?; + nwritten += n; + if n < buf.len() { + break; + } + } + Ok(nwritten) +} + +/// Non-resizing [`Write::write_all`] implementation for slices. +/// Exported for `Cursor>`'s implementation of [`Write`]. +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn slice_write_all(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<()> { + let n = slice_write(pos_mut, slice, buf)?; + if n < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } +} + +/// Non-resizing [`Write::write_all_vectored`] implementation for slices. +/// Exported for `Cursor>`'s implementation of [`Write`]. +#[inline] +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn slice_write_all_vectored( + pos_mut: &mut u64, + slice: &mut [u8], + bufs: &[IoSlice<'_>], +) -> io::Result<()> { + for buf in bufs { + let n = slice_write(pos_mut, slice, buf)?; + if n < buf.len() { + return Err(io::Error::WRITE_ALL_EOF); + } + } + Ok(()) +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Cursor<&mut [u8]> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write(pos, inner, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all(pos, inner, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "cursor_array", since = "1.61.0")] +impl Write for Cursor<[u8; N]> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write(pos, inner, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let (pos, inner) = self.into_parts_mut(); + slice_write_vectored(pos, inner, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all(pos, inner, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + let (pos, inner) = self.into_parts_mut(); + slice_write_all_vectored(pos, inner, bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl io::Seek for Cursor where @@ -328,3 +467,53 @@ where Ok(self.position()) } } + +/// Trait used to allow indirect implementation of `Write` for `Cursor`. +/// Since [`Cursor`] is not a foundational type, it is not possible to implement +/// `Write` for `Cursor` if `Write` is defined in `libcore` and `T` is in a +/// downstream crate (e.g., `liballoc` or `libstd`). +/// +/// Methods are identical in purpose and meaning to their `Write` namesakes. +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub trait WriteThroughCursor: Sized { + fn write(this: &mut Cursor, buf: &[u8]) -> io::Result; + fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result; + fn is_write_vectored(this: &Cursor) -> bool; + fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()>; + fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()>; + fn flush(this: &mut Cursor) -> io::Result<()>; +} + +#[doc(hidden)] +impl Write for Cursor { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + WriteThroughCursor::write(self, buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + WriteThroughCursor::write_vectored(self, bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + WriteThroughCursor::is_write_vectored(self) + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + WriteThroughCursor::write_all(self, buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + WriteThroughCursor::write_all_vectored(self, bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + WriteThroughCursor::flush(self) + } +} diff --git a/library/core/src/io/error.rs b/library/core/src/io/error.rs index 4214a5f87e3ef..eac7396bcfd83 100644 --- a/library/core/src/io/error.rs +++ b/library/core/src/io/error.rs @@ -79,7 +79,7 @@ pub type Result = result::Result; /// // FIXME(#74481): Hard-links required to link from `core` to `std` /// [Read]: ../../std/io/trait.Read.html -/// [Write]: ../../std/io/trait.Write.html +/// [Write]: crate::io::Write /// [Seek]: crate::io::Seek #[stable(feature = "rust1", since = "1.0.0")] #[rustc_has_incoherent_inherent_impls] @@ -853,7 +853,7 @@ pub enum ErrorKind { /// particular number of bytes but only a smaller number of bytes could be /// written. /// - /// [write]: ../../std/io/trait.Write.html#tymethod.write + /// [write]: crate::io::Write::write /// [`Ok(0)`]: Ok #[stable(feature = "rust1", since = "1.0.0")] WriteZero, diff --git a/library/core/src/io/impls.rs b/library/core/src/io/impls.rs index e8c716f060119..218bd7adc2a55 100644 --- a/library/core/src/io/impls.rs +++ b/library/core/src/io/impls.rs @@ -1,4 +1,5 @@ -use crate::io::{self, Seek, SeekFrom, SizeHint}; +use crate::io::{self, IoSlice, Seek, SeekFrom, SizeHint, Write}; +use crate::{cmp, fmt, mem}; // ============================================================================= // Forwarding implementations @@ -17,6 +18,43 @@ impl SizeHint for &mut T { } } +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for &mut W { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + (**self).write(buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + (**self).write_vectored(bufs) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + (**self).is_write_vectored() + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (**self).write_all(buf) + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + (**self).write_all_vectored(bufs) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + (**self).write_fmt(fmt) + } +} #[stable(feature = "rust1", since = "1.0.0")] impl Seek for &mut S { #[inline] @@ -61,3 +99,110 @@ impl SizeHint for &[u8] { Some(self.len()) } } + +/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting +/// its data. +/// +/// Note that writing updates the slice to point to the yet unwritten part. +/// The slice will be empty when it has been completely overwritten. +/// +/// If the number of bytes to be written exceeds the size of the slice, write operations will +/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of +/// kind `ErrorKind::WriteZero`. +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for &mut [u8] { + #[inline] + fn write(&mut self, data: &[u8]) -> io::Result { + let amt = cmp::min(data.len(), self.len()); + let (a, b) = mem::take(self).split_at_mut(amt); + a.copy_from_slice(&data[..amt]); + *self = b; + Ok(amt) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let mut nwritten = 0; + for buf in bufs { + nwritten += self.write(buf)?; + if self.is_empty() { + break; + } + } + + Ok(nwritten) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, data: &[u8]) -> io::Result<()> { + if self.write(data)? < data.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + for buf in bufs { + if self.write(buf)? < buf.len() { + return Err(io::Error::WRITE_ALL_EOF); + } + } + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[unstable(feature = "read_buf", issue = "78485")] +impl<'a> io::Write for core::io::BorrowedCursor<'a, u8> { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + let amt = cmp::min(buf.len(), self.capacity()); + self.append(&buf[..amt]); + Ok(amt) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let mut nwritten = 0; + for buf in bufs { + let n = self.write(buf)?; + nwritten += n; + if n < buf.len() { + break; + } + } + Ok(nwritten) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + if self.write(buf)? < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } + } + + #[inline] + fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + for buf in bufs { + if self.write(buf)? < buf.len() { + return Err(io::Error::WRITE_ALL_EOF); + } + } + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} diff --git a/library/core/src/io/mod.rs b/library/core/src/io/mod.rs index 1c27e42229bb7..0134540ae86c8 100644 --- a/library/core/src/io/mod.rs +++ b/library/core/src/io/mod.rs @@ -8,6 +8,7 @@ mod io_slice; mod seek; mod size_hint; mod util; +mod write; #[unstable(feature = "core_io_borrowed_buf", issue = "117693")] pub use self::borrowed_buf::{BorrowedBuf, BorrowedCursor}; @@ -24,14 +25,20 @@ pub use self::{ io_slice::{IoSlice, IoSliceMut}, seek::{Seek, SeekFrom}, util::{Chain, Empty, Repeat, Sink, Take, empty, repeat, sink}, + write::Write, }; #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] pub use self::{ + cursor::{ + WriteThroughCursor, slice_write, slice_write_all, slice_write_all_vectored, + slice_write_vectored, + }, error::{Custom, CustomOwner, OsFunctions}, seek::stream_len_default, size_hint::SizeHint, util::{chain, take}, + write::default_write_vectored, }; /// Marks that a type `T` can have IO traits such as [`Seek`], [`Write`], etc. automatically @@ -48,7 +55,7 @@ pub use self::{ // FIXME(#74481): Hard-links required to link from `core` to `std` /// [file]: ../../std/fs/struct.File.html /// [arc]: ../../alloc/sync/struct.Arc.html -/// [`Write`]: ../../std/io/trait.Write.html +/// [`Write`]: crate::io::Write /// [`Seek`]: crate::io::Seek #[doc(hidden)] #[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] diff --git a/library/core/src/io/util.rs b/library/core/src/io/util.rs index de37690436c80..b022d876eb8f5 100644 --- a/library/core/src/io/util.rs +++ b/library/core/src/io/util.rs @@ -1,10 +1,10 @@ -use crate::io::{ErrorKind, Result, Seek, SeekFrom, SizeHint}; +use crate::io::{self, ErrorKind, IoSlice, Result, Seek, SeekFrom, SizeHint, Write}; use crate::{cmp, fmt}; /// `Empty` ignores any data written via [`Write`], and will always be empty /// (returning zero bytes) when read via [`Read`]. /// -/// [`Write`]: ../../std/io/trait.Write.html +/// [`Write`]: crate::io::Write /// [`Read`]: ../../std/io/trait.Read.html /// /// This struct is generally created by calling [`empty()`]. Please @@ -23,6 +23,84 @@ impl SizeHint for Empty { } } +#[stable(feature = "empty_write", since = "1.73.0")] +impl Write for Empty { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "empty_write", since = "1.73.0")] +impl Write for &Empty { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + #[stable(feature = "empty_seek", since = "1.51.0")] impl Seek for Empty { #[inline] @@ -51,7 +129,7 @@ impl Seek for Empty { /// [`Ok(buf.len())`]: Ok /// [`Ok(0)`]: Ok /// -/// [`write`]: ../../std/io/trait.Write.html#tymethod.write +/// [`write`]: crate::io::Write::write /// [`read`]: ../../std/io/trait.Read.html#tymethod.read /// /// # Examples @@ -142,12 +220,90 @@ impl fmt::Debug for Repeat { #[derive(Copy, Clone, Debug, Default)] pub struct Sink; +#[stable(feature = "rust1", since = "1.0.0")] +impl Write for Sink { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +#[stable(feature = "write_mt", since = "1.48.0")] +impl Write for &Sink { + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + Ok(buf.len()) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { + let total_len = bufs.iter().map(|b| b.len()).sum(); + Ok(total_len) + } + + #[inline] + fn is_write_vectored(&self) -> bool { + true + } + + #[inline] + fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { + Ok(()) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + /// Creates an instance of a writer which will successfully consume all data. /// /// All calls to [`write`] on the returned instance will return [`Ok(buf.len())`] /// and the contents of the buffer will not be inspected. /// -/// [`write`]: ../../std/io/trait.Write.html#tymethod.write +/// [`write`]: crate::io::Write::write /// [`Ok(buf.len())`]: Ok /// /// # Examples diff --git a/library/core/src/io/write.rs b/library/core/src/io/write.rs new file mode 100644 index 0000000000000..cdddd380885f4 --- /dev/null +++ b/library/core/src/io/write.rs @@ -0,0 +1,417 @@ +use crate::fmt; +use crate::io::{Error, IoSlice, Result}; + +/// A trait for objects which are byte-oriented sinks. +/// +/// Implementors of the `Write` trait are sometimes called 'writers'. +/// +/// Writers are defined by two required methods, [`write`] and [`flush`]: +/// +/// * The [`write`] method will attempt to write some data into the object, +/// returning how many bytes were successfully written. +/// +/// * The [`flush`] method is useful for adapters and explicit buffers +/// themselves for ensuring that all buffered data has been pushed out to the +/// 'true sink'. +/// +/// Writers are intended to be composable with one another. Many implementors +/// throughout [`std::io`] take and provide types which implement the `Write` +/// trait. +/// +/// [`write`]: Write::write +/// [`flush`]: Write::flush +/// [`std::io`]: crate::io +/// +/// # Examples +/// +/// ```no_run +/// use std::io::prelude::*; +/// use std::fs::File; +/// +/// fn main() -> std::io::Result<()> { +/// let data = b"some bytes"; +/// +/// let mut pos = 0; +/// let mut buffer = File::create("foo.txt")?; +/// +/// while pos < data.len() { +/// let bytes_written = buffer.write(&data[pos..])?; +/// pos += bytes_written; +/// } +/// Ok(()) +/// } +/// ``` +/// +/// The trait also provides convenience methods like [`write_all`], which calls +/// `write` in a loop until its entire input has been written. +/// +/// [`write_all`]: Write::write_all +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(notable_trait)] +#[cfg_attr(not(test), rustc_diagnostic_item = "IoWrite")] +pub trait Write { + /// Writes a buffer into this writer, returning how many bytes were written. + /// + /// This function will attempt to write the entire contents of `buf`, but + /// the entire write might not succeed, or the write may also generate an + /// error. Typically, a call to `write` represents one attempt to write to + /// any wrapped object. + /// + /// Calls to `write` are not guaranteed to block waiting for data to be + /// written, and a write which would otherwise block can be indicated through + /// an [`Err`] variant. + /// + /// If this method consumed `n > 0` bytes of `buf` it must return [`Ok(n)`]. + /// If the return value is `Ok(n)` then `n` must satisfy `n <= buf.len()`. + /// A return value of `Ok(0)` typically means that the underlying object is + /// no longer able to accept bytes and will likely not be able to in the + /// future as well, or that the buffer provided is empty. + /// + /// # Errors + /// + /// Each call to `write` may generate an I/O error indicating that the + /// operation could not be completed. If an error is returned then no bytes + /// in the buffer were written to this writer. + /// + /// It is **not** considered an error if the entire buffer could not be + /// written to this writer. + /// + /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the + /// write operation should be retried if there is nothing else to do. + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // Writes some prefix of the byte string, not necessarily all of it. + /// buffer.write(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + /// + /// [`Ok(n)`]: Ok + #[stable(feature = "rust1", since = "1.0.0")] + fn write(&mut self, buf: &[u8]) -> Result; + + /// Like [`write`], except that it writes from a slice of buffers. + /// + /// Data is copied from each buffer in order, with the final buffer + /// read from possibly being only partially consumed. This method must + /// behave as a call to [`write`] with the buffers concatenated would. + /// + /// The default implementation calls [`write`] with either the first nonempty + /// buffer provided, or an empty one if none exists. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::IoSlice; + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let data1 = [1; 8]; + /// let data2 = [15; 8]; + /// let io_slice1 = IoSlice::new(&data1); + /// let io_slice2 = IoSlice::new(&data2); + /// + /// let mut buffer = File::create("foo.txt")?; + /// + /// // Writes some prefix of the byte string, not necessarily all of it. + /// buffer.write_vectored(&[io_slice1, io_slice2])?; + /// Ok(()) + /// } + /// ``` + /// + /// [`write`]: Write::write + #[stable(feature = "iovec", since = "1.36.0")] + fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result { + default_write_vectored(|b| self.write(b), bufs) + } + + /// Determines if this `Write`r has an efficient [`write_vectored`] + /// implementation. + /// + /// If a `Write`r does not override the default [`write_vectored`] + /// implementation, code using it may want to avoid the method all together + /// and coalesce writes into a single buffer for higher performance. + /// + /// The default implementation returns `false`. + /// + /// [`write_vectored`]: Write::write_vectored + #[unstable(feature = "can_vector", issue = "69941")] + fn is_write_vectored(&self) -> bool { + false + } + + /// Flushes this output stream, ensuring that all intermediately buffered + /// contents reach their destination. + /// + /// # Errors + /// + /// It is considered an error if not all bytes could be written due to + /// I/O errors or EOF being reached. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::io::BufWriter; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = BufWriter::new(File::create("foo.txt")?); + /// + /// buffer.write_all(b"some bytes")?; + /// buffer.flush()?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn flush(&mut self) -> Result<()>; + + /// Attempts to write an entire buffer into this writer. + /// + /// This method will continuously call [`write`] until there is no more data + /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is + /// returned. This method will not return until the entire buffer has been + /// successfully written or such an error occurs. The first error that is + /// not of [`ErrorKind::Interrupted`] kind generated from this method will be + /// returned. + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// + /// If the buffer contains no data, this will never call [`write`]. + /// + /// # Errors + /// + /// This function will return the first error of + /// non-[`ErrorKind::Interrupted`] kind that [`write`] returns. + /// + /// [`write`]: Write::write + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// buffer.write_all(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { + while !buf.is_empty() { + match self.write(buf) { + Ok(0) => { + return Err(Error::WRITE_ALL_EOF); + } + Ok(n) => buf = &buf[n..], + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Attempts to write multiple buffers into this writer. + /// + /// This method will continuously call [`write_vectored`] until there is no + /// more data to be written or an error of non-[`ErrorKind::Interrupted`] + /// kind is returned. This method will not return until all buffers have + /// been successfully written or such an error occurs. The first error that + /// is not of [`ErrorKind::Interrupted`] kind generated from this method + /// will be returned. + /// + /// [`ErrorKind::Interrupted`]: crate::io::ErrorKind::Interrupted + /// + /// If the buffer contains no data, this will never call [`write_vectored`]. + /// + /// # Notes + /// + /// Unlike [`write_vectored`], this takes a *mutable* reference to + /// a slice of [`IoSlice`]s, not an immutable one. That's because we need to + /// modify the slice to keep track of the bytes already written. + /// + /// Once this function returns, the contents of `bufs` are unspecified, as + /// this depends on how many calls to [`write_vectored`] were necessary. It is + /// best to understand this function as taking ownership of `bufs` and to + /// not use `bufs` afterwards. The underlying buffers, to which the + /// [`IoSlice`]s point (but not the [`IoSlice`]s themselves), are unchanged and + /// can be reused. + /// + /// [`write_vectored`]: Write::write_vectored + /// + /// # Examples + /// + /// ``` + /// #![feature(write_all_vectored)] + /// # fn main() -> std::io::Result<()> { + /// + /// use std::io::{Write, IoSlice}; + /// + /// let mut writer = Vec::new(); + /// let bufs = &mut [ + /// IoSlice::new(&[1]), + /// IoSlice::new(&[2, 3]), + /// IoSlice::new(&[4, 5, 6]), + /// ]; + /// + /// writer.write_all_vectored(bufs)?; + /// // Note: the contents of `bufs` is now undefined, see the Notes section. + /// + /// assert_eq!(writer, &[1, 2, 3, 4, 5, 6]); + /// # Ok(()) } + /// ``` + #[unstable(feature = "write_all_vectored", issue = "70436")] + fn write_all_vectored(&mut self, mut bufs: &mut [IoSlice<'_>]) -> Result<()> { + // Guarantee that bufs is empty if it contains no data, + // to avoid calling write_vectored if there is no data to be written. + IoSlice::advance_slices(&mut bufs, 0); + while !bufs.is_empty() { + match self.write_vectored(bufs) { + Ok(0) => { + return Err(Error::WRITE_ALL_EOF); + } + Ok(n) => IoSlice::advance_slices(&mut bufs, n), + Err(ref e) if e.is_interrupted() => {} + Err(e) => return Err(e), + } + } + Ok(()) + } + + /// Writes a formatted string into this writer, returning any error + /// encountered. + /// + /// This method is primarily used to interface with the + /// [`format_args!()`] macro, and it is rare that this should + /// explicitly be called. The [`write!()`] macro should be favored to + /// invoke this method instead. + /// + /// This function internally uses the [`write_all`] method on + /// this trait and hence will continuously write data so long as no errors + /// are received. This also means that partial writes are not indicated in + /// this signature. + /// + /// [`write_all`]: Write::write_all + /// + /// # Errors + /// + /// This function will return any I/O error reported while formatting. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::prelude::*; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// // this call + /// write!(buffer, "{:.*}", 2, 1.234567)?; + /// // turns into this: + /// buffer.write_fmt(format_args!("{:.*}", 2, 1.234567))?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> { + if let Some(s) = args.as_statically_known_str() { + self.write_all(s.as_bytes()) + } else { + default_write_fmt(self, args) + } + } + + /// Creates a "by reference" adapter for this instance of `Write`. + /// + /// The returned adapter also implements `Write` and will simply borrow this + /// current writer. + /// + /// # Examples + /// + /// ```no_run + /// use std::io::Write; + /// use std::fs::File; + /// + /// fn main() -> std::io::Result<()> { + /// let mut buffer = File::create("foo.txt")?; + /// + /// let reference = buffer.by_ref(); + /// + /// // we can use reference just like our original buffer + /// reference.write_all(b"some bytes")?; + /// Ok(()) + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + fn by_ref(&mut self) -> &mut Self + where + Self: Sized, + { + self + } +} + +/// Default implementation of [`Write::write_vectored`], which is currently used +/// in `libstd` for file system implementations of similar methods. +#[doc(hidden)] +#[unstable(feature = "core_io_internals", reason = "exposed only for libstd", issue = "none")] +pub fn default_write_vectored(write: F, bufs: &[IoSlice<'_>]) -> Result +where + F: FnOnce(&[u8]) -> Result, +{ + let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b); + write(buf) +} + +fn default_write_fmt(this: &mut W, args: fmt::Arguments<'_>) -> Result<()> { + // Create a shim which translates a `Write` to a `fmt::Write` and saves off + // I/O errors, instead of discarding them. + struct Adapter<'a, T: ?Sized + 'a> { + inner: &'a mut T, + error: Result<()>, + } + + impl fmt::Write for Adapter<'_, T> { + fn write_str(&mut self, s: &str) -> fmt::Result { + match self.inner.write_all(s.as_bytes()) { + Ok(()) => Ok(()), + Err(e) => { + self.error = Err(e); + Err(fmt::Error) + } + } + } + } + + let mut output = Adapter { inner: this, error: Ok(()) }; + match fmt::write(&mut output, args) { + Ok(()) => Ok(()), + Err(..) => { + // Check whether the error came from the underlying `Write`. + if output.error.is_err() { + output.error + } else { + // This shouldn't happen: the underlying stream did not error, + // but somehow the formatter still errored? + panic!( + "a formatting trait implementation returned an error when the underlying stream did not" + ); + } + } + } +} diff --git a/library/core/src/marker.rs b/library/core/src/marker.rs index 45b8b266a2671..e3785c92c8d0d 100644 --- a/library/core/src/marker.rs +++ b/library/core/src/marker.rs @@ -1073,6 +1073,20 @@ pub const trait Destruct: PointeeSized {} #[rustc_dyn_incompatible_trait] pub trait Tuple {} +/// Creates a new style directly represented const argument. +/// ```ignore (cannot test this from within core yet) +/// type const BAR: usize = N; +/// type const FOO: usize = direct!(BAR::); +/// ``` +#[rustc_builtin_macro(direct_const_arg)] +#[unstable(feature = "min_generic_const_args", issue = "132980")] +#[macro_export] +macro_rules! direct_const_arg { + ($($arg:tt)*) => { + /* compiler built-in */ + }; +} + /// A marker for types which can be used as types of `const` generic parameters. /// /// These types must have a proper equivalence relation (`Eq`) and it must be automatically diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 8a374f015a958..a2d367931ea9b 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2392,7 +2392,7 @@ macro_rules! int_impl { /// /// Beware that, unlike most other `wrapping_*` methods on integers, this /// does *not* give the same result as doing the shift in infinite precision - /// then truncating as needed. The behaviour matches what shift instructions + /// then truncating as needed. Instead, the behaviour of this method matches what shift instructions /// do on many processors, and is what the `<<` operator does when overflow /// checks are disabled, but numerically it's weird. Consider, instead, /// using [`Self::unbounded_shl`] which has nicer behaviour. @@ -2429,7 +2429,7 @@ macro_rules! int_impl { /// /// Beware that, unlike most other `wrapping_*` methods on integers, this /// does *not* give the same result as doing the shift in infinite precision - /// then truncating as needed. The behaviour matches what shift instructions + /// then truncating as needed. Instead, the behaviour of this method matches what shift instructions /// do on many processors, and is what the `>>` operator does when overflow /// checks are disabled, but numerically it's weird. Consider, instead, /// using [`Self::unbounded_shr`] which has nicer behaviour. diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index cfb5eb54fb6bc..b33b2b5db1a32 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2827,7 +2827,7 @@ macro_rules! uint_impl { /// /// Beware that, unlike most other `wrapping_*` methods on integers, this /// does *not* give the same result as doing the shift in infinite precision - /// then truncating as needed. The behaviour matches what shift instructions + /// then truncating as needed. Instead, the behaviour of this method matches what shift instructions /// do on many processors, and is what the `<<` operator does when overflow /// checks are disabled, but numerically it's weird. Consider, instead, /// using [`Self::unbounded_shl`] which has nicer behaviour. @@ -2871,7 +2871,7 @@ macro_rules! uint_impl { /// /// Beware that, unlike most other `wrapping_*` methods on integers, this /// does *not* give the same result as doing the shift in infinite precision - /// then truncating as needed. The behaviour matches what shift instructions + /// then truncating as needed. Instead, the behaviour of this method matches what shift instructions /// do on many processors, and is what the `>>` operator does when overflow /// checks are disabled, but numerically it's weird. Consider, instead, /// using [`Self::unbounded_shr`] which has nicer behaviour. diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 163e8bf714b1d..9e7a8d4472d81 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -2082,10 +2082,7 @@ impl Option { /// assert_eq!(o2.into_flat_iter().collect::>(), Vec::<&usize>::new()); /// ``` #[unstable(feature = "option_into_flat_iter", issue = "148441")] - pub fn into_flat_iter(self) -> OptionFlatten - where - T: IntoIterator, - { + pub fn into_flat_iter(self) -> OptionFlatten { OptionFlatten { iter: self.map(IntoIterator::into_iter) } } } diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 7a576e083df7c..753ca5aba561c 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -63,14 +63,15 @@ #![deny(unsafe_op_in_unsafe_fn)] #![stable(feature = "alloc_module", since = "1.28.0")] -use core::ptr::NonNull; -use core::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; -use core::{hint, mem, ptr}; - #[stable(feature = "alloc_module", since = "1.28.0")] #[doc(inline)] pub use alloc_crate::alloc::*; +use crate::ptr::NonNull; +use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; +use crate::sys::alloc as imp; +use crate::{hint, mem, ptr}; + /// The default memory allocator provided by the operating system. /// /// This is based on `malloc` on Unix platforms and `HeapAlloc` on Windows, @@ -145,11 +146,7 @@ impl System { 0 => Ok(layout.dangling_ptr().cast_slice(0)), // SAFETY: `layout` is non-zero in size, size => unsafe { - let raw_ptr = if zeroed { - GlobalAlloc::alloc_zeroed(self, layout) - } else { - GlobalAlloc::alloc(self, layout) - }; + let raw_ptr = if zeroed { imp::alloc_zeroed(layout) } else { imp::alloc(layout) }; let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(ptr.cast_slice(size)) }, @@ -182,7 +179,7 @@ impl System { // `realloc` probably checks for `new_size >= old_layout.size()` or something similar. hint::assert_unchecked(new_size >= old_layout.size()); - let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); + let raw_ptr = imp::realloc(ptr.as_ptr(), old_layout, new_size); let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; if zeroed { raw_ptr.add(old_size).write_bytes(0, new_size - old_size); @@ -205,8 +202,8 @@ impl System { } } -// The Allocator impl checks the layout size to be non-zero and forwards to the GlobalAlloc impl, -// which is in `std::sys::*::alloc`. +// The Allocator impl checks the layout size to be non-zero and forwards to the +// platform functions in `std::sys::*::alloc`. #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl Allocator for System { #[inline] @@ -224,7 +221,7 @@ unsafe impl Allocator for System { if layout.size() != 0 { // SAFETY: `layout` is non-zero in size, // other conditions must be upheld by the caller - unsafe { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } + unsafe { imp::dealloc(ptr.as_ptr(), layout) } } } @@ -274,7 +271,7 @@ unsafe impl Allocator for System { // `realloc` probably checks for `new_size <= old_layout.size()` or something similar. hint::assert_unchecked(new_size <= old_layout.size()); - let raw_ptr = GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size); + let raw_ptr = imp::realloc(ptr.as_ptr(), old_layout, new_size); let ptr = NonNull::new(raw_ptr).ok_or(AllocError)?; Ok(ptr.cast_slice(new_size)) }, @@ -294,6 +291,9 @@ unsafe impl Allocator for System { } } +#[unstable(feature = "allocator_api", issue = "32838")] +unsafe impl GlobalAllocator for System {} + static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// Registers a custom allocation error hook, replacing any that was previously registered. @@ -435,7 +435,12 @@ pub fn rust_oom(layout: Layout) -> ! { #[allow(unused_attributes)] #[unstable(feature = "alloc_internals", issue = "none")] pub mod __default_lib_allocator { - use super::{GlobalAlloc, Layout, System}; + use super::Layout; + // We call the system functions directly to avoid any overheads introduced + // by the roundtrip through `impl Allocator for System` and + // `impl GlobalAlloc for A`. + use crate::sys::alloc as imp; + // These magic symbol names are used as a fallback for implementing the // `__rust_alloc` etc symbols (see `src/liballoc/alloc.rs`) when there is // no `#[global_allocator]` attribute. @@ -452,7 +457,7 @@ pub mod __default_lib_allocator { // `GlobalAlloc::alloc`. unsafe { let layout = Layout::from_size_align_unchecked(size, align); - System.alloc(layout) + imp::alloc(layout) } } @@ -460,7 +465,7 @@ pub mod __default_lib_allocator { pub unsafe extern "C" fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) { // SAFETY: see the guarantees expected by `Layout::from_size_align` and // `GlobalAlloc::dealloc`. - unsafe { System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) } + unsafe { imp::dealloc(ptr, Layout::from_size_align_unchecked(size, align)) } } #[rustc_std_internal_symbol] @@ -474,7 +479,7 @@ pub mod __default_lib_allocator { // `GlobalAlloc::realloc`. unsafe { let old_layout = Layout::from_size_align_unchecked(old_size, align); - System.realloc(ptr, old_layout, new_size) + imp::realloc(ptr, old_layout, new_size) } } @@ -484,7 +489,7 @@ pub mod __default_lib_allocator { // `GlobalAlloc::alloc_zeroed`. unsafe { let layout = Layout::from_size_align_unchecked(size, align); - System.alloc_zeroed(layout) + imp::alloc_zeroed(layout) } } } diff --git a/library/std/src/io/cursor.rs b/library/std/src/io/cursor.rs index 5399789b4242f..e0b127bef8e2b 100644 --- a/library/std/src/io/cursor.rs +++ b/library/std/src/io/cursor.rs @@ -4,10 +4,8 @@ mod tests; #[stable(feature = "rust1", since = "1.0.0")] pub use core::io::Cursor; -use crate::alloc::Allocator; -use crate::cmp; use crate::io::prelude::*; -use crate::io::{self, BorrowedCursor, ErrorKind, IoSlice, IoSliceMut}; +use crate::io::{self, BorrowedCursor, IoSliceMut}; #[stable(feature = "rust1", since = "1.0.0")] impl Read for Cursor @@ -101,414 +99,3 @@ where self.set_position(self.position() + amt as u64); } } - -/// Trait used to allow indirect implementation of `Write` for `Cursor`. -/// Since [`Cursor`] is not a foundational type, it is not possible to implement -/// `Write` for `Cursor` if `Write` is defined in `libcore` and `T` is in a -/// downstream crate (e.g., `liballoc` or `libstd`). -/// -/// Methods are identical in purpose and meaning to their `Write` namesakes. -trait WriteThroughCursor: Sized { - fn write(this: &mut Cursor, buf: &[u8]) -> io::Result; - fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result; - fn is_write_vectored(this: &Cursor) -> bool; - fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()>; - fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()>; - fn flush(this: &mut Cursor) -> io::Result<()>; -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - WriteThroughCursor::write(self, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - WriteThroughCursor::write_vectored(self, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - WriteThroughCursor::is_write_vectored(self) - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - WriteThroughCursor::write_all(self, buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - WriteThroughCursor::write_all_vectored(self, bufs) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - WriteThroughCursor::flush(self) - } -} - -// Non-resizing write implementation -#[inline] -fn slice_write(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result { - let pos = cmp::min(*pos_mut, slice.len() as u64); - let amt = (&mut slice[(pos as usize)..]).write(buf)?; - *pos_mut += amt as u64; - Ok(amt) -} - -#[inline] -fn slice_write_vectored( - pos_mut: &mut u64, - slice: &mut [u8], - bufs: &[IoSlice<'_>], -) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - let n = slice_write(pos_mut, slice, buf)?; - nwritten += n; - if n < buf.len() { - break; - } - } - Ok(nwritten) -} - -#[inline] -fn slice_write_all(pos_mut: &mut u64, slice: &mut [u8], buf: &[u8]) -> io::Result<()> { - let n = slice_write(pos_mut, slice, buf)?; - if n < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } -} - -#[inline] -fn slice_write_all_vectored( - pos_mut: &mut u64, - slice: &mut [u8], - bufs: &[IoSlice<'_>], -) -> io::Result<()> { - for buf in bufs { - let n = slice_write(pos_mut, slice, buf)?; - if n < buf.len() { - return Err(io::Error::WRITE_ALL_EOF); - } - } - Ok(()) -} - -/// Reserves the required space, and pads the vec with 0s if necessary. -fn reserve_and_pad( - pos_mut: &mut u64, - vec: &mut Vec, - buf_len: usize, -) -> io::Result { - let pos: usize = (*pos_mut).try_into().map_err(|_| { - io::const_error!( - ErrorKind::InvalidInput, - "cursor position exceeds maximum possible vector length", - ) - })?; - - // For safety reasons, we don't want these numbers to overflow - // otherwise our allocation won't be enough - let desired_cap = pos.saturating_add(buf_len); - if desired_cap > vec.capacity() { - // We want our vec's total capacity - // to have room for (pos+buf_len) bytes. Reserve allocates - // based on additional elements from the length, so we need to - // reserve the difference - vec.reserve(desired_cap - vec.len()); - } - // Pad if pos is above the current len. - if pos > vec.len() { - let diff = pos - vec.len(); - // Unfortunately, `resize()` would suffice but the optimiser does not - // realise the `reserve` it does can be eliminated. So we do it manually - // to eliminate that extra branch - let spare = vec.spare_capacity_mut(); - debug_assert!(spare.len() >= diff); - // Safety: we have allocated enough capacity for this. - // And we are only writing, not reading - unsafe { - spare.get_unchecked_mut(..diff).fill(core::mem::MaybeUninit::new(0)); - vec.set_len(pos); - } - } - - Ok(pos) -} - -/// Writes the slice to the vec without allocating. -/// -/// # Safety -/// -/// `vec` must have `buf.len()` spare capacity. -unsafe fn vec_write_all_unchecked(pos: usize, vec: &mut Vec, buf: &[u8]) -> usize -where - A: Allocator, -{ - debug_assert!(vec.capacity() >= pos + buf.len()); - unsafe { vec.as_mut_ptr().add(pos).copy_from(buf.as_ptr(), buf.len()) }; - pos + buf.len() -} - -/// Resizing `write_all` implementation for [`Cursor`]. -/// -/// Cursor is allowed to have a pre-allocated and initialised -/// vector body, but with a position of 0. This means the [`Write`] -/// will overwrite the contents of the vec. -/// -/// This also allows for the vec body to be empty, but with a position of N. -/// This means that [`Write`] will pad the vec with 0 initially, -/// before writing anything from that point -fn vec_write_all(pos_mut: &mut u64, vec: &mut Vec, buf: &[u8]) -> io::Result -where - A: Allocator, -{ - let buf_len = buf.len(); - let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; - - // Write the buf then progress the vec forward if necessary - // Safety: we have ensured that the capacity is available - // and that all bytes get written up to pos - unsafe { - pos = vec_write_all_unchecked(pos, vec, buf); - if pos > vec.len() { - vec.set_len(pos); - } - }; - - // Bump us forward - *pos_mut += buf_len as u64; - Ok(buf_len) -} - -/// Resizing `write_all_vectored` implementation for [`Cursor`]. -/// -/// Cursor is allowed to have a pre-allocated and initialised -/// vector body, but with a position of 0. This means the [`Write`] -/// will overwrite the contents of the vec. -/// -/// This also allows for the vec body to be empty, but with a position of N. -/// This means that [`Write`] will pad the vec with 0 initially, -/// before writing anything from that point -fn vec_write_all_vectored( - pos_mut: &mut u64, - vec: &mut Vec, - bufs: &[IoSlice<'_>], -) -> io::Result -where - A: Allocator, -{ - // For safety reasons, we don't want this sum to overflow ever. - // If this saturates, the reserve should panic to avoid any unsound writing. - let buf_len = bufs.iter().fold(0usize, |a, b| a.saturating_add(b.len())); - let mut pos = reserve_and_pad(pos_mut, vec, buf_len)?; - - // Write the buf then progress the vec forward if necessary - // Safety: we have ensured that the capacity is available - // and that all bytes get written up to the last pos - unsafe { - for buf in bufs { - pos = vec_write_all_unchecked(pos, vec, buf); - } - if pos > vec.len() { - vec.set_len(pos); - } - } - - // Bump us forward - *pos_mut += buf_len as u64; - Ok(buf_len) -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Cursor<&mut [u8]> { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); - slice_write(pos, inner, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); - slice_write_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); - slice_write_all(pos, inner, buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); - slice_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "cursor_mut_vec", since = "1.25.0")] -impl WriteThroughCursor for &mut Vec -where - A: Allocator, -{ - fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - vec_write_all(pos, inner, buf) - } - - fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - vec_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(_this: &Cursor) -> bool { - true - } - - fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - vec_write_all(pos, inner, buf)?; - Ok(()) - } - - fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - vec_write_all_vectored(pos, inner, bufs)?; - Ok(()) - } - - #[inline] - fn flush(_this: &mut Cursor) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl WriteThroughCursor for Vec -where - A: Allocator, -{ - fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - vec_write_all(pos, inner, buf) - } - - fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - vec_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(_this: &Cursor) -> bool { - true - } - - fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - vec_write_all(pos, inner, buf)?; - Ok(()) - } - - fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - vec_write_all_vectored(pos, inner, bufs)?; - Ok(()) - } - - #[inline] - fn flush(_this: &mut Cursor) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "cursor_box_slice", since = "1.5.0")] -impl WriteThroughCursor for Box<[u8], A> -where - A: Allocator, -{ - #[inline] - fn write(this: &mut Cursor, buf: &[u8]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - slice_write(pos, inner, buf) - } - - #[inline] - fn write_vectored(this: &mut Cursor, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = this.into_parts_mut(); - slice_write_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(_this: &Cursor) -> bool { - true - } - - #[inline] - fn write_all(this: &mut Cursor, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - slice_write_all(pos, inner, buf) - } - - #[inline] - fn write_all_vectored(this: &mut Cursor, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = this.into_parts_mut(); - slice_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn flush(_this: &mut Cursor) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "cursor_array", since = "1.61.0")] -impl Write for Cursor<[u8; N]> { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); - slice_write(pos, inner, buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let (pos, inner) = self.into_parts_mut(); - slice_write_vectored(pos, inner, bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); - slice_write_all(pos, inner, buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - let (pos, inner) = self.into_parts_mut(); - slice_write_all_vectored(pos, inner, bufs) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} diff --git a/library/std/src/io/impls.rs b/library/std/src/io/impls.rs index 08bb34376b075..61f82828149aa 100644 --- a/library/std/src/io/impls.rs +++ b/library/std/src/io/impls.rs @@ -3,9 +3,9 @@ mod tests; use crate::alloc::Allocator; use crate::collections::VecDeque; -use crate::io::{self, BorrowedCursor, BufRead, IoSlice, IoSliceMut, Read, Write}; +use crate::io::{self, BorrowedCursor, BufRead, IoSliceMut, Read}; use crate::sync::Arc; -use crate::{cmp, fmt, mem, str}; +use crate::{cmp, str}; // ============================================================================= // Forwarding implementations @@ -53,43 +53,6 @@ impl Read for &mut R { } } #[stable(feature = "rust1", since = "1.0.0")] -impl Write for &mut W { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - (**self).write(buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - (**self).write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - (**self).is_write_vectored() - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - (**self).flush() - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - (**self).write_all(buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - (**self).write_all_vectored(bufs) - } - - #[inline] - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - (**self).write_fmt(fmt) - } -} -#[stable(feature = "rust1", since = "1.0.0")] impl BufRead for &mut B { #[inline] fn fill_buf(&mut self) -> io::Result<&[u8]> { @@ -165,43 +128,6 @@ impl Read for Box { } } #[stable(feature = "rust1", since = "1.0.0")] -impl Write for Box { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - (**self).write(buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - (**self).write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - (**self).is_write_vectored() - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - (**self).flush() - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - (**self).write_all(buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - (**self).write_all_vectored(bufs) - } - - #[inline] - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - (**self).write_fmt(fmt) - } -} -#[stable(feature = "rust1", since = "1.0.0")] impl BufRead for Box { #[inline] fn fill_buf(&mut self) -> io::Result<&[u8]> { @@ -362,108 +288,6 @@ impl BufRead for &[u8] { } } -/// Write is implemented for `&mut [u8]` by copying into the slice, overwriting -/// its data. -/// -/// Note that writing updates the slice to point to the yet unwritten part. -/// The slice will be empty when it has been completely overwritten. -/// -/// If the number of bytes to be written exceeds the size of the slice, write operations will -/// return short writes: ultimately, `Ok(0)`; in this situation, `write_all` returns an error of -/// kind `ErrorKind::WriteZero`. -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for &mut [u8] { - #[inline] - fn write(&mut self, data: &[u8]) -> io::Result { - let amt = cmp::min(data.len(), self.len()); - let (a, b) = mem::take(self).split_at_mut(amt); - a.copy_from_slice(&data[..amt]); - *self = b; - Ok(amt) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - nwritten += self.write(buf)?; - if self.is_empty() { - break; - } - } - - Ok(nwritten) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, data: &[u8]) -> io::Result<()> { - if self.write(data)? < data.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - for buf in bufs { - if self.write(buf)? < buf.len() { - return Err(io::Error::WRITE_ALL_EOF); - } - } - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -/// Write is implemented for `Vec` by appending to the vector. -/// The vector will grow as needed. -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Vec { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - self.extend_from_slice(buf); - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let len = bufs.iter().map(|b| b.len()).sum(); - self.reserve(len); - for buf in bufs { - self.extend_from_slice(buf); - } - Ok(len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.extend_from_slice(buf); - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.write_vectored(bufs)?; - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - /// Read is implemented for `VecDeque` by consuming bytes from the front of the `VecDeque`. #[stable(feature = "vecdeque_read_write", since = "1.63.0")] impl Read for VecDeque { @@ -573,96 +397,6 @@ impl BufRead for VecDeque { } } -/// Write is implemented for `VecDeque` by appending to the `VecDeque`, growing it as needed. -#[stable(feature = "vecdeque_read_write", since = "1.63.0")] -impl Write for VecDeque { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - self.extend(buf); - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let len = bufs.iter().map(|b| b.len()).sum(); - self.reserve(len); - for buf in bufs { - self.extend(&**buf); - } - Ok(len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - self.extend(buf); - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - self.write_vectored(bufs)?; - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[unstable(feature = "read_buf", issue = "78485")] -impl<'a> io::Write for core::io::BorrowedCursor<'a, u8> { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - let amt = cmp::min(buf.len(), self.capacity()); - self.append(&buf[..amt]); - Ok(amt) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let mut nwritten = 0; - for buf in bufs { - let n = self.write(buf)?; - nwritten += n; - if n < buf.len() { - break; - } - } - Ok(nwritten) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - if self.write(buf)? < buf.len() { Err(io::Error::WRITE_ALL_EOF) } else { Ok(()) } - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - for buf in bufs { - if self.write(buf)? < buf.len() { - return Err(io::Error::WRITE_ALL_EOF); - } - } - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - #[stable(feature = "io_traits_arc", since = "1.73.0")] impl Read for Arc where @@ -709,44 +443,3 @@ where (&**self).read_buf_exact(cursor) } } -#[stable(feature = "io_traits_arc", since = "1.73.0")] -impl Write for Arc -where - for<'a> &'a W: Write, - W: crate::io::IoHandle, -{ - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - (&**self).write(buf) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - (&**self).write_vectored(bufs) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - (&**self).is_write_vectored() - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - (&**self).flush() - } - - #[inline] - fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { - (&**self).write_all(buf) - } - - #[inline] - fn write_all_vectored(&mut self, bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - (&**self).write_all_vectored(bufs) - } - - #[inline] - fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { - (&**self).write_fmt(fmt) - } -} diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 38c8120d66fc6..b1dbcf1f5bd46 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -306,11 +306,14 @@ pub use alloc_crate::io::RawOsError; pub use alloc_crate::io::SimpleMessage; #[unstable(feature = "io_const_error", issue = "133448")] pub use alloc_crate::io::const_error; +#[allow(unused_imports, reason = "only used by certain platforms")] +pub(crate) use alloc_crate::io::default_write_vectored; #[unstable(feature = "read_buf", issue = "78485")] pub use alloc_crate::io::{BorrowedBuf, BorrowedCursor}; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc_crate::io::{ - Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, empty, repeat, sink, + Chain, Empty, Error, ErrorKind, Repeat, Result, Seek, SeekFrom, Sink, Take, Write, empty, + repeat, sink, }; pub(crate) use alloc_crate::io::{IoHandle, stream_len_default}; #[stable(feature = "iovec", since = "1.36.0")] @@ -338,7 +341,7 @@ pub use self::{ stdio::{Stderr, StderrLock, Stdin, StdinLock, Stdout, StdoutLock, stderr, stdin, stdout}, }; use crate::mem::MaybeUninit; -use crate::{cmp, fmt, slice, str}; +use crate::{cmp, slice, str}; mod buffered; pub(crate) mod copy; @@ -549,14 +552,6 @@ where read(buf) } -pub(crate) fn default_write_vectored(write: F, bufs: &[IoSlice<'_>]) -> Result -where - F: FnOnce(&[u8]) -> Result, -{ - let buf = bufs.iter().find(|b| !b.is_empty()).map_or(&[][..], |b| &**b); - write(buf) -} - pub(crate) fn default_read_exact(this: &mut R, mut buf: &mut [u8]) -> Result<()> { while !buf.is_empty() { match this.read(buf) { @@ -600,47 +595,6 @@ pub(crate) fn default_read_buf_exact( Ok(()) } -pub(crate) fn default_write_fmt( - this: &mut W, - args: fmt::Arguments<'_>, -) -> Result<()> { - // Create a shim which translates a `Write` to a `fmt::Write` and saves off - // I/O errors, instead of discarding them. - struct Adapter<'a, T: ?Sized + 'a> { - inner: &'a mut T, - error: Result<()>, - } - - impl fmt::Write for Adapter<'_, T> { - fn write_str(&mut self, s: &str) -> fmt::Result { - match self.inner.write_all(s.as_bytes()) { - Ok(()) => Ok(()), - Err(e) => { - self.error = Err(e); - Err(fmt::Error) - } - } - } - } - - let mut output = Adapter { inner: this, error: Ok(()) }; - match fmt::write(&mut output, args) { - Ok(()) => Ok(()), - Err(..) => { - // Check whether the error came from the underlying `Write`. - if output.error.is_err() { - output.error - } else { - // This shouldn't happen: the underlying stream did not error, - // but somehow the formatter still errored? - panic!( - "a formatting trait implementation returned an error when the underlying stream did not" - ); - } - } - } -} - /// The `Read` trait allows for reading bytes from a source. /// /// Implementors of the `Read` trait are called 'readers'. @@ -1403,365 +1357,6 @@ pub fn read_to_string(mut reader: R) -> Result { Ok(buf) } -/// A trait for objects which are byte-oriented sinks. -/// -/// Implementors of the `Write` trait are sometimes called 'writers'. -/// -/// Writers are defined by two required methods, [`write`] and [`flush`]: -/// -/// * The [`write`] method will attempt to write some data into the object, -/// returning how many bytes were successfully written. -/// -/// * The [`flush`] method is useful for adapters and explicit buffers -/// themselves for ensuring that all buffered data has been pushed out to the -/// 'true sink'. -/// -/// Writers are intended to be composable with one another. Many implementors -/// throughout [`std::io`] take and provide types which implement the `Write` -/// trait. -/// -/// [`write`]: Write::write -/// [`flush`]: Write::flush -/// [`std::io`]: self -/// -/// # Examples -/// -/// ```no_run -/// use std::io::prelude::*; -/// use std::fs::File; -/// -/// fn main() -> std::io::Result<()> { -/// let data = b"some bytes"; -/// -/// let mut pos = 0; -/// let mut buffer = File::create("foo.txt")?; -/// -/// while pos < data.len() { -/// let bytes_written = buffer.write(&data[pos..])?; -/// pos += bytes_written; -/// } -/// Ok(()) -/// } -/// ``` -/// -/// The trait also provides convenience methods like [`write_all`], which calls -/// `write` in a loop until its entire input has been written. -/// -/// [`write_all`]: Write::write_all -#[stable(feature = "rust1", since = "1.0.0")] -#[doc(notable_trait)] -#[cfg_attr(not(test), rustc_diagnostic_item = "IoWrite")] -pub trait Write { - /// Writes a buffer into this writer, returning how many bytes were written. - /// - /// This function will attempt to write the entire contents of `buf`, but - /// the entire write might not succeed, or the write may also generate an - /// error. Typically, a call to `write` represents one attempt to write to - /// any wrapped object. - /// - /// Calls to `write` are not guaranteed to block waiting for data to be - /// written, and a write which would otherwise block can be indicated through - /// an [`Err`] variant. - /// - /// If this method consumed `n > 0` bytes of `buf` it must return [`Ok(n)`]. - /// If the return value is `Ok(n)` then `n` must satisfy `n <= buf.len()`. - /// A return value of `Ok(0)` typically means that the underlying object is - /// no longer able to accept bytes and will likely not be able to in the - /// future as well, or that the buffer provided is empty. - /// - /// # Errors - /// - /// Each call to `write` may generate an I/O error indicating that the - /// operation could not be completed. If an error is returned then no bytes - /// in the buffer were written to this writer. - /// - /// It is **not** considered an error if the entire buffer could not be - /// written to this writer. - /// - /// An error of the [`ErrorKind::Interrupted`] kind is non-fatal and the - /// write operation should be retried if there is nothing else to do. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// // Writes some prefix of the byte string, not necessarily all of it. - /// buffer.write(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - /// - /// [`Ok(n)`]: Ok - #[stable(feature = "rust1", since = "1.0.0")] - fn write(&mut self, buf: &[u8]) -> Result; - - /// Like [`write`], except that it writes from a slice of buffers. - /// - /// Data is copied from each buffer in order, with the final buffer - /// read from possibly being only partially consumed. This method must - /// behave as a call to [`write`] with the buffers concatenated would. - /// - /// The default implementation calls [`write`] with either the first nonempty - /// buffer provided, or an empty one if none exists. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::IoSlice; - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let data1 = [1; 8]; - /// let data2 = [15; 8]; - /// let io_slice1 = IoSlice::new(&data1); - /// let io_slice2 = IoSlice::new(&data2); - /// - /// let mut buffer = File::create("foo.txt")?; - /// - /// // Writes some prefix of the byte string, not necessarily all of it. - /// buffer.write_vectored(&[io_slice1, io_slice2])?; - /// Ok(()) - /// } - /// ``` - /// - /// [`write`]: Write::write - #[stable(feature = "iovec", since = "1.36.0")] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> Result { - default_write_vectored(|b| self.write(b), bufs) - } - - /// Determines if this `Write`r has an efficient [`write_vectored`] - /// implementation. - /// - /// If a `Write`r does not override the default [`write_vectored`] - /// implementation, code using it may want to avoid the method all together - /// and coalesce writes into a single buffer for higher performance. - /// - /// The default implementation returns `false`. - /// - /// [`write_vectored`]: Write::write_vectored - #[unstable(feature = "can_vector", issue = "69941")] - fn is_write_vectored(&self) -> bool { - false - } - - /// Flushes this output stream, ensuring that all intermediately buffered - /// contents reach their destination. - /// - /// # Errors - /// - /// It is considered an error if not all bytes could be written due to - /// I/O errors or EOF being reached. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::io::BufWriter; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = BufWriter::new(File::create("foo.txt")?); - /// - /// buffer.write_all(b"some bytes")?; - /// buffer.flush()?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn flush(&mut self) -> Result<()>; - - /// Attempts to write an entire buffer into this writer. - /// - /// This method will continuously call [`write`] until there is no more data - /// to be written or an error of non-[`ErrorKind::Interrupted`] kind is - /// returned. This method will not return until the entire buffer has been - /// successfully written or such an error occurs. The first error that is - /// not of [`ErrorKind::Interrupted`] kind generated from this method will be - /// returned. - /// - /// If the buffer contains no data, this will never call [`write`]. - /// - /// # Errors - /// - /// This function will return the first error of - /// non-[`ErrorKind::Interrupted`] kind that [`write`] returns. - /// - /// [`write`]: Write::write - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// buffer.write_all(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn write_all(&mut self, mut buf: &[u8]) -> Result<()> { - while !buf.is_empty() { - match self.write(buf) { - Ok(0) => { - return Err(Error::WRITE_ALL_EOF); - } - Ok(n) => buf = &buf[n..], - Err(ref e) if e.is_interrupted() => {} - Err(e) => return Err(e), - } - } - Ok(()) - } - - /// Attempts to write multiple buffers into this writer. - /// - /// This method will continuously call [`write_vectored`] until there is no - /// more data to be written or an error of non-[`ErrorKind::Interrupted`] - /// kind is returned. This method will not return until all buffers have - /// been successfully written or such an error occurs. The first error that - /// is not of [`ErrorKind::Interrupted`] kind generated from this method - /// will be returned. - /// - /// If the buffer contains no data, this will never call [`write_vectored`]. - /// - /// # Notes - /// - /// Unlike [`write_vectored`], this takes a *mutable* reference to - /// a slice of [`IoSlice`]s, not an immutable one. That's because we need to - /// modify the slice to keep track of the bytes already written. - /// - /// Once this function returns, the contents of `bufs` are unspecified, as - /// this depends on how many calls to [`write_vectored`] were necessary. It is - /// best to understand this function as taking ownership of `bufs` and to - /// not use `bufs` afterwards. The underlying buffers, to which the - /// [`IoSlice`]s point (but not the [`IoSlice`]s themselves), are unchanged and - /// can be reused. - /// - /// [`write_vectored`]: Write::write_vectored - /// - /// # Examples - /// - /// ``` - /// #![feature(write_all_vectored)] - /// # fn main() -> std::io::Result<()> { - /// - /// use std::io::{Write, IoSlice}; - /// - /// let mut writer = Vec::new(); - /// let bufs = &mut [ - /// IoSlice::new(&[1]), - /// IoSlice::new(&[2, 3]), - /// IoSlice::new(&[4, 5, 6]), - /// ]; - /// - /// writer.write_all_vectored(bufs)?; - /// // Note: the contents of `bufs` is now undefined, see the Notes section. - /// - /// assert_eq!(writer, &[1, 2, 3, 4, 5, 6]); - /// # Ok(()) } - /// ``` - #[unstable(feature = "write_all_vectored", issue = "70436")] - fn write_all_vectored(&mut self, mut bufs: &mut [IoSlice<'_>]) -> Result<()> { - // Guarantee that bufs is empty if it contains no data, - // to avoid calling write_vectored if there is no data to be written. - IoSlice::advance_slices(&mut bufs, 0); - while !bufs.is_empty() { - match self.write_vectored(bufs) { - Ok(0) => { - return Err(Error::WRITE_ALL_EOF); - } - Ok(n) => IoSlice::advance_slices(&mut bufs, n), - Err(ref e) if e.is_interrupted() => {} - Err(e) => return Err(e), - } - } - Ok(()) - } - - /// Writes a formatted string into this writer, returning any error - /// encountered. - /// - /// This method is primarily used to interface with the - /// [`format_args!()`] macro, and it is rare that this should - /// explicitly be called. The [`write!()`] macro should be favored to - /// invoke this method instead. - /// - /// This function internally uses the [`write_all`] method on - /// this trait and hence will continuously write data so long as no errors - /// are received. This also means that partial writes are not indicated in - /// this signature. - /// - /// [`write_all`]: Write::write_all - /// - /// # Errors - /// - /// This function will return any I/O error reported while formatting. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::prelude::*; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// // this call - /// write!(buffer, "{:.*}", 2, 1.234567)?; - /// // turns into this: - /// buffer.write_fmt(format_args!("{:.*}", 2, 1.234567))?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> Result<()> { - if let Some(s) = args.as_statically_known_str() { - self.write_all(s.as_bytes()) - } else { - default_write_fmt(self, args) - } - } - - /// Creates a "by reference" adapter for this instance of `Write`. - /// - /// The returned adapter also implements `Write` and will simply borrow this - /// current writer. - /// - /// # Examples - /// - /// ```no_run - /// use std::io::Write; - /// use std::fs::File; - /// - /// fn main() -> std::io::Result<()> { - /// let mut buffer = File::create("foo.txt")?; - /// - /// let reference = buffer.by_ref(); - /// - /// // we can use reference just like our original buffer - /// reference.write_all(b"some bytes")?; - /// Ok(()) - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - fn by_ref(&mut self) -> &mut Self - where - Self: Sized, - { - self - } -} - fn read_until(r: &mut R, delim: u8, buf: &mut Vec) -> Result { let mut read = 0; loop { diff --git a/library/std/src/io/util.rs b/library/std/src/io/util.rs index ce2137f9567c7..ac3e0f84d1e80 100644 --- a/library/std/src/io/util.rs +++ b/library/std/src/io/util.rs @@ -3,10 +3,7 @@ #[cfg(test)] mod tests; -use crate::fmt; -use crate::io::{ - self, BorrowedCursor, BufRead, Empty, IoSlice, IoSliceMut, Read, Repeat, Sink, Write, -}; +use crate::io::{self, BorrowedCursor, BufRead, Empty, IoSliceMut, Read, Repeat}; #[stable(feature = "rust1", since = "1.0.0")] impl Read for Empty { @@ -83,84 +80,6 @@ impl BufRead for Empty { } } -#[stable(feature = "empty_write", since = "1.73.0")] -impl Write for Empty { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "empty_write", since = "1.73.0")] -impl Write for &Empty { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl Read for Repeat { #[inline] @@ -213,81 +132,3 @@ impl Read for Repeat { true } } - -#[stable(feature = "rust1", since = "1.0.0")] -impl Write for Sink { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -#[stable(feature = "write_mt", since = "1.48.0")] -impl Write for &Sink { - #[inline] - fn write(&mut self, buf: &[u8]) -> io::Result { - Ok(buf.len()) - } - - #[inline] - fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result { - let total_len = bufs.iter().map(|b| b.len()).sum(); - Ok(total_len) - } - - #[inline] - fn is_write_vectored(&self) -> bool { - true - } - - #[inline] - fn write_all(&mut self, _buf: &[u8]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_all_vectored(&mut self, _bufs: &mut [IoSlice<'_>]) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn write_fmt(&mut self, _args: fmt::Arguments<'_>) -> io::Result<()> { - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index db37df63c5763..7cf576ecd8803 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -322,6 +322,7 @@ #![feature(borrowed_buf_init)] #![feature(bstr)] #![feature(bstr_internals)] +#![feature(can_vector)] #![feature(cast_maybe_uninit)] #![feature(char_internals)] #![feature(clone_to_uninit)] @@ -389,6 +390,7 @@ #![feature(ub_checks)] #![feature(uint_carryless_mul)] #![feature(used_with_arg)] +#![feature(write_all_vectored)] // tidy-alphabetical-end // // Library features (alloc): diff --git a/library/std/src/sys/alloc/hermit.rs b/library/std/src/sys/alloc/hermit.rs index 77f8200a70a64..9afcb315f4ab5 100644 --- a/library/std/src/sys/alloc/hermit.rs +++ b/library/std/src/sys/alloc/hermit.rs @@ -1,27 +1,24 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - let size = layout.size(); - let align = layout.align(); - unsafe { hermit_abi::malloc(size, align) } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + let size = layout.size(); + let align = layout.align(); + unsafe { hermit_abi::malloc(size, align) } +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - let size = layout.size(); - let align = layout.align(); - unsafe { - hermit_abi::free(ptr, size, align); - } +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + let size = layout.size(); + let align = layout.align(); + unsafe { + hermit_abi::free(ptr, size, align); } +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - let size = layout.size(); - let align = layout.align(); - unsafe { hermit_abi::realloc(ptr, size, align, new_size) } - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + let size = layout.size(); + let align = layout.align(); + unsafe { hermit_abi::realloc(ptr, size, align, new_size) } } diff --git a/library/std/src/sys/alloc/mod.rs b/library/std/src/sys/alloc/mod.rs index f2f1d1c7feceb..73d35781f09bf 100644 --- a/library/std/src/sys/alloc/mod.rs +++ b/library/std/src/sys/alloc/mod.rs @@ -1,6 +1,6 @@ #![forbid(unsafe_op_in_unsafe_fn)] -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; use crate::ptr; // The minimum alignment guaranteed by the architecture. This value is used to @@ -47,21 +47,16 @@ const MIN_ALIGN: usize = if cfg!(any( }; #[allow(dead_code)] -unsafe fn realloc_fallback( - alloc: &System, - ptr: *mut u8, - old_layout: Layout, - new_size: usize, -) -> *mut u8 { +unsafe fn realloc_fallback(ptr: *mut u8, old_layout: Layout, new_size: usize) -> *mut u8 { // SAFETY: Docs for GlobalAlloc::realloc require this to be valid unsafe { let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); - let new_ptr = GlobalAlloc::alloc(alloc, new_layout); + let new_ptr = alloc(new_layout); if !new_ptr.is_null() { let size = usize::min(old_layout.size(), new_size); ptr::copy_nonoverlapping(ptr, new_ptr, size); - GlobalAlloc::dealloc(alloc, ptr, old_layout); + dealloc(ptr, old_layout); } new_ptr @@ -76,35 +71,69 @@ cfg_select! { target_os = "trusty", ) => { mod unix; + use unix as imp; } target_os = "windows" => { mod windows; + use windows as imp; } target_os = "hermit" => { mod hermit; + use hermit as imp; } target_os = "motor" => { mod motor; + use motor as imp; } all(target_vendor = "fortanix", target_env = "sgx") => { mod sgx; + use sgx as imp; } target_os = "solid_asp3" => { mod solid; + use solid as imp; } target_os = "uefi" => { mod uefi; + use uefi as imp; } target_os = "vexos" => { mod vexos; + use vexos as imp; } target_family = "wasm" => { mod wasm; + use wasm as imp; } target_os = "xous" => { mod xous; + use xous as imp; } target_os = "zkvm" => { mod zkvm; + use zkvm as imp; + } +} + +pub use imp::{alloc, dealloc, realloc}; + +cfg_select! { + any( + target_os = "hermit", + target_os = "solid_asp3", + target_os = "uefi", + target_os = "zkvm", + ) => { + #[inline] + pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + let ptr = unsafe { alloc(layout) }; + if !ptr.is_null() { + unsafe { ptr.write_bytes(0, layout.size()) }; + } + ptr + } + } + _ => { + pub use imp::alloc_zeroed; } } diff --git a/library/std/src/sys/alloc/motor.rs b/library/std/src/sys/alloc/motor.rs index 271e3c40c26ae..090dc198bb500 100644 --- a/library/std/src/sys/alloc/motor.rs +++ b/library/std/src/sys/alloc/motor.rs @@ -1,28 +1,13 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // SAFETY: same requirements as in GlobalAlloc::alloc. - moto_rt::alloc::alloc(layout) - } - - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // SAFETY: same requirements as in GlobalAlloc::alloc_zeroed. - moto_rt::alloc::alloc_zeroed(layout) - } - - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - // SAFETY: same requirements as in GlobalAlloc::dealloc. - unsafe { moto_rt::alloc::dealloc(ptr, layout) } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + moto_rt::alloc::alloc(layout) +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - // SAFETY: same requirements as in GlobalAlloc::realloc. - unsafe { moto_rt::alloc::realloc(ptr, layout, new_size) } - } +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + moto_rt::alloc::alloc_zeroed(layout) } + +pub use moto_rt::alloc::{dealloc, realloc}; diff --git a/library/std/src/sys/alloc/sgx.rs b/library/std/src/sys/alloc/sgx.rs index afdef7a5cb647..c20761259048e 100644 --- a/library/std/src/sys/alloc/sgx.rs +++ b/library/std/src/sys/alloc/sgx.rs @@ -1,4 +1,4 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; use crate::ptr; use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; use crate::sys::pal::abi::mem as sgx_mem; @@ -57,31 +57,28 @@ unsafe impl dlmalloc::Allocator for Sgx { } } -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // SAFETY: the caller must uphold the safety contract for `malloc` - unsafe { DLMALLOC.lock().malloc(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + // SAFETY: the caller must uphold the safety contract for `malloc` + unsafe { DLMALLOC.lock().malloc(layout.size(), layout.align()) } +} - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // SAFETY: the caller must uphold the safety contract for `malloc` - unsafe { DLMALLOC.lock().calloc(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + // SAFETY: the caller must uphold the safety contract for `malloc` + unsafe { DLMALLOC.lock().calloc(layout.size(), layout.align()) } +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - // SAFETY: the caller must uphold the safety contract for `malloc` - unsafe { DLMALLOC.lock().free(ptr, layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + // SAFETY: the caller must uphold the safety contract for `malloc` + unsafe { DLMALLOC.lock().free(ptr, layout.size(), layout.align()) } +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - // SAFETY: the caller must uphold the safety contract for `malloc` - unsafe { DLMALLOC.lock().realloc(ptr, layout.size(), layout.align(), new_size) } - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: the caller must uphold the safety contract for `malloc` + unsafe { DLMALLOC.lock().realloc(ptr, layout.size(), layout.align(), new_size) } } // The following functions are needed by libunwind. These symbols are named diff --git a/library/std/src/sys/alloc/solid.rs b/library/std/src/sys/alloc/solid.rs index 47cfa2eb1162b..5d3f396f0dd11 100644 --- a/library/std/src/sys/alloc/solid.rs +++ b/library/std/src/sys/alloc/solid.rs @@ -1,30 +1,27 @@ use super::{MIN_ALIGN, realloc_fallback}; -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - unsafe { libc::malloc(layout.size()) as *mut u8 } - } else { - unsafe { libc::memalign(layout.align(), layout.size()) as *mut u8 } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + unsafe { libc::malloc(layout.size()) as *mut u8 } + } else { + unsafe { libc::memalign(layout.align(), layout.size()) as *mut u8 } } +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - unsafe { libc::free(ptr as *mut libc::c_void) } - } +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, _layout: Layout) { + unsafe { libc::free(ptr as *mut libc::c_void) } +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - unsafe { - if layout.align() <= MIN_ALIGN && layout.align() <= new_size { - libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 - } else { - realloc_fallback(self, ptr, layout, new_size) - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + unsafe { + if layout.align() <= MIN_ALIGN && layout.align() <= new_size { + libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 + } else { + realloc_fallback(ptr, layout, new_size) } } } diff --git a/library/std/src/sys/alloc/uefi.rs b/library/std/src/sys/alloc/uefi.rs index 5221876e90866..aa7fcc9fe110c 100644 --- a/library/std/src/sys/alloc/uefi.rs +++ b/library/std/src/sys/alloc/uefi.rs @@ -3,47 +3,48 @@ use r_efi::protocols::loaded_image; -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; use crate::sync::OnceLock; use crate::sys::pal::helpers; -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - static EFI_MEMORY_TYPE: OnceLock = OnceLock::new(); - - // Return null pointer if boot services are not available - if crate::os::uefi::env::boot_services().is_none() { - return crate::ptr::null_mut(); - } - - // If boot services is valid then SystemTable is not null. - let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); - - // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this - // will never fail. - let mem_type = EFI_MEMORY_TYPE.get_or_init(|| { - let protocol = helpers::image_handle_protocol::( - loaded_image::PROTOCOL_GUID, - ) - .unwrap(); - // Gives allocations the memory type that the data sections were loaded as. - unsafe { (*protocol.as_ptr()).image_data_type } - }); - - // The caller must ensure non-0 layout - unsafe { r_efi_alloc::raw::alloc(system_table, layout, *mem_type) } +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + static EFI_MEMORY_TYPE: OnceLock = OnceLock::new(); + + // Return null pointer if boot services are not available + if crate::os::uefi::env::boot_services().is_none() { + return crate::ptr::null_mut(); } - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - // Do nothing if boot services are not available - if crate::os::uefi::env::boot_services().is_none() { - return; - } + // If boot services is valid then SystemTable is not null. + let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); + + // Each loaded image has an image handle that supports `EFI_LOADED_IMAGE_PROTOCOL`. Thus, this + // will never fail. + let mem_type = EFI_MEMORY_TYPE.get_or_init(|| { + let protocol = + helpers::image_handle_protocol::(loaded_image::PROTOCOL_GUID) + .unwrap(); + // Gives allocations the memory type that the data sections were loaded as. + unsafe { (*protocol.as_ptr()).image_data_type } + }); + + // The caller must ensure non-0 layout + unsafe { r_efi_alloc::raw::alloc(system_table, layout, *mem_type) } +} - // If boot services is valid then SystemTable is not null. - let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); - // The caller must ensure non-0 layout - unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) } +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + // Do nothing if boot services are not available + if crate::os::uefi::env::boot_services().is_none() { + return; } + + // If boot services is valid then SystemTable is not null. + let system_table = crate::os::uefi::env::system_table().as_ptr().cast(); + // The caller must ensure non-0 layout + unsafe { r_efi_alloc::raw::dealloc(system_table, ptr, layout) } +} + +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: this is just a `pub` wrapper. + unsafe { super::realloc_fallback(ptr, layout, new_size) } } diff --git a/library/std/src/sys/alloc/unix.rs b/library/std/src/sys/alloc/unix.rs index 3d369b08abc77..d6de8bf28c44c 100644 --- a/library/std/src/sys/alloc/unix.rs +++ b/library/std/src/sys/alloc/unix.rs @@ -1,60 +1,57 @@ use super::{MIN_ALIGN, realloc_fallback}; -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; use crate::ptr; -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // jemalloc provides alignment less than MIN_ALIGN for small allocations. - // So only rely on MIN_ALIGN if size >= align. - // Also see and - // . - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - unsafe { libc::malloc(layout.size()) as *mut u8 } - } else { - // `posix_memalign` returns a non-aligned value if supplied a very - // large alignment on older versions of Apple's platforms (unknown - // exactly which version range, but the issue is definitely - // present in macOS 10.14 and iOS 13.3). - // - // - #[cfg(target_vendor = "apple")] - { - if layout.align() > (1 << 31) { - return ptr::null_mut(); - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + // jemalloc provides alignment less than MIN_ALIGN for small allocations. + // So only rely on MIN_ALIGN if size >= align. + // Also see and + // . + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + unsafe { libc::malloc(layout.size()) as *mut u8 } + } else { + // `posix_memalign` returns a non-aligned value if supplied a very + // large alignment on older versions of Apple's platforms (unknown + // exactly which version range, but the issue is definitely + // present in macOS 10.14 and iOS 13.3). + // + // + #[cfg(target_vendor = "apple")] + { + if layout.align() > (1 << 31) { + return ptr::null_mut(); } - unsafe { aligned_malloc(&layout) } } + unsafe { aligned_malloc(&layout) } } +} - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // See the comment above in `alloc` for why this check looks the way it does. - if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - unsafe { libc::calloc(layout.size(), 1) as *mut u8 } - } else { - let ptr = unsafe { self.alloc(layout) }; - if !ptr.is_null() { - unsafe { ptr::write_bytes(ptr, 0, layout.size()) }; - } - ptr +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + // See the comment above in `alloc` for why this check looks the way it does. + if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { + unsafe { libc::calloc(layout.size(), 1) as *mut u8 } + } else { + let ptr = unsafe { alloc(layout) }; + if !ptr.is_null() { + unsafe { ptr::write_bytes(ptr, 0, layout.size()) }; } + ptr } +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { - unsafe { libc::free(ptr as *mut libc::c_void) } - } +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, _layout: Layout) { + unsafe { libc::free(ptr as *mut libc::c_void) } +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - if layout.align() <= MIN_ALIGN && layout.align() <= new_size { - unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 } - } else { - unsafe { realloc_fallback(self, ptr, layout, new_size) } - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if layout.align() <= MIN_ALIGN && layout.align() <= new_size { + unsafe { libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 } + } else { + unsafe { realloc_fallback(ptr, layout, new_size) } } } diff --git a/library/std/src/sys/alloc/vexos.rs b/library/std/src/sys/alloc/vexos.rs index 5ccf6625ed626..6aba6dc474911 100644 --- a/library/std/src/sys/alloc/vexos.rs +++ b/library/std/src/sys/alloc/vexos.rs @@ -1,7 +1,7 @@ // FIXME(static_mut_refs): use raw pointers instead of references #![allow(static_mut_refs)] -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; use crate::ptr; use crate::sync::atomic::{AtomicBool, Ordering}; @@ -60,37 +60,34 @@ unsafe impl dlmalloc::Allocator for Vexos { } } -#[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] +pub unsafe fn alloc(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] +pub unsafe fn alloc_zeroed(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] +pub unsafe fn dealloc(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) } - } +#[inline] +pub unsafe fn realloc(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) } } diff --git a/library/std/src/sys/alloc/wasm.rs b/library/std/src/sys/alloc/wasm.rs index 48e2fdd4eccec..995230aebaa01 100644 --- a/library/std/src/sys/alloc/wasm.rs +++ b/library/std/src/sys/alloc/wasm.rs @@ -18,7 +18,7 @@ use core::cell::SyncUnsafeCell; -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; struct SyncDlmalloc(dlmalloc::Dlmalloc); unsafe impl Sync for SyncDlmalloc {} @@ -26,39 +26,36 @@ unsafe impl Sync for SyncDlmalloc {} static DLMALLOC: SyncUnsafeCell = SyncUnsafeCell::new(SyncDlmalloc(dlmalloc::Dlmalloc::new())); -#[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 the lock gives us unique and non-reentrant access. - // Calling malloc() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { (*DLMALLOC.get()).0.malloc(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling malloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { (*DLMALLOC.get()).0.malloc(layout.size(), layout.align()) } +} - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. - // Calling calloc() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { (*DLMALLOC.get()).0.calloc(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling calloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { (*DLMALLOC.get()).0.calloc(layout.size(), layout.align()) } +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. - // Calling free() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { (*DLMALLOC.get()).0.free(ptr, layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling free() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { (*DLMALLOC.get()).0.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 the lock gives us unique and non-reentrant access. - // Calling realloc() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { (*DLMALLOC.get()).0.realloc(ptr, layout.size(), layout.align(), new_size) } - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling realloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { (*DLMALLOC.get()).0.realloc(ptr, layout.size(), layout.align(), new_size) } } #[cfg(target_feature = "atomics")] diff --git a/library/std/src/sys/alloc/windows.rs b/library/std/src/sys/alloc/windows.rs index 9336a6ec085aa..1d75cbd9d54f1 100644 --- a/library/std/src/sys/alloc/windows.rs +++ b/library/std/src/sys/alloc/windows.rs @@ -1,5 +1,18 @@ +//! Implements `System` on Windows. +//! +//! All pointers returned by this allocator have, in addition to the guarantees of `GlobalAlloc`, the +//! following properties: +//! +//! If the pointer was allocated or reallocated with a `layout` specifying an alignment <= `MIN_ALIGN` +//! the pointer will be aligned to at least `MIN_ALIGN` and point to the start of the allocated block. +//! +//! If the pointer was allocated or reallocated with a `layout` specifying an alignment > `MIN_ALIGN` +//! the pointer will be aligned to the specified alignment and not point to the start of the allocated block. +//! Instead there will be a header readable directly before the returned pointer, containing the actual +//! location of the start of the block. + use super::{MIN_ALIGN, realloc_fallback}; -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; use crate::ffi::c_void; use crate::mem::MaybeUninit; use crate::ptr; @@ -150,70 +163,56 @@ unsafe fn allocate(layout: Layout, zeroed: bool) -> *mut u8 { } } -// All pointers returned by this allocator have, in addition to the guarantees of `GlobalAlloc`, the -// following properties: -// -// If the pointer was allocated or reallocated with a `layout` specifying an alignment <= `MIN_ALIGN` -// the pointer will be aligned to at least `MIN_ALIGN` and point to the start of the allocated block. -// -// If the pointer was allocated or reallocated with a `layout` specifying an alignment > `MIN_ALIGN` -// the pointer will be aligned to the specified alignment and not point to the start of the allocated block. -// Instead there will be a header readable directly before the returned pointer, containing the actual -// location of the start of the block. -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System` - let zeroed = false; - unsafe { allocate(layout, zeroed) } - } +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System` + let zeroed = false; + unsafe { allocate(layout, zeroed) } +} - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System` - let zeroed = true; - unsafe { allocate(layout, zeroed) } - } +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + // SAFETY: Pointers returned by `allocate` satisfy the guarantees of `System` + let zeroed = true; + unsafe { allocate(layout, zeroed) } +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - let block = { - if layout.align() <= MIN_ALIGN { - ptr - } else { - // The location of the start of the block is stored in the padding before `ptr`. +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + let block = { + if layout.align() <= MIN_ALIGN { + ptr + } else { + // The location of the start of the block is stored in the padding before `ptr`. - // SAFETY: Because of the contract of `System`, `ptr` is guaranteed to be non-null - // and have a header readable directly before it. - unsafe { ptr::read((ptr as *mut Header).sub(1)).0 } - } - }; + // SAFETY: Because of the contract of `System`, `ptr` is guaranteed to be non-null + // and have a header readable directly before it. + unsafe { ptr::read((ptr as *mut Header).sub(1)).0 } + } + }; + // because `ptr` has been successfully allocated with this allocator, + // there must be a valid process heap. + let heap = get_process_heap(); + + // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`, + // `block` is a pointer to the start of an allocated block. + unsafe { HeapFree(heap, 0, block.cast::()) }; +} + +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if layout.align() <= MIN_ALIGN { // because `ptr` has been successfully allocated with this allocator, // there must be a valid process heap. let heap = get_process_heap(); // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`, - // `block` is a pointer to the start of an allocated block. - unsafe { HeapFree(heap, 0, block.cast::()) }; - } - - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - if layout.align() <= MIN_ALIGN { - // because `ptr` has been successfully allocated with this allocator, - // there must be a valid process heap. - let heap = get_process_heap(); - - // SAFETY: `heap` is a non-null handle returned by `GetProcessHeap`, - // `ptr` is a pointer to the start of an allocated block. - // The returned pointer points to the start of an allocated block. - unsafe { HeapReAlloc(heap, 0, ptr.cast::(), new_size).cast::() } - } else { - // SAFETY: `realloc_fallback` is implemented using `dealloc` and `alloc`, which will - // correctly handle `ptr` and return a pointer satisfying the guarantees of `System` - unsafe { realloc_fallback(self, ptr, layout, new_size) } - } + // `ptr` is a pointer to the start of an allocated block. + // The returned pointer points to the start of an allocated block. + unsafe { HeapReAlloc(heap, 0, ptr.cast::(), new_size).cast::() } + } else { + // SAFETY: `realloc_fallback` is implemented using `dealloc` and `alloc`, which will + // correctly handle `ptr` and return a pointer satisfying the guarantees of `System` + unsafe { realloc_fallback(ptr, layout, new_size) } } } diff --git a/library/std/src/sys/alloc/xous.rs b/library/std/src/sys/alloc/xous.rs index af2dcabc1a23b..74229351f6d22 100644 --- a/library/std/src/sys/alloc/xous.rs +++ b/library/std/src/sys/alloc/xous.rs @@ -1,7 +1,7 @@ // FIXME(static_mut_refs): use raw pointers instead of references #![allow(static_mut_refs)] -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; #[cfg(not(test))] #[unsafe(export_name = "_ZN16__rust_internals3std3sys4xous5alloc8DLMALLOCE")] @@ -13,39 +13,36 @@ unsafe extern "Rust" { static mut DLMALLOC: dlmalloc::Dlmalloc; } -#[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 the lock gives us unique and non-reentrant access. - // Calling malloc() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { DLMALLOC.malloc(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling malloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + 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 the lock gives us unique and non-reentrant access. - // Calling calloc() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { DLMALLOC.calloc(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling calloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + 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 the lock gives us unique and non-reentrant access. - // Calling free() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { DLMALLOC.free(ptr, layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling free() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + 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 the lock gives us unique and non-reentrant access. - // Calling realloc() is safe because preconditions on this function match the trait method preconditions. - let _lock = lock::lock(); - unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: DLMALLOC access is guaranteed to be safe because the lock gives us unique and non-reentrant access. + // Calling realloc() is safe because preconditions on this function match the trait method preconditions. + let _lock = lock::lock(); + unsafe { DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } } mod lock { diff --git a/library/std/src/sys/alloc/zkvm.rs b/library/std/src/sys/alloc/zkvm.rs index a600cfa2220dd..7f39d7fed777e 100644 --- a/library/std/src/sys/alloc/zkvm.rs +++ b/library/std/src/sys/alloc/zkvm.rs @@ -1,15 +1,18 @@ -use crate::alloc::{GlobalAlloc, Layout, System}; +use crate::alloc::Layout; use crate::sys::pal::abi; -#[stable(feature = "alloc_system_type", since = "1.28.0")] -unsafe impl GlobalAlloc for System { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - unsafe { abi::sys_alloc_aligned(layout.size(), layout.align()) } - } +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + unsafe { abi::sys_alloc_aligned(layout.size(), layout.align()) } +} + +#[inline] +pub unsafe fn dealloc(_ptr: *mut u8, _layout: Layout) { + // this allocator never deallocates memory +} - #[inline] - unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) { - // this allocator never deallocates memory - } +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + // SAFETY: this is just a `pub` wrapper. + unsafe { super::realloc_fallback(ptr, layout, new_size) } } diff --git a/library/std/src/sys/io/error/mod.rs b/library/std/src/sys/io/error/mod.rs index cc8cda9aec045..1b6bfea27d3b0 100644 --- a/library/std/src/sys/io/error/mod.rs +++ b/library/std/src/sys/io/error/mod.rs @@ -15,22 +15,14 @@ cfg_select! { mod solid; pub use solid::*; } - target_os = "teeos" => { - mod teeos; - pub use teeos::*; - } target_os = "uefi" => { mod uefi; pub use uefi::*; } - target_family = "unix" => { + any(target_family = "unix", target_os = "wasi", target_os = "teeos") => { mod unix; pub use unix::*; } - target_os = "wasi" => { - mod wasi; - pub use wasi::*; - } target_os = "windows" => { mod windows; pub use windows::*; diff --git a/library/std/src/sys/io/error/teeos.rs b/library/std/src/sys/io/error/teeos.rs deleted file mode 100644 index 18826ffc3894f..0000000000000 --- a/library/std/src/sys/io/error/teeos.rs +++ /dev/null @@ -1,64 +0,0 @@ -use crate::io; - -pub fn errno() -> i32 { - unsafe { (*libc::__errno_location()) as i32 } -} - -#[inline] -pub fn is_interrupted(errno: i32) -> bool { - errno == libc::EINTR -} - -// Note: code below is 1:1 copied from unix/mod.rs -pub fn decode_error_kind(errno: i32) -> io::ErrorKind { - use io::ErrorKind::*; - match errno as libc::c_int { - libc::E2BIG => ArgumentListTooLong, - libc::EADDRINUSE => AddrInUse, - libc::EADDRNOTAVAIL => AddrNotAvailable, - libc::EBUSY => ResourceBusy, - libc::ECONNABORTED => ConnectionAborted, - libc::ECONNREFUSED => ConnectionRefused, - libc::ECONNRESET => ConnectionReset, - libc::EDEADLK => Deadlock, - libc::EDQUOT => QuotaExceeded, - libc::EEXIST => AlreadyExists, - libc::EFBIG => FileTooLarge, - libc::EHOSTUNREACH => HostUnreachable, - libc::EINTR => Interrupted, - libc::EINVAL => InvalidInput, - libc::EISDIR => IsADirectory, - libc::ELOOP => FilesystemLoop, - libc::ENOENT => NotFound, - libc::ENOMEM => OutOfMemory, - libc::ENOSPC => StorageFull, - libc::ENOSYS => Unsupported, - libc::EMLINK => TooManyLinks, - libc::ENAMETOOLONG => InvalidFilename, - libc::ENETDOWN => NetworkDown, - libc::ENETUNREACH => NetworkUnreachable, - libc::ENOTCONN => NotConnected, - libc::ENOTDIR => NotADirectory, - libc::ENOTEMPTY => DirectoryNotEmpty, - libc::EPIPE => BrokenPipe, - libc::EROFS => ReadOnlyFilesystem, - libc::ESPIPE => NotSeekable, - libc::ESTALE => StaleNetworkFileHandle, - libc::ETIMEDOUT => TimedOut, - libc::ETXTBSY => ExecutableFileBusy, - libc::EXDEV => CrossesDevices, - - libc::EACCES | libc::EPERM => PermissionDenied, - - // These two constants can have the same value on some systems, - // but different values on others, so we can't use a match - // clause - x if x == libc::EAGAIN || x == libc::EWOULDBLOCK => WouldBlock, - - _ => Uncategorized, - } -} - -pub fn error_string(_errno: i32) -> String { - "error string unimplemented".to_string() -} diff --git a/library/std/src/sys/io/error/unix.rs b/library/std/src/sys/io/error/unix.rs index 85e3c09782ae2..2feeefa2530c8 100644 --- a/library/std/src/sys/io/error/unix.rs +++ b/library/std/src/sys/io/error/unix.rs @@ -1,8 +1,15 @@ -use crate::ffi::{CStr, c_char, c_int}; +use crate::ffi::c_int; +#[cfg(not(target_os = "teeos"))] +use crate::ffi::{CStr, c_char}; use crate::io; unsafe extern "C" { - #[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] + #[cfg(not(any( + target_os = "dragonfly", + target_os = "vxworks", + target_os = "rtems", + target_os = "wasi" + )))] #[cfg_attr( any( target_os = "linux", @@ -10,6 +17,7 @@ unsafe extern "C" { target_os = "fuchsia", target_os = "l4re", target_os = "hurd", + target_os = "teeos", ), link_name = "__errno_location" )] @@ -37,7 +45,12 @@ unsafe extern "C" { } /// Returns the platform-specific value of errno -#[cfg(not(any(target_os = "dragonfly", target_os = "vxworks", target_os = "rtems")))] +#[cfg(not(any( + target_os = "dragonfly", + target_os = "vxworks", + target_os = "rtems", + target_os = "wasi" +)))] #[inline] pub fn errno() -> i32 { unsafe { (*errno_location()) as i32 } @@ -45,7 +58,12 @@ pub fn errno() -> i32 { /// Sets the platform-specific value of errno // needed for readdir and syscall! -#[cfg(all(not(target_os = "dragonfly"), not(target_os = "vxworks"), not(target_os = "rtems")))] +#[cfg(not(any( + target_os = "dragonfly", + target_os = "vxworks", + target_os = "rtems", + target_os = "wasi", +)))] #[allow(dead_code)] // but not all target cfgs actually end up using it #[inline] pub fn set_errno(e: i32) { @@ -94,6 +112,25 @@ pub fn set_errno(e: i32) { } } +#[cfg(target_os = "wasi")] +unsafe extern "C" { + #[thread_local] + #[link_name = "errno"] + static mut libc_errno: libc::c_int; +} + +#[cfg(target_os = "wasi")] +pub fn errno() -> i32 { + unsafe { libc_errno as i32 } +} + +#[cfg(target_os = "wasi")] +pub fn set_errno(val: i32) { + unsafe { + libc_errno = val; + } +} + #[inline] pub fn is_interrupted(errno: i32) -> bool { errno == libc::EINTR @@ -153,8 +190,9 @@ pub fn decode_error_kind(errno: i32) -> io::ErrorKind { } /// Gets a detailed string description for the given error number. +#[cfg(any(target_family = "unix", target_os = "wasi"))] pub fn error_string(errno: i32) -> String { - const TMPBUF_SZ: usize = 128; + const TMPBUF_SZ: usize = if cfg!(target_os = "wasi") { 1024 } else { 128 }; unsafe extern "C" { #[cfg_attr( @@ -186,3 +224,8 @@ pub fn error_string(errno: i32) -> String { String::from_utf8_lossy(CStr::from_ptr(p).to_bytes()).into() } } + +#[cfg(target_os = "teeos")] +pub fn error_string(_errno: i32) -> String { + "error string unimplemented".to_string() +} diff --git a/library/std/src/sys/io/error/wasi.rs b/library/std/src/sys/io/error/wasi.rs deleted file mode 100644 index 3a0fa37e979bc..0000000000000 --- a/library/std/src/sys/io/error/wasi.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::ffi::CStr; -use crate::io as std_io; - -unsafe extern "C" { - #[thread_local] - #[link_name = "errno"] - static mut libc_errno: libc::c_int; -} - -pub fn errno() -> i32 { - unsafe { libc_errno as i32 } -} - -pub fn set_errno(val: i32) { - unsafe { - libc_errno = val; - } -} - -#[inline] -pub fn is_interrupted(errno: i32) -> bool { - errno == libc::EINTR -} - -pub fn decode_error_kind(errno: i32) -> std_io::ErrorKind { - use std_io::ErrorKind::*; - match errno as libc::c_int { - libc::E2BIG => ArgumentListTooLong, - libc::EADDRINUSE => AddrInUse, - libc::EADDRNOTAVAIL => AddrNotAvailable, - libc::EBUSY => ResourceBusy, - libc::ECONNABORTED => ConnectionAborted, - libc::ECONNREFUSED => ConnectionRefused, - libc::ECONNRESET => ConnectionReset, - libc::EDEADLK => Deadlock, - libc::EDQUOT => QuotaExceeded, - libc::EEXIST => AlreadyExists, - libc::EFBIG => FileTooLarge, - libc::EHOSTUNREACH => HostUnreachable, - libc::EINTR => Interrupted, - libc::EINVAL => InvalidInput, - libc::EISDIR => IsADirectory, - libc::ELOOP => FilesystemLoop, - libc::ENOENT => NotFound, - libc::ENOMEM => OutOfMemory, - libc::ENOSPC => StorageFull, - libc::ENOSYS => Unsupported, - libc::EMLINK => TooManyLinks, - libc::ENAMETOOLONG => InvalidFilename, - libc::ENETDOWN => NetworkDown, - libc::ENETUNREACH => NetworkUnreachable, - libc::ENOTCONN => NotConnected, - libc::ENOTDIR => NotADirectory, - libc::EPIPE => BrokenPipe, - libc::EROFS => ReadOnlyFilesystem, - libc::ESPIPE => NotSeekable, - libc::ESTALE => StaleNetworkFileHandle, - libc::ETIMEDOUT => TimedOut, - libc::ETXTBSY => ExecutableFileBusy, - libc::EXDEV => CrossesDevices, - libc::EINPROGRESS => InProgress, - libc::EMFILE | libc::ENFILE => TooManyOpenFiles, - libc::EOPNOTSUPP => Unsupported, - libc::EACCES | libc::EPERM => PermissionDenied, - libc::EWOULDBLOCK => WouldBlock, - - _ => Uncategorized, - } -} - -pub fn error_string(errno: i32) -> String { - let mut buf = [0 as libc::c_char; 1024]; - - let p = buf.as_mut_ptr(); - unsafe { - if libc::strerror_r(errno as libc::c_int, p, buf.len()) < 0 { - panic!("strerror_r failure"); - } - str::from_utf8(CStr::from_ptr(p).to_bytes()).unwrap().to_owned() - } -} diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs index fe0c103952414..58c8dc43e12ae 100644 --- a/library/std/src/sys/mod.rs +++ b/library/std/src/sys/mod.rs @@ -1,11 +1,11 @@ #![allow(unsafe_op_in_unsafe_fn)] -mod alloc; mod configure_builtins; mod helpers; mod pal; mod personality; +pub mod alloc; pub mod args; pub mod backtrace; pub mod cmath; diff --git a/library/std/src/sys/personality/gcc.rs b/library/std/src/sys/personality/gcc.rs index 019d5629d6d6e..0e20a4192b1b9 100644 --- a/library/std/src/sys/personality/gcc.rs +++ b/library/std/src/sys/personality/gcc.rs @@ -204,6 +204,35 @@ cfg_select! { } } _ => { + #[rustc_force_inline] + unsafe fn sign_lpad(context: *mut uw::_Unwind_Context, lpad: *const u8) -> *const u8 { + cfg_select! { + all(target_abi = "pauthtest", target_arch = "aarch64") => { + // DWARF register number for SP on AArch64. + const SP_REG: i32 = 31; + + unsafe { + let sp = uw::_Unwind_GetGR(context, SP_REG).addr() as u64; + let mut addr = lpad.addr(); + + // `pacib` corresponds to `ptrauth_key_process_dependent_code` in . + core::arch::asm!( + "pacib {addr}, {sp}", + addr = inout(reg) addr, + sp = in(reg) sp, + options(nostack, preserves_flags) + ); + + lpad.with_addr(addr) + } + } + _ => { + let _ = context; + lpad + } + } + } + /// Default personality routine, which is used directly on most targets /// and indirectly on Windows x86_64 and AArch64 via SEH. unsafe extern "C" fn rust_eh_personality_impl( @@ -239,7 +268,8 @@ cfg_select! { exception_object.cast(), ); uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, core::ptr::null()); - uw::_Unwind_SetIP(context, lpad); + let maybe_signed_lpad = sign_lpad(context, lpad); + uw::_Unwind_SetIP(context, maybe_signed_lpad); uw::_URC_INSTALL_CONTEXT } EHAction::Terminate => uw::_URC_FATAL_PHASE2_ERROR, diff --git a/library/std/tests/pipe_subprocess.rs b/library/std/tests/pipe_subprocess.rs index dad1ea6c57377..9643c3b7bdad8 100644 --- a/library/std/tests/pipe_subprocess.rs +++ b/library/std/tests/pipe_subprocess.rs @@ -13,10 +13,16 @@ fn main() { fn parent() { let me = env::current_exe().unwrap(); + // If a custom `runner` is set up for the current target, we'll be + // executing `./runner ./test`, not just `./test`. For such a case, + // use the same arguments for child to avoid executing `runner` + // without an actual executable. + let args = env::args(); let (rx, tx) = pipe().unwrap(); assert!( process::Command::new(me) + .args(args) .env("I_AM_THE_CHILD", "1") .stdout(tx) .status() diff --git a/library/std/tests/process_spawning.rs b/library/std/tests/process_spawning.rs index 93f73ccad3ea4..80e712a2388a1 100644 --- a/library/std/tests/process_spawning.rs +++ b/library/std/tests/process_spawning.rs @@ -26,8 +26,16 @@ fn issue_15149() { env::join_paths(paths).unwrap() }; - let child_output = - process::Command::new("mytest").env("PATH", &path).arg("child").output().unwrap(); + // If a custom `runner` is set up for the current target, we'll be executing `./runner ./test`, + // not just `./test`. For such a case, use the same arguments for child to avoid executing + // `runner` without an actual executable. + let args = env::args(); + let child_output = process::Command::new("mytest") + .args(args) + .env("PATH", &path) + .arg("child") + .output() + .unwrap(); assert!( child_output.status.success(), diff --git a/library/stdarch/.github/workflows/main.yml b/library/stdarch/.github/workflows/main.yml index 6333b32ab8ad4..9e102a15e8ff7 100644 --- a/library/stdarch/.github/workflows/main.yml +++ b/library/stdarch/.github/workflows/main.yml @@ -285,12 +285,13 @@ jobs: include: - target: aarch64_be-unknown-linux-gnu build_std: true - - target: x86_64-unknown-linux-gnu - cc: icx - profile: dev - - target: x86_64-unknown-linux-gnu - cc: icx - profile: release + # getting the icx compiler hits network failures. + # - target: x86_64-unknown-linux-gnu + # cc: icx + # profile: dev + # - target: x86_64-unknown-linux-gnu + # cc: icx + # profile: release exclude: - target: armv7-unknown-linux-gnueabihf cc: gcc @@ -318,7 +319,7 @@ jobs: # Check that the generated files agree with the checked-in versions. check-stdarch-gen: needs: [style] - name: Check stdarch-gen-{arm, loongarch, hexagon, hexagon-scalar} output + name: Check stdarch-gen-{arm, loongarch, hexagon} output runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 @@ -342,11 +343,7 @@ jobs: run: | cargo run -p stdarch-gen-hexagon --release git diff --exit-code - - name: Check hexagon scalar - run: | - cargo run -p stdarch-gen-hexagon-scalar --release - git diff --exit-code - + # Run some tests with Miri. Most stdarch functions use platform-specific intrinsics # that Miri does not support. Also Miri is reltively slow. # diff --git a/library/stdarch/Cargo.lock b/library/stdarch/Cargo.lock index c8cfc21a5a1a1..cf6ce0d38e40c 100644 --- a/library/stdarch/Cargo.lock +++ b/library/stdarch/Cargo.lock @@ -849,13 +849,6 @@ dependencies = [ "stdarch-gen-common", ] -[[package]] -name = "stdarch-gen-hexagon-scalar" -version = "0.1.0" -dependencies = [ - "regex", -] - [[package]] name = "stdarch-gen-loongarch" version = "0.1.0" @@ -869,7 +862,6 @@ version = "0.1.0" dependencies = [ "assert-instr-macro", "cc", - "cfg-if", "rustc-demangle", "simd-test-macro", "wasmprinter", diff --git a/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile index 1b61dd0c1b87a..0ae4172f9dd37 100644 --- a/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile @@ -11,7 +11,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ xz-utils \ wget -RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz +RUN wget https://ci-mirrors.rust-lang.org/llvm/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm @@ -19,5 +19,5 @@ ENV CLANG_PATH="/llvm/bin/clang" ENV GCC_PATH=aarch64-linux-gnu-gcc ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ - CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-aarch64 -cpu max -L /usr/aarch64-linux-gnu" \ + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-aarch64 -cpu max,sve512=on -L /usr/aarch64-linux-gnu" \ OBJDUMP=aarch64-linux-gnu-objdump diff --git a/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile index 70acc2d22a418..0b7a59cc08d83 100644 --- a/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile @@ -19,7 +19,7 @@ RUN curl -L "https://developer.arm.com/-/media/Files/downloads/gnu/14.3.rel1/bin RUN tar -xvf "${TOOLCHAIN}.tar.xz" RUN mkdir /toolchains && mv "./${TOOLCHAIN}" /toolchains -RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz +RUN wget https://ci-mirrors.rust-lang.org/llvm/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm diff --git a/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile b/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile index 3c8a1b5add27f..f4240be7bb47b 100644 --- a/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile +++ b/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile @@ -10,7 +10,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ file \ wget -RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz +RUN wget https://ci-mirrors.rust-lang.org/llvm/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm diff --git a/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile index efbb2b0853371..1ac46dc407395 100644 --- a/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile @@ -13,7 +13,7 @@ RUN wget http://ci-mirrors.rust-lang.org/sde-external-10.8.0-2026-03-15-lin.tar. RUN mkdir intel-sde RUN tar -xJf sde.tar.xz --strip-components=1 -C intel-sde -RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz +RUN wget https://ci-mirrors.rust-lang.org/llvm/llvm-22.1.4-x86_64.tar.gz -O llvm.tar.xz RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm diff --git a/library/stdarch/ci/intrinsic-test.sh b/library/stdarch/ci/intrinsic-test.sh index 8de7a4cfa5221..3966aa88d8f41 100755 --- a/library/stdarch/ci/intrinsic-test.sh +++ b/library/stdarch/ci/intrinsic-test.sh @@ -45,21 +45,25 @@ case ${TARGET} in aarch64_be*) export CFLAGS="-I${AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc/usr/include --sysroot={AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc -Wno-nonportable-vector-initialization" ARCH=aarch64_be + RUNTIME_RUSTFLAGS= ;; aarch64*) export CFLAGS="-I/usr/aarch64-linux-gnu/include/" ARCH=aarch64 + RUNTIME_RUSTFLAGS=-Ctarget-feature=+sve,+sve2 ;; armv7*) export CFLAGS="-I/usr/arm-linux-gnueabihf/include/" ARCH=arm + RUNTIME_RUSTFLAGS= ;; x86_64*) export CFLAGS="-I/usr/include/x86_64-linux-gnu/" ARCH=x86 + RUNTIME_RUSTFLAGS= ;; *) ;; @@ -86,5 +90,5 @@ case "${TARGET}" in ;; esac -cargo test --manifest-path=rust_programs/Cargo.toml --target "${TARGET}" --profile "${PROFILE}" \ - --tests "$@" +RUSTFLAGS="${RUNTIME_RUSTFLAGS}" cargo test --manifest-path=rust_programs/Cargo.toml \ + --target "${TARGET}" --profile "${PROFILE}" --tests --no-fail-fast "$@" diff --git a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs index ac3070918a025..4e5547b1d083e 100644 --- a/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs +++ b/library/stdarch/crates/core_arch/src/aarch64/sve/generated.rs @@ -42211,19 +42211,6 @@ pub fn svtmad_f64(op1: svfloat64_t, op2: svfloat64_t) -> svfloa unsafe { _svtmad_f64(op1, op2, IMM3) } } #[doc = "Interleave even elements from two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn1_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(trn1))] -pub fn svtrn1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.nxv16i1")] - fn _svtrn1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svtrn1_b8(op1, op2) } -} -#[doc = "Interleave even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn1_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -42231,10 +42218,10 @@ pub fn svtrn1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(trn1))] pub fn svtrn1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.nxv8i1")] - fn _svtrn1_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.b16")] + fn _svtrn1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svtrn1_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svtrn1_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn1_b32)"] @@ -42244,10 +42231,10 @@ pub fn svtrn1_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(trn1))] pub fn svtrn1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.nxv4i1")] - fn _svtrn1_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.b32")] + fn _svtrn1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svtrn1_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svtrn1_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn1_b64)"] @@ -42257,10 +42244,10 @@ pub fn svtrn1_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(trn1))] pub fn svtrn1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.nxv2i1")] - fn _svtrn1_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.b64")] + fn _svtrn1_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svtrn1_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svtrn1_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave even elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn1[_f32])"] @@ -42376,6 +42363,19 @@ pub fn svtrn1_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svtrn1_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svtrn1_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Interleave even elements from two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn1[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(trn1))] +pub fn svtrn1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn1.nxv16i1")] + fn _svtrn1_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svtrn1_b8(op1, op2) } +} #[doc = "Interleave even quadwords from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn1q[_f32])"] #[inline] @@ -42491,19 +42491,6 @@ pub fn svtrn1q_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svtrn1q_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } #[doc = "Interleave odd elements from two inputs"] -#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn2_b8)"] -#[inline] -#[target_feature(enable = "sve")] -#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] -#[cfg_attr(test, assert_instr(trn2))] -pub fn svtrn2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { - unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.nxv16i1")] - fn _svtrn2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; - } - unsafe { _svtrn2_b8(op1, op2) } -} -#[doc = "Interleave odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn2_b16)"] #[inline] #[target_feature(enable = "sve")] @@ -42511,10 +42498,10 @@ pub fn svtrn2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(trn2))] pub fn svtrn2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.nxv8i1")] - fn _svtrn2_b16(op1: svbool8_t, op2: svbool8_t) -> svbool8_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.b16")] + fn _svtrn2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svtrn2_b16(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svtrn2_b16(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn2_b32)"] @@ -42524,10 +42511,10 @@ pub fn svtrn2_b16(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(trn2))] pub fn svtrn2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.nxv4i1")] - fn _svtrn2_b32(op1: svbool4_t, op2: svbool4_t) -> svbool4_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.b32")] + fn _svtrn2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svtrn2_b32(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svtrn2_b32(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn2_b64)"] @@ -42537,10 +42524,10 @@ pub fn svtrn2_b32(op1: svbool_t, op2: svbool_t) -> svbool_t { #[cfg_attr(test, assert_instr(trn2))] pub fn svtrn2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t { unsafe extern "unadjusted" { - #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.nxv2i1")] - fn _svtrn2_b64(op1: svbool2_t, op2: svbool2_t) -> svbool2_t; + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.b64")] + fn _svtrn2_b64(op1: svbool_t, op2: svbool_t) -> svbool_t; } - unsafe { _svtrn2_b64(op1.sve_into(), op2.sve_into()).sve_into() } + unsafe { _svtrn2_b64(op1.sve_into(), op2.sve_into()) } } #[doc = "Interleave odd elements from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn2[_f32])"] @@ -42656,6 +42643,19 @@ pub fn svtrn2_u32(op1: svuint32_t, op2: svuint32_t) -> svuint32_t { pub fn svtrn2_u64(op1: svuint64_t, op2: svuint64_t) -> svuint64_t { unsafe { svtrn2_s64(op1.as_signed(), op2.as_signed()).as_unsigned() } } +#[doc = "Interleave odd elements from two inputs"] +#[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn2[_b8])"] +#[inline] +#[target_feature(enable = "sve")] +#[unstable(feature = "stdarch_aarch64_sve", issue = "145052")] +#[cfg_attr(test, assert_instr(trn2))] +pub fn svtrn2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t { + unsafe extern "unadjusted" { + #[cfg_attr(target_arch = "aarch64", link_name = "llvm.aarch64.sve.trn2.nxv16i1")] + fn _svtrn2_b8(op1: svbool_t, op2: svbool_t) -> svbool_t; + } + unsafe { _svtrn2_b8(op1, op2) } +} #[doc = "Interleave odd quadwords from two inputs"] #[doc = "[Arm's documentation](https://developer.arm.com/architectures/instruction-sets/intrinsics/svtrn2q[_f32])"] #[inline] diff --git a/library/stdarch/crates/core_arch/src/hexagon/scalar.rs b/library/stdarch/crates/core_arch/src/hexagon/scalar.rs index 477414de74b85..a42c5c5e10f30 100644 --- a/library/stdarch/crates/core_arch/src/hexagon/scalar.rs +++ b/library/stdarch/crates/core_arch/src/hexagon/scalar.rs @@ -1,3 +1,4 @@ +// This code is automatically generated. DO NOT MODIFY. //! Hexagon scalar intrinsics //! //! This module provides intrinsics for scalar (non-HVX) Hexagon DSP operations, diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs b/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs index 6a6a3ae924d85..311479750601a 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs @@ -199,10 +199,6 @@ unsafe extern "unadjusted" { fn __lasx_xvfclass_s(a: __v8f32) -> __v8i32; #[link_name = "llvm.loongarch.lasx.xvfclass.d"] fn __lasx_xvfclass_d(a: __v4f64) -> __v4i64; - #[link_name = "llvm.loongarch.lasx.xvfrecip.s"] - fn __lasx_xvfrecip_s(a: __v8f32) -> __v8f32; - #[link_name = "llvm.loongarch.lasx.xvfrecip.d"] - fn __lasx_xvfrecip_d(a: __v4f64) -> __v4f64; #[link_name = "llvm.loongarch.lasx.xvfrecipe.s"] fn __lasx_xvfrecipe_s(a: __v8f32) -> __v8f32; #[link_name = "llvm.loongarch.lasx.xvfrecipe.d"] @@ -215,10 +211,6 @@ unsafe extern "unadjusted" { fn __lasx_xvfrint_s(a: __v8f32) -> __v8f32; #[link_name = "llvm.loongarch.lasx.xvfrint.d"] fn __lasx_xvfrint_d(a: __v4f64) -> __v4f64; - #[link_name = "llvm.loongarch.lasx.xvfrsqrt.s"] - fn __lasx_xvfrsqrt_s(a: __v8f32) -> __v8f32; - #[link_name = "llvm.loongarch.lasx.xvfrsqrt.d"] - fn __lasx_xvfrsqrt_d(a: __v4f64) -> __v4f64; #[link_name = "llvm.loongarch.lasx.xvflogb.s"] fn __lasx_xvflogb_s(a: __v8f32) -> __v8f32; #[link_name = "llvm.loongarch.lasx.xvflogb.d"] @@ -265,22 +257,6 @@ unsafe extern "unadjusted" { fn __lasx_xvreplve_d(a: __v4i64, b: i32) -> __v4i64; #[link_name = "llvm.loongarch.lasx.xvpermi.w"] fn __lasx_xvpermi_w(a: __v8i32, b: __v8i32, c: u32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvmuh.b"] - fn __lasx_xvmuh_b(a: __v32i8, b: __v32i8) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvmuh.h"] - fn __lasx_xvmuh_h(a: __v16i16, b: __v16i16) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvmuh.w"] - fn __lasx_xvmuh_w(a: __v8i32, b: __v8i32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvmuh.d"] - fn __lasx_xvmuh_d(a: __v4i64, b: __v4i64) -> __v4i64; - #[link_name = "llvm.loongarch.lasx.xvmuh.bu"] - fn __lasx_xvmuh_bu(a: __v32u8, b: __v32u8) -> __v32u8; - #[link_name = "llvm.loongarch.lasx.xvmuh.hu"] - fn __lasx_xvmuh_hu(a: __v16u16, b: __v16u16) -> __v16u16; - #[link_name = "llvm.loongarch.lasx.xvmuh.wu"] - fn __lasx_xvmuh_wu(a: __v8u32, b: __v8u32) -> __v8u32; - #[link_name = "llvm.loongarch.lasx.xvmuh.du"] - fn __lasx_xvmuh_du(a: __v4u64, b: __v4u64) -> __v4u64; #[link_name = "llvm.loongarch.lasx.xvsllwil.h.b"] fn __lasx_xvsllwil_h_b(a: __v32i8, b: u32) -> __v16i16; #[link_name = "llvm.loongarch.lasx.xvsllwil.w.h"] @@ -443,22 +419,6 @@ unsafe extern "unadjusted" { fn __lasx_xvfrintrne_s(a: __v8f32) -> __v8f32; #[link_name = "llvm.loongarch.lasx.xvfrintrne.d"] fn __lasx_xvfrintrne_d(a: __v4f64) -> __v4f64; - #[link_name = "llvm.loongarch.lasx.xvfrintrz.s"] - fn __lasx_xvfrintrz_s(a: __v8f32) -> __v8f32; - #[link_name = "llvm.loongarch.lasx.xvfrintrz.d"] - fn __lasx_xvfrintrz_d(a: __v4f64) -> __v4f64; - #[link_name = "llvm.loongarch.lasx.xvfrintrp.s"] - fn __lasx_xvfrintrp_s(a: __v8f32) -> __v8f32; - #[link_name = "llvm.loongarch.lasx.xvfrintrp.d"] - fn __lasx_xvfrintrp_d(a: __v4f64) -> __v4f64; - #[link_name = "llvm.loongarch.lasx.xvfrintrm.s"] - fn __lasx_xvfrintrm_s(a: __v8f32) -> __v8f32; - #[link_name = "llvm.loongarch.lasx.xvfrintrm.d"] - fn __lasx_xvfrintrm_d(a: __v4f64) -> __v4f64; - #[link_name = "llvm.loongarch.lasx.xvld"] - fn __lasx_xvld(a: *const i8, b: i32) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvst"] - fn __lasx_xvst(a: __v32i8, b: *mut i8, c: i32); #[link_name = "llvm.loongarch.lasx.xvstelm.b"] fn __lasx_xvstelm_b(a: __v32i8, b: *mut i8, c: i32, d: u32); #[link_name = "llvm.loongarch.lasx.xvstelm.h"] @@ -489,10 +449,6 @@ unsafe extern "unadjusted" { fn __lasx_xvssrln_w_d(a: __v4i64, b: __v4i64) -> __v8i32; #[link_name = "llvm.loongarch.lasx.xvldi"] fn __lasx_xvldi(a: i32) -> __v4i64; - #[link_name = "llvm.loongarch.lasx.xvldx"] - fn __lasx_xvldx(a: *const i8, b: i64) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvstx"] - fn __lasx_xvstx(a: __v32i8, b: *mut i8, c: i64); #[link_name = "llvm.loongarch.lasx.xvextl.qu.du"] fn __lasx_xvextl_qu_du(a: __v4u64) -> __v4u64; #[link_name = "llvm.loongarch.lasx.vext2xv.h.b"] @@ -709,14 +665,6 @@ unsafe extern "unadjusted" { fn __lasx_xvmaddwod_w_hu_h(a: __v8i32, b: __v16u16, c: __v16i16) -> __v8i32; #[link_name = "llvm.loongarch.lasx.xvmaddwod.h.bu.b"] fn __lasx_xvmaddwod_h_bu_b(a: __v16i16, b: __v32u8, c: __v32i8) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvrotr.b"] - fn __lasx_xvrotr_b(a: __v32i8, b: __v32i8) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvrotr.h"] - fn __lasx_xvrotr_h(a: __v16i16, b: __v16i16) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvrotr.w"] - fn __lasx_xvrotr_w(a: __v8i32, b: __v8i32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvrotr.d"] - fn __lasx_xvrotr_d(a: __v4i64, b: __v4i64) -> __v4i64; #[link_name = "llvm.loongarch.lasx.xvadd.q"] fn __lasx_xvadd_q(a: __v4i64, b: __v4i64) -> __v4i64; #[link_name = "llvm.loongarch.lasx.xvsub.q"] @@ -749,14 +697,6 @@ unsafe extern "unadjusted" { fn __lasx_xvexth_du_wu(a: __v8u32) -> __v4u64; #[link_name = "llvm.loongarch.lasx.xvexth.qu.du"] fn __lasx_xvexth_qu_du(a: __v4u64) -> __v4u64; - #[link_name = "llvm.loongarch.lasx.xvrotri.b"] - fn __lasx_xvrotri_b(a: __v32i8, b: u32) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvrotri.h"] - fn __lasx_xvrotri_h(a: __v16i16, b: u32) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvrotri.w"] - fn __lasx_xvrotri_w(a: __v8i32, b: u32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvrotri.d"] - fn __lasx_xvrotri_d(a: __v4i64, b: u32) -> __v4i64; #[link_name = "llvm.loongarch.lasx.xvextl.q.d"] fn __lasx_xvextl_q_d(a: __v4i64) -> __v4i64; #[link_name = "llvm.loongarch.lasx.xvsrlni.b.h"] @@ -1737,20 +1677,6 @@ pub fn lasx_xvfclass_d(a: m256d) -> m256i { unsafe { transmute(__lasx_xvfclass_d(transmute(a))) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrecip_s(a: m256) -> m256 { - unsafe { transmute(__lasx_xvfrecip_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrecip_d(a: m256d) -> m256d { - unsafe { transmute(__lasx_xvfrecip_d(transmute(a))) } -} - #[inline] #[target_feature(enable = "lasx,frecipe")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -1793,20 +1719,6 @@ pub fn lasx_xvfrint_d(a: m256d) -> m256d { unsafe { transmute(__lasx_xvfrint_d(transmute(a))) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrsqrt_s(a: m256) -> m256 { - unsafe { transmute(__lasx_xvfrsqrt_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrsqrt_d(a: m256d) -> m256d { - unsafe { transmute(__lasx_xvfrsqrt_d(transmute(a))) } -} - #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -1970,62 +1882,6 @@ pub fn lasx_xvpermi_w(a: m256i, b: m256i) -> m256i { unsafe { transmute(__lasx_xvpermi_w(transmute(a), transmute(b), IMM8)) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_b(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_h(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_w(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_d(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_d(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_bu(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_bu(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_hu(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_hu(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_wu(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_wu(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvmuh_du(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvmuh_du(transmute(a), transmute(b))) } -} - #[inline] #[target_feature(enable = "lasx")] #[rustc_legacy_const_generics(1)] @@ -2623,66 +2479,6 @@ pub fn lasx_xvfrintrne_d(a: m256d) -> m256d { unsafe { transmute(__lasx_xvfrintrne_d(transmute(a))) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrintrz_s(a: m256) -> m256 { - unsafe { transmute(__lasx_xvfrintrz_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrintrz_d(a: m256d) -> m256d { - unsafe { transmute(__lasx_xvfrintrz_d(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrintrp_s(a: m256) -> m256 { - unsafe { transmute(__lasx_xvfrintrp_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrintrp_d(a: m256d) -> m256d { - unsafe { transmute(__lasx_xvfrintrp_d(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrintrm_s(a: m256) -> m256 { - unsafe { transmute(__lasx_xvfrintrm_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvfrintrm_d(a: m256d) -> m256d { - unsafe { transmute(__lasx_xvfrintrm_d(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lasx_xvld(mem_addr: *const i8) -> m256i { - static_assert_simm_bits!(IMM_S12, 12); - transmute(__lasx_xvld(mem_addr, IMM_S12)) -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(2)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lasx_xvst(a: m256i, mem_addr: *mut i8) { - static_assert_simm_bits!(IMM_S12, 12); - __lasx_xvst(transmute(a), mem_addr, IMM_S12) -} - #[inline] #[target_feature(enable = "lasx")] #[rustc_legacy_const_generics(2, 3)] @@ -2810,20 +2606,6 @@ pub fn lasx_xvldi() -> m256i { unsafe { transmute(__lasx_xvldi(IMM_S13)) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lasx_xvldx(mem_addr: *const i8, b: i64) -> m256i { - transmute(__lasx_xvldx(mem_addr, transmute(b))) -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lasx_xvstx(a: m256i, mem_addr: *mut i8, b: i64) { - __lasx_xvstx(transmute(a), mem_addr, transmute(b)) -} - #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -3592,34 +3374,6 @@ pub fn lasx_xvmaddwod_h_bu_b(a: m256i, b: m256i, c: m256i) -> m256i { unsafe { transmute(__lasx_xvmaddwod_h_bu_b(transmute(a), transmute(b), transmute(c))) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotr_b(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvrotr_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotr_h(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvrotr_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotr_w(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvrotr_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotr_d(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvrotr_d(transmute(a), transmute(b))) } -} - #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -3732,42 +3486,6 @@ pub fn lasx_xvexth_qu_du(a: m256i) -> m256i { unsafe { transmute(__lasx_xvexth_qu_du(transmute(a))) } } -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotri_b(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM3, 3); - unsafe { transmute(__lasx_xvrotri_b(transmute(a), IMM3)) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotri_h(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM4, 4); - unsafe { transmute(__lasx_xvrotri_h(transmute(a), IMM4)) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotri_w(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM5, 5); - unsafe { transmute(__lasx_xvrotri_w(transmute(a), IMM5)) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrotri_d(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM6, 6); - unsafe { transmute(__lasx_xvrotri_d(transmute(a), IMM6)) } -} - #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs b/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs index 4dfe80795f68c..110a98cfb06b4 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs @@ -328,6 +328,16 @@ impl_vv!("lasx", lasx_xvneg_w, is::simd_neg, m256i, i32x8); impl_vv!("lasx", lasx_xvneg_d, is::simd_neg, m256i, i64x4); impl_vv!("lasx", lasx_xvfsqrt_s, is::simd_fsqrt, m256, f32x8); impl_vv!("lasx", lasx_xvfsqrt_d, is::simd_fsqrt, m256d, f64x4); +impl_vv!("lasx", lasx_xvfrsqrt_s, ls::simd_frsqrt_s, m256, f32x8); +impl_vv!("lasx", lasx_xvfrsqrt_d, ls::simd_frsqrt_d, m256d, f64x4); +impl_vv!("lasx", lasx_xvfrecip_s, ls::simd_frecip_s, m256, f32x8); +impl_vv!("lasx", lasx_xvfrecip_d, ls::simd_frecip_d, m256d, f64x4); +impl_vv!("lasx", lasx_xvfrintrp_s, is::simd_ceil, m256, f32x8); +impl_vv!("lasx", lasx_xvfrintrp_d, is::simd_ceil, m256d, f64x4); +impl_vv!("lasx", lasx_xvfrintrm_s, is::simd_floor, m256, f32x8); +impl_vv!("lasx", lasx_xvfrintrm_d, is::simd_floor, m256d, f64x4); +impl_vv!("lasx", lasx_xvfrintrz_s, is::simd_trunc, m256, f32x8); +impl_vv!("lasx", lasx_xvfrintrz_d, is::simd_trunc, m256d, f64x4); impl_vv!("lasx", lasx_xvreplve0_b, simd_replve0_b, m256i, i8x32); impl_vv!("lasx", lasx_xvreplve0_h, simd_replve0_h, m256i, i16x16); impl_vv!("lasx", lasx_xvreplve0_w, simd_replve0_w, m256i, i32x8); @@ -339,6 +349,10 @@ impl_gv!("lasx", lasx_xvreplgr2vr_h, ls::simd_splat, m256i, i16x16, i32); impl_gv!("lasx", lasx_xvreplgr2vr_w, ls::simd_splat, m256i, i32x8, i32); impl_gv!("lasx", lasx_xvreplgr2vr_d, ls::simd_splat, m256i, i64x4, i64); +impl_ggv!("lasx", lasx_xvldx, simd_ldx, m256i, i8x32, *const i8, i64, unsafe); + +impl_gsv!("lasx", lasx_xvld, simd_ld, m256i, i8x32, *const i8, 12, const, unsafe); + impl_sv!("lasx", lasx_xvrepli_b, ls::simd_splat, m256i, i8x32, 10); impl_sv!("lasx", lasx_xvrepli_h, ls::simd_splat, m256i, i16x16, 10); impl_sv!("lasx", lasx_xvrepli_w, ls::simd_splat, m256i, i32x8, 10); @@ -434,6 +448,10 @@ impl_vvv!("lasx", lasx_xvsrl_b, ls::simd_shr, m256i, u8x32); impl_vvv!("lasx", lasx_xvsrl_h, ls::simd_shr, m256i, u16x16); impl_vvv!("lasx", lasx_xvsrl_w, ls::simd_shr, m256i, u32x8); impl_vvv!("lasx", lasx_xvsrl_d, ls::simd_shr, m256i, u64x4); +impl_vvv!("lasx", lasx_xvrotr_b, ls::simd_rotr, m256i, u8x32); +impl_vvv!("lasx", lasx_xvrotr_h, ls::simd_rotr, m256i, u16x16); +impl_vvv!("lasx", lasx_xvrotr_w, ls::simd_rotr, m256i, u32x8); +impl_vvv!("lasx", lasx_xvrotr_d, ls::simd_rotr, m256i, u64x4); impl_vvv!("lasx", lasx_xvbitclr_b, ls::simd_bitclr, m256i, u8x32); impl_vvv!("lasx", lasx_xvbitclr_h, ls::simd_bitclr, m256i, u16x16); impl_vvv!("lasx", lasx_xvbitclr_w, ls::simd_bitclr, m256i, u32x8); @@ -474,6 +492,14 @@ impl_vvv!("lasx", lasx_xvabsd_bu, ls::simd_absd, m256i, u8x32); impl_vvv!("lasx", lasx_xvabsd_hu, ls::simd_absd, m256i, u16x16); impl_vvv!("lasx", lasx_xvabsd_wu, ls::simd_absd, m256i, u32x8); impl_vvv!("lasx", lasx_xvabsd_du, ls::simd_absd, m256i, u64x4); +impl_vvv!("lasx", lasx_xvmuh_b, simd_muh, m256i, i8x32, i16x32); +impl_vvv!("lasx", lasx_xvmuh_h, simd_muh, m256i, i16x16, i32x16); +impl_vvv!("lasx", lasx_xvmuh_w, simd_muh, m256i, i32x8, i64x8); +impl_vvv!("lasx", lasx_xvmuh_d, simd_muh, m256i, i64x4, i128x4); +impl_vvv!("lasx", lasx_xvmuh_bu, simd_muh, m256i, u8x32, u16x32); +impl_vvv!("lasx", lasx_xvmuh_hu, simd_muh, m256i, u16x16, u32x16); +impl_vvv!("lasx", lasx_xvmuh_wu, simd_muh, m256i, u32x8, u64x8); +impl_vvv!("lasx", lasx_xvmuh_du, simd_muh, m256i, u64x4, u128x4); impl_vvv!("lasx", lasx_xvpickev_b, simd_pickev_b, m256i, i8x32); impl_vvv!("lasx", lasx_xvpickev_h, simd_pickev_h, m256i, i16x16); impl_vvv!("lasx", lasx_xvpickev_w, simd_pickev_w, m256i, i32x8); @@ -499,6 +525,10 @@ impl_vvv!("lasx", lasx_xvpackod_h, simd_packod_h, m256i, i16x16); impl_vvv!("lasx", lasx_xvpackod_w, simd_packod_w, m256i, i32x8); impl_vvv!("lasx", lasx_xvpackod_d, simd_packod_d, m256i, i64x4); +impl_vgg!("lasx", lasx_xvstx, simd_stx, m256i, i8x32, *mut i8, i64, unsafe); + +impl_vgs!("lasx", lasx_xvst, simd_st, m256i, i8x32, *mut i8, 12, const, unsafe); + impl_vuv!("lasx", lasx_xvslli_b, is::simd_shl, m256i, i8x32); impl_vuv!("lasx", lasx_xvslli_h, is::simd_shl, m256i, i16x16); impl_vuv!("lasx", lasx_xvslli_w, is::simd_shl, m256i, i32x8); @@ -511,6 +541,10 @@ impl_vuv!("lasx", lasx_xvsrli_b, is::simd_shr, m256i, u8x32); impl_vuv!("lasx", lasx_xvsrli_h, is::simd_shr, m256i, u16x16); impl_vuv!("lasx", lasx_xvsrli_w, is::simd_shr, m256i, u32x8); impl_vuv!("lasx", lasx_xvsrli_d, is::simd_shr, m256i, u64x4); +impl_vuv!("lasx", lasx_xvrotri_b, ls::simd_rotr, m256i, u8x32); +impl_vuv!("lasx", lasx_xvrotri_h, ls::simd_rotr, m256i, u16x16); +impl_vuv!("lasx", lasx_xvrotri_w, ls::simd_rotr, m256i, u32x8); +impl_vuv!("lasx", lasx_xvrotri_d, ls::simd_rotr, m256i, u64x4); impl_vuv!("lasx", lasx_xvaddi_bu, is::simd_add, m256i, u8x32, 5); impl_vuv!("lasx", lasx_xvaddi_hu, is::simd_add, m256i, u16x16, 5); impl_vuv!("lasx", lasx_xvaddi_wu, is::simd_add, m256i, u32x8, 5); diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs b/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs index 555866040e420..6483c6296a344 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs @@ -205,10 +205,6 @@ unsafe extern "unadjusted" { fn __lsx_vfclass_s(a: __v4f32) -> __v4i32; #[link_name = "llvm.loongarch.lsx.vfclass.d"] fn __lsx_vfclass_d(a: __v2f64) -> __v2i64; - #[link_name = "llvm.loongarch.lsx.vfrecip.s"] - fn __lsx_vfrecip_s(a: __v4f32) -> __v4f32; - #[link_name = "llvm.loongarch.lsx.vfrecip.d"] - fn __lsx_vfrecip_d(a: __v2f64) -> __v2f64; #[link_name = "llvm.loongarch.lsx.vfrecipe.s"] fn __lsx_vfrecipe_s(a: __v4f32) -> __v4f32; #[link_name = "llvm.loongarch.lsx.vfrecipe.d"] @@ -221,10 +217,6 @@ unsafe extern "unadjusted" { fn __lsx_vfrint_s(a: __v4f32) -> __v4f32; #[link_name = "llvm.loongarch.lsx.vfrint.d"] fn __lsx_vfrint_d(a: __v2f64) -> __v2f64; - #[link_name = "llvm.loongarch.lsx.vfrsqrt.s"] - fn __lsx_vfrsqrt_s(a: __v4f32) -> __v4f32; - #[link_name = "llvm.loongarch.lsx.vfrsqrt.d"] - fn __lsx_vfrsqrt_d(a: __v2f64) -> __v2f64; #[link_name = "llvm.loongarch.lsx.vflogb.s"] fn __lsx_vflogb_s(a: __v4f32) -> __v4f32; #[link_name = "llvm.loongarch.lsx.vflogb.d"] @@ -261,22 +253,6 @@ unsafe extern "unadjusted" { fn __lsx_vffint_s_wu(a: __v4u32) -> __v4f32; #[link_name = "llvm.loongarch.lsx.vffint.d.lu"] fn __lsx_vffint_d_lu(a: __v2u64) -> __v2f64; - #[link_name = "llvm.loongarch.lsx.vmuh.b"] - fn __lsx_vmuh_b(a: __v16i8, b: __v16i8) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vmuh.h"] - fn __lsx_vmuh_h(a: __v8i16, b: __v8i16) -> __v8i16; - #[link_name = "llvm.loongarch.lsx.vmuh.w"] - fn __lsx_vmuh_w(a: __v4i32, b: __v4i32) -> __v4i32; - #[link_name = "llvm.loongarch.lsx.vmuh.d"] - fn __lsx_vmuh_d(a: __v2i64, b: __v2i64) -> __v2i64; - #[link_name = "llvm.loongarch.lsx.vmuh.bu"] - fn __lsx_vmuh_bu(a: __v16u8, b: __v16u8) -> __v16u8; - #[link_name = "llvm.loongarch.lsx.vmuh.hu"] - fn __lsx_vmuh_hu(a: __v8u16, b: __v8u16) -> __v8u16; - #[link_name = "llvm.loongarch.lsx.vmuh.wu"] - fn __lsx_vmuh_wu(a: __v4u32, b: __v4u32) -> __v4u32; - #[link_name = "llvm.loongarch.lsx.vmuh.du"] - fn __lsx_vmuh_du(a: __v2u64, b: __v2u64) -> __v2u64; #[link_name = "llvm.loongarch.lsx.vsllwil.h.b"] fn __lsx_vsllwil_h_b(a: __v16i8, b: u32) -> __v8i16; #[link_name = "llvm.loongarch.lsx.vsllwil.w.h"] @@ -437,18 +413,6 @@ unsafe extern "unadjusted" { fn __lsx_vfrintrne_s(a: __v4f32) -> __v4f32; #[link_name = "llvm.loongarch.lsx.vfrintrne.d"] fn __lsx_vfrintrne_d(a: __v2f64) -> __v2f64; - #[link_name = "llvm.loongarch.lsx.vfrintrz.s"] - fn __lsx_vfrintrz_s(a: __v4f32) -> __v4f32; - #[link_name = "llvm.loongarch.lsx.vfrintrz.d"] - fn __lsx_vfrintrz_d(a: __v2f64) -> __v2f64; - #[link_name = "llvm.loongarch.lsx.vfrintrp.s"] - fn __lsx_vfrintrp_s(a: __v4f32) -> __v4f32; - #[link_name = "llvm.loongarch.lsx.vfrintrp.d"] - fn __lsx_vfrintrp_d(a: __v2f64) -> __v2f64; - #[link_name = "llvm.loongarch.lsx.vfrintrm.s"] - fn __lsx_vfrintrm_s(a: __v4f32) -> __v4f32; - #[link_name = "llvm.loongarch.lsx.vfrintrm.d"] - fn __lsx_vfrintrm_d(a: __v2f64) -> __v2f64; #[link_name = "llvm.loongarch.lsx.vstelm.b"] fn __lsx_vstelm_b(a: __v16i8, b: *mut i8, c: i32, d: u32); #[link_name = "llvm.loongarch.lsx.vstelm.h"] @@ -641,14 +605,6 @@ unsafe extern "unadjusted" { fn __lsx_vmaddwev_q_du_d(a: __v2i64, b: __v2u64, c: __v2i64) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vmaddwod.q.du.d"] fn __lsx_vmaddwod_q_du_d(a: __v2i64, b: __v2u64, c: __v2i64) -> __v2i64; - #[link_name = "llvm.loongarch.lsx.vrotr.b"] - fn __lsx_vrotr_b(a: __v16i8, b: __v16i8) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vrotr.h"] - fn __lsx_vrotr_h(a: __v8i16, b: __v8i16) -> __v8i16; - #[link_name = "llvm.loongarch.lsx.vrotr.w"] - fn __lsx_vrotr_w(a: __v4i32, b: __v4i32) -> __v4i32; - #[link_name = "llvm.loongarch.lsx.vrotr.d"] - fn __lsx_vrotr_d(a: __v2i64, b: __v2i64) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vadd.q"] fn __lsx_vadd_q(a: __v2i64, b: __v2i64) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vsub.q"] @@ -681,14 +637,6 @@ unsafe extern "unadjusted" { fn __lsx_vexth_du_wu(a: __v4u32) -> __v2u64; #[link_name = "llvm.loongarch.lsx.vexth.qu.du"] fn __lsx_vexth_qu_du(a: __v2u64) -> __v2u64; - #[link_name = "llvm.loongarch.lsx.vrotri.b"] - fn __lsx_vrotri_b(a: __v16i8, b: u32) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vrotri.h"] - fn __lsx_vrotri_h(a: __v8i16, b: u32) -> __v8i16; - #[link_name = "llvm.loongarch.lsx.vrotri.w"] - fn __lsx_vrotri_w(a: __v4i32, b: u32) -> __v4i32; - #[link_name = "llvm.loongarch.lsx.vrotri.d"] - fn __lsx_vrotri_d(a: __v2i64, b: u32) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vextl.q.d"] fn __lsx_vextl_q_d(a: __v2i64) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vsrlni.b.h"] @@ -789,10 +737,6 @@ unsafe extern "unadjusted" { fn __lsx_vssrarni_du_q(a: __v2u64, b: __v2i64, c: u32) -> __v2u64; #[link_name = "llvm.loongarch.lsx.vpermi.w"] fn __lsx_vpermi_w(a: __v4i32, b: __v4i32, c: u32) -> __v4i32; - #[link_name = "llvm.loongarch.lsx.vld"] - fn __lsx_vld(a: *const i8, b: i32) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vst"] - fn __lsx_vst(a: __v16i8, b: *mut i8, c: i32); #[link_name = "llvm.loongarch.lsx.vssrlrn.b.h"] fn __lsx_vssrlrn_b_h(a: __v8i16, b: __v8i16) -> __v16i8; #[link_name = "llvm.loongarch.lsx.vssrlrn.h.w"] @@ -809,10 +753,6 @@ unsafe extern "unadjusted" { fn __lsx_vldi(a: i32) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vshuf.b"] fn __lsx_vshuf_b(a: __v16i8, b: __v16i8, c: __v16i8) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vldx"] - fn __lsx_vldx(a: *const i8, b: i64) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vstx"] - fn __lsx_vstx(a: __v16i8, b: *mut i8, c: i64); #[link_name = "llvm.loongarch.lsx.vextl.qu.du"] fn __lsx_vextl_qu_du(a: __v2u64) -> __v2u64; #[link_name = "llvm.loongarch.lsx.bnz.b"] @@ -1678,20 +1618,6 @@ pub fn lsx_vfclass_d(a: m128d) -> m128i { unsafe { transmute(__lsx_vfclass_d(transmute(a))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrecip_s(a: m128) -> m128 { - unsafe { transmute(__lsx_vfrecip_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrecip_d(a: m128d) -> m128d { - unsafe { transmute(__lsx_vfrecip_d(transmute(a))) } -} - #[inline] #[target_feature(enable = "lsx,frecipe")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -1734,20 +1660,6 @@ pub fn lsx_vfrint_d(a: m128d) -> m128d { unsafe { transmute(__lsx_vfrint_d(transmute(a))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrsqrt_s(a: m128) -> m128 { - unsafe { transmute(__lsx_vfrsqrt_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrsqrt_d(a: m128d) -> m128d { - unsafe { transmute(__lsx_vfrsqrt_d(transmute(a))) } -} - #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -1874,62 +1786,6 @@ pub fn lsx_vffint_d_lu(a: m128i) -> m128d { unsafe { transmute(__lsx_vffint_d_lu(transmute(a))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_b(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_h(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_w(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_d(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_d(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_bu(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_bu(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_hu(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_hu(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_wu(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_wu(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vmuh_du(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vmuh_du(transmute(a), transmute(b))) } -} - #[inline] #[target_feature(enable = "lsx")] #[rustc_legacy_const_generics(1)] @@ -2518,48 +2374,6 @@ pub fn lsx_vfrintrne_d(a: m128d) -> m128d { unsafe { transmute(__lsx_vfrintrne_d(transmute(a))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrintrz_s(a: m128) -> m128 { - unsafe { transmute(__lsx_vfrintrz_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrintrz_d(a: m128d) -> m128d { - unsafe { transmute(__lsx_vfrintrz_d(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrintrp_s(a: m128) -> m128 { - unsafe { transmute(__lsx_vfrintrp_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrintrp_d(a: m128d) -> m128d { - unsafe { transmute(__lsx_vfrintrp_d(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrintrm_s(a: m128) -> m128 { - unsafe { transmute(__lsx_vfrintrm_s(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vfrintrm_d(a: m128d) -> m128d { - unsafe { transmute(__lsx_vfrintrm_d(transmute(a))) } -} - #[inline] #[target_feature(enable = "lsx")] #[rustc_legacy_const_generics(2, 3)] @@ -3244,34 +3058,6 @@ pub fn lsx_vmaddwod_q_du_d(a: m128i, b: m128i, c: m128i) -> m128i { unsafe { transmute(__lsx_vmaddwod_q_du_d(transmute(a), transmute(b), transmute(c))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotr_b(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vrotr_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotr_h(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vrotr_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotr_w(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vrotr_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotr_d(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vrotr_d(transmute(a), transmute(b))) } -} - #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -3392,42 +3178,6 @@ pub fn lsx_vexth_qu_du(a: m128i) -> m128i { unsafe { transmute(__lsx_vexth_qu_du(transmute(a))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotri_b(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM3, 3); - unsafe { transmute(__lsx_vrotri_b(transmute(a), IMM3)) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotri_h(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM4, 4); - unsafe { transmute(__lsx_vrotri_h(transmute(a), IMM4)) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotri_w(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM5, 5); - unsafe { transmute(__lsx_vrotri_w(transmute(a), IMM5)) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vrotri_d(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM6, 6); - unsafe { transmute(__lsx_vrotri_d(transmute(a), IMM6)) } -} - #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -3876,24 +3626,6 @@ pub fn lsx_vpermi_w(a: m128i, b: m128i) -> m128i { unsafe { transmute(__lsx_vpermi_w(transmute(a), transmute(b), IMM8)) } } -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lsx_vld(mem_addr: *const i8) -> m128i { - static_assert_simm_bits!(IMM_S12, 12); - transmute(__lsx_vld(mem_addr, IMM_S12)) -} - -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(2)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lsx_vst(a: m128i, mem_addr: *mut i8) { - static_assert_simm_bits!(IMM_S12, 12); - __lsx_vst(transmute(a), mem_addr, IMM_S12) -} - #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -3952,20 +3684,6 @@ pub fn lsx_vshuf_b(a: m128i, b: m128i, c: m128i) -> m128i { unsafe { transmute(__lsx_vshuf_b(transmute(a), transmute(b), transmute(c))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lsx_vldx(mem_addr: *const i8, b: i64) -> m128i { - transmute(__lsx_vldx(mem_addr, transmute(b))) -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub unsafe fn lsx_vstx(a: m128i, mem_addr: *mut i8, b: i64) { - __lsx_vstx(transmute(a), mem_addr, transmute(b)) -} - #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs b/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs index 0b0df11bbfaac..2ceed73283726 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs @@ -227,12 +227,26 @@ impl_vv!("lsx", lsx_vneg_w, is::simd_neg, m128i, i32x4); impl_vv!("lsx", lsx_vneg_d, is::simd_neg, m128i, i64x2); impl_vv!("lsx", lsx_vfsqrt_s, is::simd_fsqrt, m128, f32x4); impl_vv!("lsx", lsx_vfsqrt_d, is::simd_fsqrt, m128d, f64x2); +impl_vv!("lsx", lsx_vfrsqrt_s, ls::simd_frsqrt_s, m128, f32x4); +impl_vv!("lsx", lsx_vfrsqrt_d, ls::simd_frsqrt_d, m128d, f64x2); +impl_vv!("lsx", lsx_vfrecip_s, ls::simd_frecip_s, m128, f32x4); +impl_vv!("lsx", lsx_vfrecip_d, ls::simd_frecip_d, m128d, f64x2); +impl_vv!("lsx", lsx_vfrintrp_s, is::simd_ceil, m128, f32x4); +impl_vv!("lsx", lsx_vfrintrp_d, is::simd_ceil, m128d, f64x2); +impl_vv!("lsx", lsx_vfrintrm_s, is::simd_floor, m128, f32x4); +impl_vv!("lsx", lsx_vfrintrm_d, is::simd_floor, m128d, f64x2); +impl_vv!("lsx", lsx_vfrintrz_s, is::simd_trunc, m128, f32x4); +impl_vv!("lsx", lsx_vfrintrz_d, is::simd_trunc, m128d, f64x2); impl_gv!("lsx", lsx_vreplgr2vr_b, ls::simd_splat, m128i, i8x16, i32); impl_gv!("lsx", lsx_vreplgr2vr_h, ls::simd_splat, m128i, i16x8, i32); impl_gv!("lsx", lsx_vreplgr2vr_w, ls::simd_splat, m128i, i32x4, i32); impl_gv!("lsx", lsx_vreplgr2vr_d, ls::simd_splat, m128i, i64x2, i64); +impl_ggv!("lsx", lsx_vldx, simd_ldx, m128i, i8x16, *const i8, i64, unsafe); + +impl_gsv!("lsx", lsx_vld, simd_ld, m128i, i8x16, *const i8, 12, const, unsafe); + impl_sv!("lsx", lsx_vrepli_b, ls::simd_splat, m128i, i8x16, 10); impl_sv!("lsx", lsx_vrepli_h, ls::simd_splat, m128i, i16x8, 10); impl_sv!("lsx", lsx_vrepli_w, ls::simd_splat, m128i, i32x4, 10); @@ -328,6 +342,10 @@ impl_vvv!("lsx", lsx_vsrl_b, ls::simd_shr, m128i, u8x16); impl_vvv!("lsx", lsx_vsrl_h, ls::simd_shr, m128i, u16x8); impl_vvv!("lsx", lsx_vsrl_w, ls::simd_shr, m128i, u32x4); impl_vvv!("lsx", lsx_vsrl_d, ls::simd_shr, m128i, u64x2); +impl_vvv!("lsx", lsx_vrotr_b, ls::simd_rotr, m128i, u8x16); +impl_vvv!("lsx", lsx_vrotr_h, ls::simd_rotr, m128i, u16x8); +impl_vvv!("lsx", lsx_vrotr_w, ls::simd_rotr, m128i, u32x4); +impl_vvv!("lsx", lsx_vrotr_d, ls::simd_rotr, m128i, u64x2); impl_vvv!("lsx", lsx_vbitclr_b, ls::simd_bitclr, m128i, u8x16); impl_vvv!("lsx", lsx_vbitclr_h, ls::simd_bitclr, m128i, u16x8); impl_vvv!("lsx", lsx_vbitclr_w, ls::simd_bitclr, m128i, u32x4); @@ -368,6 +386,14 @@ impl_vvv!("lsx", lsx_vabsd_bu, ls::simd_absd, m128i, u8x16); impl_vvv!("lsx", lsx_vabsd_hu, ls::simd_absd, m128i, u16x8); impl_vvv!("lsx", lsx_vabsd_wu, ls::simd_absd, m128i, u32x4); impl_vvv!("lsx", lsx_vabsd_du, ls::simd_absd, m128i, u64x2); +impl_vvv!("lsx", lsx_vmuh_b, simd_muh, m128i, i8x16, i16x16); +impl_vvv!("lsx", lsx_vmuh_h, simd_muh, m128i, i16x8, i32x8); +impl_vvv!("lsx", lsx_vmuh_w, simd_muh, m128i, i32x4, i64x4); +impl_vvv!("lsx", lsx_vmuh_d, simd_muh, m128i, i64x2, i128x2); +impl_vvv!("lsx", lsx_vmuh_bu, simd_muh, m128i, u8x16, u16x16); +impl_vvv!("lsx", lsx_vmuh_hu, simd_muh, m128i, u16x8, u32x8); +impl_vvv!("lsx", lsx_vmuh_wu, simd_muh, m128i, u32x4, u64x4); +impl_vvv!("lsx", lsx_vmuh_du, simd_muh, m128i, u64x2, u128x2); impl_vvv!("lsx", lsx_vpickev_b, simd_pickev_b, m128i, i8x16); impl_vvv!("lsx", lsx_vpickev_h, simd_pickev_h, m128i, i16x8); impl_vvv!("lsx", lsx_vpickev_w, simd_pickev_w, m128i, i32x4); @@ -393,6 +419,10 @@ impl_vvv!("lsx", lsx_vpackod_h, simd_packod_h, m128i, i16x8); impl_vvv!("lsx", lsx_vpackod_w, simd_packod_w, m128i, i32x4); impl_vvv!("lsx", lsx_vpackod_d, simd_packod_d, m128i, i64x2); +impl_vgg!("lsx", lsx_vstx, simd_stx, m128i, i8x16, *mut i8, i64, unsafe); + +impl_vgs!("lsx", lsx_vst, simd_st, m128i, i8x16, *mut i8, 12, const, unsafe); + impl_vuv!("lsx", lsx_vslli_b, is::simd_shl, m128i, i8x16); impl_vuv!("lsx", lsx_vslli_h, is::simd_shl, m128i, i16x8); impl_vuv!("lsx", lsx_vslli_w, is::simd_shl, m128i, i32x4); @@ -405,6 +435,10 @@ impl_vuv!("lsx", lsx_vsrli_b, is::simd_shr, m128i, u8x16); impl_vuv!("lsx", lsx_vsrli_h, is::simd_shr, m128i, u16x8); impl_vuv!("lsx", lsx_vsrli_w, is::simd_shr, m128i, u32x4); impl_vuv!("lsx", lsx_vsrli_d, is::simd_shr, m128i, u64x2); +impl_vuv!("lsx", lsx_vrotri_b, ls::simd_rotr, m128i, u8x16); +impl_vuv!("lsx", lsx_vrotri_h, ls::simd_rotr, m128i, u16x8); +impl_vuv!("lsx", lsx_vrotri_w, ls::simd_rotr, m128i, u32x4); +impl_vuv!("lsx", lsx_vrotri_d, ls::simd_rotr, m128i, u64x2); impl_vuv!("lsx", lsx_vaddi_bu, is::simd_add, m128i, u8x16, 5); impl_vuv!("lsx", lsx_vaddi_hu, is::simd_add, m128i, u16x8, 5); impl_vuv!("lsx", lsx_vaddi_wu, is::simd_add, m128i, u32x4, 5); diff --git a/library/stdarch/crates/core_arch/src/loongarch64/simd.rs b/library/stdarch/crates/core_arch/src/loongarch64/simd.rs index 32f191509be27..58dd016400432 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/simd.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/simd.rs @@ -35,16 +35,26 @@ impl_simd_ext!(u8x16, u8); impl_simd_ext!(u8x32, u8); impl_simd_ext!(i16x8, i16); impl_simd_ext!(i16x16, i16); +impl_simd_ext!(i16x32, i16); impl_simd_ext!(u16x8, u16); impl_simd_ext!(u16x16, u16); +impl_simd_ext!(u16x32, u16); impl_simd_ext!(i32x4, i32); impl_simd_ext!(i32x8, i32); +impl_simd_ext!(i32x16, i32); impl_simd_ext!(u32x4, u32); impl_simd_ext!(u32x8, u32); +impl_simd_ext!(u32x16, u32); impl_simd_ext!(i64x2, i64); impl_simd_ext!(i64x4, i64); +impl_simd_ext!(i64x8, i64); impl_simd_ext!(u64x2, u64); impl_simd_ext!(u64x4, u64); +impl_simd_ext!(u64x8, u64); +impl_simd_ext!(i128x2, i128); +impl_simd_ext!(u128x2, u128); +impl_simd_ext!(i128x4, i128); +impl_simd_ext!(u128x4, u128); #[inline(always)] #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] @@ -108,6 +118,42 @@ pub(super) const unsafe fn simd_fnmsub(a: T, b: T, c: T) -> T { is::simd_neg(ls::simd_fmsub(a, b, c)) } +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_frecip_s(a: T) -> T { + is::simd_div(is::simd_splat(1.0f32), a) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_frecip_d(a: T) -> T { + is::simd_div(is::simd_splat(1.0f64), a) +} + +#[inline(always)] +pub(super) unsafe fn simd_frsqrt_s(a: T) -> T { + ls::simd_frecip_s(is::simd_fsqrt(a)) +} + +#[inline(always)] +pub(super) unsafe fn simd_frsqrt_d(a: T) -> T { + ls::simd_frecip_d(is::simd_fsqrt(a)) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_ld(a: *const i8) -> T { + let a = a.offset(I as isize) as *const T; + core::ptr::read_unaligned(a) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_ldx(a: *const i8, b: i64) -> T { + let a = a.offset(b as isize) as *const T; + core::ptr::read_unaligned(a) +} + #[inline(always)] #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] pub(super) const unsafe fn simd_madd(a: T, b: T, c: T) -> T { @@ -120,6 +166,18 @@ pub(super) const unsafe fn simd_msub(a: T, b: T, c: T) -> T { is::simd_sub(a, is::simd_mul(b, c)) } +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_muh(a: T, b: T) -> T { + let a: W = is::simd_cast(a); + let b: W = is::simd_cast(b); + let p = is::simd_mul(a, b); + is::simd_cast(is::simd_shr( + p, + ls::simd_splat((size_of::() * 8 / 2) as i64), + )) +} + #[inline(always)] #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] pub(super) const unsafe fn simd_nor(a: T, b: T) -> T { @@ -138,6 +196,15 @@ pub(super) const unsafe fn simd_orn(a: T, b: T) -> T { is::simd_or(a, ls::simd_not(b)) } +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_rotr(a: T, b: T) -> T { + let m = (size_of::() * 8 - 1) as i64; + let r = is::simd_and(b, ls::simd_splat(m)); + let l = is::simd_and(is::simd_sub(ls::simd_splat(m + 1), r), ls::simd_splat(m)); + is::simd_or(is::simd_shr(a, r), is::simd_shl(a, l)) +} + #[inline(always)] #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] pub(super) const unsafe fn simd_shl(a: T, b: T) -> T { @@ -158,6 +225,20 @@ pub(super) const unsafe fn simd_splat(a: i64) -> T { T::splat(a) } +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_st(a: T, b: *mut i8) { + let b = b.offset(I as isize) as *mut T; + core::ptr::write_unaligned(b, a); +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_stx(a: T, b: *mut i8, c: i64) { + let b = b.offset(c as isize) as *mut T; + core::ptr::write_unaligned(b, a); +} + macro_rules! impl_vv { ($ft:literal, $name:ident, $op:path, $oty:ty, $ity:ty) => { #[inline] @@ -191,6 +272,36 @@ macro_rules! impl_gv { pub(super) use impl_gv; +macro_rules! impl_ggv { + ($ft:literal, $name:ident, $op:path, $oty:ty, $ity:ident, $gty:ty, $xty:ty, unsafe) => { + #[inline] + #[target_feature(enable = $ft)] + #[unstable(feature = "stdarch_loongarch", issue = "117427")] + pub unsafe fn $name(a: $gty, b: $xty) -> $oty { + let r: $ity = $op(a, b); + transmute(r) + } + }; +} + +pub(super) use impl_ggv; + +macro_rules! impl_gsv { + ($ft:literal, $name:ident, $op:ident, $oty:ty, $ity:ident, $gty:ty, $ibs:expr, const, unsafe) => { + #[inline] + #[target_feature(enable = $ft)] + #[rustc_legacy_const_generics(1)] + #[unstable(feature = "stdarch_loongarch", issue = "117427")] + pub unsafe fn $name(a: $gty) -> $oty { + static_assert_simm_bits!(IMM, $ibs); + let r: $ity = $op::(a); + transmute(r) + } + }; +} + +pub(super) use impl_gsv; + macro_rules! impl_sv { ($ft:literal, $name:ident, $op:path, $oty:ty, $ity:ident, $ibs:expr) => { #[inline] @@ -223,10 +334,51 @@ macro_rules! impl_vvv { } } }; + ($ft:literal, $name:ident, $op:ident, $oty:ty, $ity:ty, $wty:ty) => { + #[inline] + #[target_feature(enable = $ft)] + #[unstable(feature = "stdarch_loongarch", issue = "117427")] + pub fn $name(a: $oty, b: $oty) -> $oty { + unsafe { + let a: $ity = transmute(a); + let b: $ity = transmute(b); + let r: $ity = $op::<$ity, $wty>(a, b); + transmute(r) + } + } + }; } pub(super) use impl_vvv; +macro_rules! impl_vgg { + ($ft:literal, $name:ident, $op:path, $oty:ty, $ity:ident, $gty:ty, $xty:ty, unsafe) => { + #[inline] + #[target_feature(enable = $ft)] + #[unstable(feature = "stdarch_loongarch", issue = "117427")] + pub unsafe fn $name(a: $oty, b: $gty, c: $xty) { + $op(a, b, c); + } + }; +} + +pub(super) use impl_vgg; + +macro_rules! impl_vgs { + ($ft:literal, $name:ident, $op:ident, $oty:ty, $ity:ident, $gty:ty, $ibs:expr, const, unsafe) => { + #[inline] + #[target_feature(enable = $ft)] + #[rustc_legacy_const_generics(2)] + #[unstable(feature = "stdarch_loongarch", issue = "117427")] + pub unsafe fn $name(a: $oty, b: $gty) { + static_assert_simm_bits!(IMM, $ibs); + $op::(a, b); + } + }; +} + +pub(super) use impl_vgs; + macro_rules! impl_vuv { ($ft:literal, $name:ident, $op:path, $oty:ty, $ity:ident) => { #[inline] diff --git a/library/stdarch/crates/core_arch/src/simd.rs b/library/stdarch/crates/core_arch/src/simd.rs index 30c3125f5f8b8..08ee17548a01d 100644 --- a/library/stdarch/crates/core_arch/src/simd.rs +++ b/library/stdarch/crates/core_arch/src/simd.rs @@ -28,11 +28,13 @@ unsafe impl SimdElement for u8 {} unsafe impl SimdElement for u16 {} unsafe impl SimdElement for u32 {} unsafe impl SimdElement for u64 {} +unsafe impl SimdElement for u128 {} unsafe impl SimdElement for i8 {} unsafe impl SimdElement for i16 {} unsafe impl SimdElement for i32 {} unsafe impl SimdElement for i64 {} +unsafe impl SimdElement for i128 {} unsafe impl SimdElement for f16 {} unsafe impl SimdElement for f32 {} @@ -371,11 +373,13 @@ pub(crate) type u8x32 = Simd; pub(crate) type u16x16 = Simd; pub(crate) type u32x8 = Simd; pub(crate) type u64x4 = Simd; +pub(crate) type u128x2 = Simd; pub(crate) type i8x32 = Simd; pub(crate) type i16x16 = Simd; pub(crate) type i32x8 = Simd; pub(crate) type i64x4 = Simd; +pub(crate) type i128x2 = Simd; pub(crate) type f16x16 = Simd; pub(crate) type f32x8 = Simd; @@ -391,11 +395,13 @@ pub(crate) type u8x64 = Simd; pub(crate) type u16x32 = Simd; pub(crate) type u32x16 = Simd; pub(crate) type u64x8 = Simd; +pub(crate) type u128x4 = Simd; pub(crate) type i8x64 = Simd; pub(crate) type i16x32 = Simd; pub(crate) type i32x16 = Simd; pub(crate) type i64x8 = Simd; +pub(crate) type i128x4 = Simd; pub(crate) type f16x32 = Simd; pub(crate) type f32x16 = Simd; diff --git a/library/stdarch/crates/core_arch/src/x86/sse2.rs b/library/stdarch/crates/core_arch/src/x86/sse2.rs index bd7e4df2138d5..5e8fe959c301a 100644 --- a/library/stdarch/crates/core_arch/src/x86/sse2.rs +++ b/library/stdarch/crates/core_arch/src/x86/sse2.rs @@ -1445,7 +1445,7 @@ pub unsafe fn _mm_stream_si128(mem_addr: *mut __m128i, a: __m128i) { ); } -/// Stores a 32-bit integer value in the specified memory location. +/// Stores a 32-bit integer value in a 4-byte aligned memory location. /// To minimize caching, the data is flagged as non-temporal (unlikely to be /// used again soon). /// diff --git a/library/stdarch/crates/core_arch/src/x86_64/amx.rs b/library/stdarch/crates/core_arch/src/x86_64/amx.rs index cddae18b7684d..f97acf1d3f746 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/amx.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/amx.rs @@ -632,50 +632,6 @@ pub unsafe fn __tile_stream_loaddrs(dst: *mut __tile1024i, base: *const u8, stri (*dst).tile = tileloaddrst164_internal((*dst).rows, (*dst).colsb, base, stride as u64); } -/// Perform matrix multiplication of two tiles a and b, containing packed single precision (32-bit) -/// floating-point elements, which are converted to TF32 (tensor-float32) format, and accumulate the -/// results into a packed single precision tile. -/// For each possible combination of (row of a, column of b), it performs -/// - convert to TF32 -/// - multiply the corresponding elements of a and b -/// - accumulate the results into the corresponding row and column of dst using round-to-nearest-even -/// rounding mode. -/// Output FP32 denormals are always flushed to zero, input single precision denormals are always -/// handled and *not* treated as zero. -#[inline] -#[rustc_legacy_const_generics(0, 1, 2)] -#[target_feature(enable = "amx-tf32")] -#[cfg_attr( - all(test, not(target_vendor = "apple")), - assert_instr(tmmultf32ps, DST = 0, A = 1, B = 2) -)] -#[unstable(feature = "x86_amx_intrinsics", issue = "126622")] -pub unsafe fn _tile_mmultf32ps() { - static_assert_uimm_bits!(DST, 3); - static_assert_uimm_bits!(A, 3); - static_assert_uimm_bits!(B, 3); - tmmultf32ps(DST as i8, A as i8, B as i8); -} - -/// Perform matrix multiplication of two tiles a and b, containing packed single precision (32-bit) -/// floating-point elements, which are converted to TF32 (tensor-float32) format, and accumulate the -/// results into a packed single precision tile. -/// For each possible combination of (row of a, column of b), it performs -/// - convert to TF32 -/// - multiply the corresponding elements of a and b -/// - accumulate the results into the corresponding row and column of dst using round-to-nearest-even -/// rounding mode. -/// Output FP32 denormals are always flushed to zero, input single precision denormals are always -/// handled and *not* treated as zero. -/// The shape of the tile is specified in the struct of [`__tile1024i`]. The register of the tile is allocated by the compiler. -#[inline] -#[target_feature(enable = "amx-tf32")] -#[cfg_attr(all(test, not(target_vendor = "apple")), assert_instr(tmmultf32ps))] -#[unstable(feature = "x86_amx_intrinsics", issue = "126622")] -pub unsafe fn __tile_mmultf32ps(dst: *mut __tile1024i, a: __tile1024i, b: __tile1024i) { - (*dst).tile = tmmultf32ps_internal(a.rows, b.colsb, a.colsb, (*dst).tile, a.tile, b.tile); -} - /// Moves a row from a tile register to a zmm register, converting the packed 32-bit signed integer /// elements to packed single-precision (32-bit) floating-point elements. #[inline] @@ -1037,11 +993,6 @@ unsafe extern "unadjusted" { #[link_name = "llvm.x86.tileloaddrst164.internal"] fn tileloaddrst164_internal(rows: u16, colsb: u16, base: *const u8, stride: u64) -> Tile; - #[link_name = "llvm.x86.tmmultf32ps"] - fn tmmultf32ps(dst: i8, a: i8, b: i8); - #[link_name = "llvm.x86.tmmultf32ps.internal"] - fn tmmultf32ps_internal(m: u16, n: u16, k: u16, dst: Tile, a: Tile, b: Tile) -> Tile; - #[link_name = "llvm.x86.tcvtrowd2ps"] fn tcvtrowd2ps(tile: i8, row: u32) -> f32x16; #[link_name = "llvm.x86.tcvtrowd2psi"] @@ -2368,53 +2319,4 @@ mod tests { } } } - - #[simd_test(enable = "amx-tf32")] - fn test_tile_mmultf32ps() { - unsafe { - _init_amx(); - let a: [[f32; 16]; 16] = array::from_fn(|i| [i as _; _]); - let b: [[f32; 16]; 16] = [array::from_fn(|j| j as _); _]; - let mut res = [[0.0; 16]; 16]; - - let mut config = __tilecfg::default(); - config.palette = 1; - (0..=2).for_each(|i| { - config.colsb[i] = 64; - config.rows[i] = 16; - }); - _tile_loadconfig(config.as_ptr()); - _tile_zero::<0>(); - _tile_loadd::<1>(a.as_ptr().cast(), 64); - _tile_loadd::<2>(b.as_ptr().cast(), 64); - _tile_mmultf32ps::<0, 1, 2>(); - _tile_stored::<0>(res.as_mut_ptr().cast(), 64); - _tile_release(); - - let expected = array::from_fn(|i| array::from_fn(|j| 16.0 * i as f32 * j as f32)); - assert_eq!(res, expected); - } - } - - #[simd_test(enable = "amx-tf32")] - fn test__tile_mmultf32ps() { - unsafe { - _init_amx(); - let a: [[f32; 16]; 16] = array::from_fn(|i| [i as _; _]); - let b: [[f32; 16]; 16] = [array::from_fn(|j| j as _); _]; - let mut res = [[0.0; 16]; 16]; - - let mut tile_a = __tile1024i::zeroed(16, 64); - let mut tile_b = __tile1024i::zeroed(16, 64); - let mut tile_c = __tile1024i::zeroed(16, 64); - - __tile_loadd(&mut tile_a, a.as_ptr().cast(), 64); - __tile_loadd(&mut tile_b, b.as_ptr().cast(), 64); - __tile_mmultf32ps(&mut tile_c, tile_a, tile_b); - __tile_stored(res.as_mut_ptr().cast(), 64, tile_c); - - let expected = array::from_fn(|i| array::from_fn(|j| 16.0 * i as f32 * j as f32)); - assert_eq!(res, expected); - } - } } diff --git a/library/stdarch/crates/core_arch/src/x86_64/sse2.rs b/library/stdarch/crates/core_arch/src/x86_64/sse2.rs index c4768cedbfa6b..ba1d811fe5b20 100644 --- a/library/stdarch/crates/core_arch/src/x86_64/sse2.rs +++ b/library/stdarch/crates/core_arch/src/x86_64/sse2.rs @@ -59,7 +59,7 @@ pub fn _mm_cvttsd_si64x(a: __m128d) -> i64 { _mm_cvttsd_si64(a) } -/// Stores a 64-bit integer value in the specified memory location. +/// Stores a 64-bit integer value in an 8-byte aligned memory location. /// To minimize caching, the data is flagged as non-temporal (unlikely to be /// used again soon). /// diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index 26f861ca64b62..013c20f2db594 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -58,22 +58,14 @@ struct JsonIntrinsic { _instructions: Option>>, } -pub fn get_neon_intrinsics( - filename: &Path, -) -> Result>, Box> { +pub fn get_intrinsics(filename: &Path) -> Result>, Box> { let file = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(file); let json: Vec = serde_json::from_reader(reader).expect("Couldn't parse JSON"); let parsed = json .into_iter() - .filter_map(|intr| { - if intr.simd_isa == "Neon" { - Some(json_to_intrinsic(intr).expect("Couldn't parse JSON")) - } else { - None - } - }) + .map(|intr| json_to_intrinsic(intr).expect("Couldn't parse JSON")) .collect(); Ok(parsed) } @@ -121,8 +113,14 @@ fn json_to_intrinsic( } }); - let mut arg = - Argument::::new(i, String::from(arg_name), ArmType(arg_ty), constraint); + let is_predicate = arg_name == "pg"; + let mut arg = Argument::::new( + i, + String::from(arg_name), + ArmType(arg_ty), + constraint, + is_predicate, + ); // The JSON doesn't list immediates as const let IntrinsicType { diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index a60e0ff1551a3..4054d02502bfa 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -2,12 +2,14 @@ mod intrinsic; mod json_parser; mod types; -use crate::common::SupportedArchitecture; +use crate::common::argument::Argument; use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; -use crate::common::intrinsic_helpers::{SimdLen, TypeKind}; +use crate::common::intrinsic_helpers::{SimdLen, TypeDefinition, TypeKind}; +use crate::common::values::test_values_array_name; +use crate::common::{PASSES, PREDICATE_LOCAL, SupportedArchitecture}; use intrinsic::ArmType; -use json_parser::get_neon_intrinsics; +use json_parser::get_intrinsics; #[derive(PartialEq)] pub struct Arm(Vec>); @@ -29,12 +31,21 @@ impl SupportedArchitecture for Arm { #include #include #include +#ifdef __ARM_FEATURE_SVE +#include +#endif "#; const RUST_PRELUDE: &str = RUST_PRELUDE; fn c_compiler_flags(&self, cli_options: &ProcessedCli) -> Vec<&str> { // GCC uses an extra `-` in the arch name + let big_endian = cli_options.target.starts_with("aarch64_be"); + let a32 = cli_options.target.starts_with("armv7"); match cli_options.cc_arg_style { + CcArgStyle::Clang if !a32 && !big_endian => vec![ + "-march=armv8.6a+crypto+crc+dotprod+fp16+sve2-aes+sve2-sm4+sve2-sha3+sve2-bitperm+\ + f32mm+f64mm+sve2p1", + ], CcArgStyle::Clang => vec!["-march=armv8.6a+crypto+crc+dotprod+fp16"], // SVE tests aren't run under GCC so there are no target features added for SVE CcArgStyle::Gcc => vec!["-march=armv8.6-a+crypto+crc+dotprod+fp16+sha3+sm4"], @@ -45,7 +56,7 @@ impl SupportedArchitecture for Arm { let big_endian = cli_options.target.starts_with("aarch64_be"); let a32 = cli_options.target.starts_with("armv7"); let mut intrinsics = - get_neon_intrinsics(&cli_options.filename).expect("Error parsing input file"); + get_intrinsics(&cli_options.filename).expect("Error parsing input file"); intrinsics.sort_by(|a, b| a.name.cmp(&b.name)); intrinsics.dedup(); @@ -69,13 +80,13 @@ impl SupportedArchitecture for Arm { let has_sve_arg = i .arguments .iter() - .any(|a| a.ty.simd_len == Some(SimdLen::Scalable)); + .any(|a| a.ty.num_lanes() == SimdLen::Scalable); !(has_f16_arg && has_sve_arg) }) .filter(|i| { let has_f16_ret = i.results.kind() == TypeKind::Float && i.results.bit_len == Some(16); - let has_sve_ret = i.results.simd_len == Some(SimdLen::Scalable); + let has_sve_ret = i.results.num_lanes() == SimdLen::Scalable; !(has_f16_ret && has_sve_ret) }) // Skip `svqcvtn{u,}n*_x2` intrinsics - not yet implemented! @@ -129,6 +140,35 @@ impl SupportedArchitecture for Arm { Self(intrinsics) } + + fn predicate_function(size: u32) -> String { + format!("svptrue_b{size}()") + } + + fn load_call(arg: &Argument, idx: usize) -> String { + let name = arg.generate_name(); + let load = arg.ty.load_function(); + let ptr = format!( + "{vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _", + vals_name = test_values_array_name(&arg.ty) + ); + + match arg.ty.num_lanes() { + // If the load is of a `svbool_t`, then we load a `svint8_t` and + SimdLen::Scalable if matches!(arg.ty.kind(), TypeKind::Bool) => { + format!( + r#" +let {name} = {load}({PREDICATE_LOCAL}, {ptr}); +let {name} = svcmpne_n_s8({PREDICATE_LOCAL}, {name}, 0); + "# + ) + } + // If this load is of a scalable vector, then prepend an additional argument + // containing the predicate for the load. + SimdLen::Scalable => format!("let {name} = {load}({PREDICATE_LOCAL}, {ptr});"), + SimdLen::Fixed(..) => format!("let {name} = {load}({ptr});"), + } + } } const RUST_PRELUDE: &str = r#" @@ -140,6 +180,7 @@ const RUST_PRELUDE: &str = r#" #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_ftts))] #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_feat_lut))] #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(stdarch_neon_fp8))] +#![cfg_attr(all(any(target_arch = "aarch64", target_arch = "arm64ec"), target_endian = "little"), feature(stdarch_aarch64_sve))] #![cfg_attr(any(target_arch = "aarch64", target_arch = "arm64ec"), feature(faminmax))] #![feature(stdarch_neon_f16)] @@ -148,4 +189,116 @@ use core_arch::arch::aarch64::*; #[cfg(target_arch = "arm")] use core_arch::arch::arm::*; + +#[cfg(all(any(target_arch = "aarch64", target_arch = "arm64ec"), target_endian = "little"))] +const fn svpattern_from_i32(value: i32) -> svpattern { + match value { + 0 => svpattern::SV_POW2, + 1 => svpattern::SV_VL1, + 2 => svpattern::SV_VL2, + 3 => svpattern::SV_VL3, + 4 => svpattern::SV_VL4, + 5 => svpattern::SV_VL5, + 6 => svpattern::SV_VL6, + 7 => svpattern::SV_VL7, + 8 => svpattern::SV_VL8, + 9 => svpattern::SV_VL16, + 10 => svpattern::SV_VL32, + 11 => svpattern::SV_VL64, + 12 => svpattern::SV_VL128, + 13 => svpattern::SV_VL256, + 29 => svpattern::SV_MUL4, + 30 => svpattern::SV_MUL3, + 31 => svpattern::SV_ALL, + _ => unreachable!(), + } +} + +#[cfg(all(any(target_arch = "aarch64", target_arch = "arm64ec"), target_endian = "little"))] +const fn svprfop_from_i32(value: i32) -> svprfop { + match value { + 0 => svprfop::SV_PLDL1KEEP, + 1 => svprfop::SV_PLDL1STRM, + 2 => svprfop::SV_PLDL2KEEP, + 3 => svprfop::SV_PLDL2STRM, + 4 => svprfop::SV_PLDL3KEEP, + 5 => svprfop::SV_PLDL3STRM, + 8 => svprfop::SV_PSTL1KEEP, + 9 => svprfop::SV_PSTL1STRM, + 10 => svprfop::SV_PSTL2KEEP, + 11 => svprfop::SV_PSTL2STRM, + 12 => svprfop::SV_PSTL3KEEP, + 13 => svprfop::SV_PSTL3STRM, + _ => unreachable!(), + } +} + +macro_rules! debug_print_integral { + ($($name:ident => ($ty:ty, $svptrue_fn:ident, $svcnt_fn:ident, $svst_fn:ident)),*) => { + $( + #[inline] + #[target_feature(enable = "sve")] + #[cfg(all(any(target_arch = "aarch64", target_arch = "arm64ec"), target_endian = "little"))] + pub fn $name(v: $ty) -> String { + unsafe { + let __pred = $svptrue_fn(); + let __num_elems = $svcnt_fn() as usize; + let mut __buf = std::vec::Vec::with_capacity(__num_elems); + $svst_fn(__pred, __buf.as_mut_ptr(), v); + __buf.set_len(__num_elems); + format!( + "[{}]", + __buf.iter().map(|el| el.to_string()).collect::>().join(", ") + ) + } + } + )* + } +} + +debug_print_integral! { + debug_print_f32 => (svfloat32_t, svptrue_b32, svcntw, svst1_f32), + debug_print_f64 => (svfloat64_t, svptrue_b64, svcntd, svst1_f64), + debug_print_s8 => (svint8_t, svptrue_b8, svcntb, svst1_s8), + debug_print_s16 => (svint16_t, svptrue_b16, svcnth, svst1_s16), + debug_print_s32 => (svint32_t, svptrue_b32, svcntw, svst1_s32), + debug_print_s64 => (svint64_t, svptrue_b64, svcntd, svst1_s64), + debug_print_u8 => (svuint8_t, svptrue_b8, svcntb, svst1_u8), + debug_print_u16 => (svuint16_t, svptrue_b16, svcnth, svst1_u16), + debug_print_u32 => (svuint32_t, svptrue_b32, svcntw, svst1_u32), + debug_print_u64 => (svuint64_t, svptrue_b64, svcntd, svst1_u64) +} + +macro_rules! debug_print_bool { + ($($name:ident => ($ty:ty, $svst_fn:ident, $svdup_fn:ident)),*) => { + $( + #[inline] + #[target_feature(enable = "sve")] + #[cfg(all(any(target_arch = "aarch64", target_arch = "arm64ec"), target_endian = "little"))] + pub fn $name(v: $ty) -> String { + unsafe { + let __num_elems = svcntb() as usize; + let mut __buf = std::vec::Vec::with_capacity(__num_elems); + $svst_fn(v, __buf.as_mut_ptr(), $svdup_fn(1)); + __buf.set_len(__num_elems); + format!( + "[{}]", + __buf.iter() + .map(|el| *el == 1) + .map(|el| el.to_string()) + .collect::>() + .join(", ") + ) + } + } + )* + } +} + +debug_print_bool! { + debug_print_b8 => (svbool_t, svst1_u8, svdup_n_u8), + debug_print_b16 => (svbool_t, svst1_u16, svdup_n_u16), + debug_print_b32 => (svbool_t, svst1_u32, svdup_n_u32), + debug_print_b64 => (svbool_t, svst1_u64, svdup_n_u64) +} "#; diff --git a/library/stdarch/crates/intrinsic-test/src/arm/types.rs b/library/stdarch/crates/intrinsic-test/src/arm/types.rs index 7754e9ec2d0dd..7481bb4aeb30d 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/types.rs @@ -1,5 +1,9 @@ use super::intrinsic::ArmType; -use crate::common::intrinsic_helpers::{IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind}; +use crate::common::PREDICATE_LOCAL; +use crate::common::intrinsic_helpers::{ + IntrinsicType, Sign, SimdLen, TypeDefinition, TypeKind, default_fixed_vector_comparison, +}; +use itertools::Itertools; impl TypeDefinition for ArmType { /// Gets a string containing the typename for this type in C format. @@ -70,32 +74,137 @@ impl TypeDefinition for ArmType { } } + fn rust_scalar_type_for_test_value_array(&self) -> String { + if self.kind() == TypeKind::Bool && self.num_lanes() == SimdLen::Scalable { + let mut ty = self.clone(); + ty.kind = TypeKind::Int(Sign::Signed); + ty.rust_scalar_type() + } else { + self.rust_scalar_type() + } + } + /// Determines the load function for this type. fn load_function(&self) -> String { - if let IntrinsicType { - kind: k, - bit_len: Some(bl), - vec_len, - .. - } = **self - { - let quad = if self.num_lanes() * bl > 64 { "q" } else { "" }; + if let Some(bl) = self.bit_len { + match self.num_lanes() { + SimdLen::Scalable => { + format!( + "svld{len}_{type}{bl}", + len = self.num_vectors(), + type = self.rust_intrinsic_name_prefix(), + ) + } + SimdLen::Fixed(num_lanes) => { + format!( + "vld{len}{quad}_{type}{bl}", + quad = if num_lanes * bl > 64 { "q" } else { "" }, + len = self.num_vectors(), + type = self.rust_intrinsic_name_prefix(), + ) + } + } + } else { + todo!("load_function IntrinsicType: {self:#?}") + } + } + + fn comparison_function(&self) -> String { + if let SimdLen::Fixed(num_lanes) = self.num_lanes() { + return default_fixed_vector_comparison(self, num_lanes); + } + + if self.kind() == TypeKind::Bool { + // There isn't a `svcmpeq` for `svbool_t` and there aren't `svboolxN_t` types, so just + // do an XOR and test it is empty. + return format!( + r#" +let __eq = sveor_b_z({PREDICATE_LOCAL}, __rust_return_value, __c_return_value); +assert!(!svptest_any({PREDICATE_LOCAL}, __eq), "{{}}", id); + "# + ); + } + + // Returns `of` when `num_vectors == 1` otherwise returns the appropriate `svget` invocation + // for `of`. + let get = |num_vectors: u32, idx: u32, from: &'static str| -> String { + if num_vectors == 1 { + return from.to_string(); + } format!( - "vld{len}{quad}_{type}{size}", - type = match k { - TypeKind::Int(Sign::Unsigned) => "u", - TypeKind::Int(Sign::Signed) => "s", - TypeKind::Float => "f", - TypeKind::Poly => "p", - x => todo!("get_load_function TypeKind: {x:#?}"), - }, - size = bl, - quad = quad, - len = vec_len.unwrap_or(1), + "svget{num_vectors}_{ty}{bl}::<{idx}>({from})", + ty = self.rust_intrinsic_name_prefix(), + bl = self.inner_size(), ) - } else { - todo!("load_function IntrinsicType: {self:#?}") + }; + + let n = self.num_vectors(); + (0..n) + .format_with("\n", |i, fmt| { + match self.kind() { + TypeKind::Float | TypeKind::BFloat => { + // Floats need special handling because `NaN != NaN` normally - this + // effectively does `(rust == c) || (isnan(rust) && isnan(c))` + fmt(&format_args!( + r#" +let __rust_eq_return_value = {rust_return_value}; +let __c_eq_return_value = {c_return_value}; +let __eq_sans_nan = svcmpeq_{ty}{bl}({PREDICATE_LOCAL}, __rust_eq_return_value, __c_eq_return_value); +let __rust_nan = svcmpuo_{ty}{bl}({PREDICATE_LOCAL}, __rust_eq_return_value, __rust_eq_return_value); +let __c_nan = svcmpuo_{ty}{bl}({PREDICATE_LOCAL}, __c_eq_return_value, __c_eq_return_value); +let __both_nan = svand_b_z({PREDICATE_LOCAL}, __rust_nan, __c_nan); +let __eq = svorr_b_z({PREDICATE_LOCAL}, __eq_sans_nan, __both_nan); +if !svptest_any(__pred, __eq) {{ + let __rust_pretty = debug_print_{ty}{bl}(__rust_eq_return_value); + let __c_pretty = debug_print_{ty}{bl}(__c_eq_return_value); + panic!("{{}}-{i_plus_one}/{n}\nRust: {{__rust_pretty}}\nC: {{__c_pretty}}", id); +}} +"#, + ty = self.rust_intrinsic_name_prefix(), + bl = self.inner_size(), + rust_return_value = get(n, i, "__rust_return_value"), + c_return_value = get(n, i, "__c_return_value"), + i_plus_one = i + 1, // so that the output is "1/2" and "2/2" + )) + } + _ => { + // Most types can just use `svcmpeq` + fmt(&format_args!( + r#" +let __rust_eq_return_value = {rust_return_value}; +let __c_eq_return_value = {c_return_value}; +let __eq = svcmpeq_{ty}{bl}({PREDICATE_LOCAL}, __rust_eq_return_value, __c_eq_return_value); +if !svptest_any(__pred, __eq) {{ + let __rust_pretty = debug_print_{ty}{bl}(__rust_eq_return_value); + let __c_pretty = debug_print_{ty}{bl}(__c_eq_return_value); + panic!("{{}}-{i_plus_one}/{n}\nRust: {{__rust_pretty}}\nC: {{__c_pretty}}", id); +}} +"#, + ty = self.rust_intrinsic_name_prefix(), + bl = self.inner_size(), + rust_return_value = get(n, i, "__rust_return_value"), + c_return_value = get(n, i, "__c_return_value"), + i_plus_one = i + 1, // so that the output is "1/2" and "2/2" + )) + } + } + }) + .to_string() + } +} + +impl ArmType { + /// Returns the Rust prefix for the name of an intrinsic with this type kind (i.e. `s` for + /// `i16`, or `u` for `u16`). For type kinds without any bit length at the end (e.g. `bool`), + /// returns the whole type name. + pub fn rust_intrinsic_name_prefix(&self) -> &str { + match self.kind() { + TypeKind::Char(Sign::Signed) => "s", + TypeKind::Int(Sign::Signed) => "s", + TypeKind::Poly => "p", + TypeKind::Bool => "s", + _ => self.kind.rust_prefix(), } } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/argument.rs b/library/stdarch/crates/intrinsic-test/src/common/argument.rs index 4d38bce327930..10d9224183377 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/argument.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/argument.rs @@ -4,9 +4,9 @@ use crate::common::SupportedArchitecture; use crate::common::intrinsic_helpers::TypeKind; use crate::common::values::test_values_array_name; -use super::PASSES; use super::constraint::Constraint; use super::intrinsic_helpers::TypeDefinition; +use super::{PASSES, PREDICATE_LOCAL}; /// An argument for the intrinsic. #[derive(Debug, PartialEq, Clone)] @@ -19,18 +19,27 @@ pub struct Argument { pub ty: A::Type, /// Any constraints that are on this argument pub constraint: Option, + /// Is the argument a predicate for a scalable intrinsic? + pub is_predicate: bool, } impl Argument where A: SupportedArchitecture, { - pub fn new(pos: usize, name: String, ty: A::Type, constraint: Option) -> Self { + pub fn new( + pos: usize, + name: String, + ty: A::Type, + constraint: Option, + is_predicate: bool, + ) -> Self { Argument { pos, name, ty, constraint, + is_predicate, } } @@ -38,8 +47,14 @@ where self.ty.c_type() } + /// Generates local variable name for the value passed to this argument pub fn generate_name(&self) -> String { - format!("{}_val", self.name) + // The same predicate is used for scalable intrinsic invocations + if self.is_predicate { + format!("{PREDICATE_LOCAL}") + } else { + format!("{}_val", self.name) + } } pub fn is_simd(&self) -> bool { @@ -176,15 +191,11 @@ where pub fn load_values_rust(&self) -> String { self.iter() .filter(|&arg| !arg.has_constraint()) + .filter(|&arg| !arg.is_predicate) .enumerate() .map(|(idx, arg)| { if arg.is_simd() { - format!( - "let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);", - name = arg.generate_name(), - vals_name = test_values_array_name(&arg.ty), - load = arg.ty.load_function(), - ) + A::load_call(arg, idx) } else { format!( "let {name} = {vals_name}[(i+{idx}) % {PASSES}];", diff --git a/library/stdarch/crates/intrinsic-test/src/common/constraint.rs b/library/stdarch/crates/intrinsic-test/src/common/constraint.rs index ab52d866ab20a..c7d37da2adcbb 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/constraint.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/constraint.rs @@ -1,6 +1,7 @@ -use serde::Deserialize; use std::ops::Range; +use serde::Deserialize; + /// Describes the values to test for a const generic parameter #[derive(Debug, PartialEq, Clone, Deserialize)] pub enum Constraint { @@ -23,21 +24,53 @@ pub enum Constraint { SvImmRotationAdd, } -impl Constraint { - /// Returns an iterator over the values of this constraint - pub fn iter(&self) -> Box + '_> { +/// Workaround to enable the `Constraint::into_iter` to return an iterator that implements `Clone`, +/// so that it can be used with `Itertools::multi_cartesian_product`. +/// +/// With the different iterator types, returning `Box + '_>` would the +/// idiomatic approach, but this can't be made to implement `Clone`. Given the limited number +/// of iterator types used and their relative lack of complexity, wrapping them all in an enum isn't +/// too bad. +#[derive(Clone)] +pub enum ConstraintIterator<'a> { + Once(std::iter::Once), + Range(std::ops::Range), + Copied(std::iter::Copied>), + Chain(std::iter::Chain, std::ops::RangeInclusive>), + StepBy(std::iter::StepBy>), +} + +impl<'a> Iterator for ConstraintIterator<'a> { + type Item = i64; + + fn next(&mut self) -> Option { + match self { + ConstraintIterator::Once(once) => once.next(), + ConstraintIterator::Range(range) => range.next(), + ConstraintIterator::Copied(copied) => copied.next(), + ConstraintIterator::Chain(chain) => chain.next(), + ConstraintIterator::StepBy(step_by) => step_by.next(), + } + } +} + +impl<'a> IntoIterator for &'a Constraint { + type Item = i64; + type IntoIter = ConstraintIterator<'a>; + + fn into_iter(self) -> Self::IntoIter { match self { - Constraint::Equal(i) => Box::new(std::iter::once(*i)), - Constraint::Range(range) => Box::new(range.clone()), - Constraint::Set(items) => Box::new(items.iter().copied().chain(Range::default())), + Constraint::Equal(i) => ConstraintIterator::Once(std::iter::once(*i)), + Constraint::Range(range) => ConstraintIterator::Range(range.clone()), + Constraint::Set(items) => ConstraintIterator::Copied(items.iter().copied()), // These values are discriminants of the `svpattern` enum - Constraint::SvPattern => Box::new((0..=13).chain(29..=31)), + Constraint::SvPattern => ConstraintIterator::Chain((0..=13).chain(29..=31)), // These values are discriminants of the `svprfop` enum - Constraint::SvPrefetchOp => Box::new((0..=5).chain(8..=14)), + Constraint::SvPrefetchOp => ConstraintIterator::Chain((0..=5).chain(8..=14)), // Valid rotations for intrinsics operating on complex pairs: 0, 90, 180, 270 - Constraint::SvImmRotation => Box::new((0..=270).step_by(90)), + Constraint::SvImmRotation => ConstraintIterator::StepBy((0..=270).step_by(90)), // Valid rotations for `svcadd` and `svqcadd`: 0, 270 - Constraint::SvImmRotationAdd => Box::new((90..=270).step_by(180)), + Constraint::SvImmRotationAdd => ConstraintIterator::StepBy((90..=270).step_by(180)), } } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index 21cc5cfa2e597..bbff7a91b6c41 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -18,30 +18,36 @@ pub fn write_wrapper_c( w: &mut impl std::io::Write, intrinsics: &[Intrinsic], ) -> std::io::Result<()> { - write!(w, "{}", A::NOTICE)?; + write!( + w, + r#" +{notice} +#include +#include +{prelude} - writeln!(w, "#include ")?; - writeln!(w, "#include ")?; - writeln!(w, "{}", A::C_PRELUDE)?; - - for intrinsic in intrinsics { - intrinsic.iter_specializations(|imm_values| { - writeln!( - w, - " +{intrinsics} +"#, + notice = A::NOTICE, + prelude = A::C_PRELUDE, + intrinsics = intrinsics.iter().format_with("", |intrinsic, fmt| { + fmt(&intrinsic + .specializations() + .format_with("\n", |imm_values, fmt| { + fmt(&format_args!( + " void {name}_wrapper{imm_arglist}({return_ty}* __dst{arglist}) {{ *__dst = {name}({params}); }}", - return_ty = intrinsic.results.c_type(), - name = intrinsic.name, - imm_arglist = imm_values - .iter() - .format_with("", |i, fmt| fmt(&format_args!("_{i}"))), - arglist = intrinsic.arguments.as_non_imm_arglist_c(), - params = intrinsic.arguments.as_call_params_c(&imm_values) - ) - })?; - } - - Ok(()) + return_ty = intrinsic.results.c_type(), + name = intrinsic.name, + imm_arglist = imm_values + .iter() + .format_with("", |i, fmt| fmt(&format_args!("_{i}"))), + arglist = intrinsic.arguments.as_non_imm_arglist_c(), + params = intrinsic.arguments.as_call_params_c(&imm_values) + )) + })) + }), + ) } diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index baa11511ec219..44128d43b99a0 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -5,8 +5,9 @@ use itertools::Itertools; use super::intrinsic_helpers::TypeDefinition; use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; +use crate::common::intrinsic_helpers::TypeKind; use crate::common::values::{test_values_array_name, test_values_array_static}; -use crate::common::{PASSES, SupportedArchitecture}; +use crate::common::{PASSES, PREDICATE_LOCAL, SupportedArchitecture}; /// Rust definitions that are included verbatim in the generated source. In particular, defines /// a wrapper around float types that defines `NaN`s to be equal reflexively to enable @@ -126,7 +127,10 @@ pub fn write_lib_rs( for intrinsic in intrinsics { for arg in &intrinsic.arguments.args { - if !arg.has_constraint() { + // Skip arguments with constraints as these correspond to generic instantiatons, and + // predicates for scalable intrinsics as the same predicate is used for all intrinsics + // under test. + if !arg.has_constraint() && !arg.is_predicate { let name = test_values_array_name(&arg.ty); if seen.insert(name) { @@ -168,38 +172,14 @@ fn generate_rust_test_loop( coerce += ") -> _"; c_coerce += ")"; - if intrinsic - .arguments - .iter() - .filter(|arg| arg.has_constraint()) - .count() - == 0 - { - writeln!( - w, - " let specializations = [(\"\", {intrinsic_name}, {intrinsic_name}_wrapper)];" - )?; - } else { - writeln!(w, " let specializations = [")?; - - intrinsic.iter_specializations(|imm_values| { - writeln!( - w, - " (\"{const_args}\", {intrinsic_name}::<{const_args}> as unsafe {coerce}, {intrinsic_name}_wrapper_{c_const_args} as unsafe extern \"C\" {c_coerce}),", - const_args = imm_values.iter().join(","), - c_const_args = imm_values.iter().join("_"), - ) - })?; - - writeln!(w, " ];")?; - } - write!( w, r#" +let specializations = [{specializations}]; for (id, rust, c) in specializations {{ for i in 0..{PASSES} {{ unsafe {{ + {predicate} {loaded_args} let __rust_return_value = rust({rust_args}); @@ -212,9 +192,50 @@ for (id, rust, c) in specializations {{ }} }} "#, + specializations = intrinsic + .specializations() + .format_with(",", |imm_values, fmt| { + if imm_values.is_empty() { + fmt(&format_args!( + "(\"\", {intrinsic_name}, {intrinsic_name}_wrapper)" + )) + } else { + let constraint_args = intrinsic.arguments.iter().filter(|a| a.has_constraint()); + fmt(&format_args!( + r#" + ( + "{const_args}", + {intrinsic_name}::<{const_args}> as unsafe {coerce}, + {intrinsic_name}_wrapper_{c_const_args} as unsafe extern "C" {c_coerce} + ) + "#, + const_args = imm_values + .iter() + .zip(constraint_args) + .map(|(imm_val, arg)| { + match arg.ty.kind() { + TypeKind::SvPattern | TypeKind::SvPrefetchOp => { + format!("{{ {}_from_i32({imm_val}) }}", arg.ty.kind()) + } + _ => imm_val.to_string(), + } + }) + .join(","), + c_const_args = imm_values.iter().join("_"), + )) + } + }), loaded_args = intrinsic.arguments.load_values_rust(), rust_args = intrinsic.arguments.as_call_param_rust(), c_args = intrinsic.arguments.as_c_call_param_rust(), + predicate = if intrinsic.has_scalable_argument_or_result() { + format!( + "let {PREDICATE_LOCAL} = {pred};", + pred = A::predicate_function(intrinsic.results.inner_size()), + ) + } else { + "".to_string() + }, comparison = intrinsic.results.comparison_function(), ) } @@ -256,25 +277,25 @@ pub fn write_bindings_rust( #[allow(improper_ctypes)] #[link(name = "wrapper_{i}")] unsafe extern "C" {{ + {definitions} +}} "#, - )?; - - for intrinsic in intrinsics { - intrinsic.iter_specializations(|imm_values| { - writeln!( - w, - "fn {name}_wrapper{imm_arglist}(__dst: *mut {return_ty}{arglist});", - return_ty = intrinsic.results.rust_type(), - name = intrinsic.name, - imm_arglist = imm_values - .iter() - .format_with("", |i, fmt| fmt(&format_args!("_{i}"))), - arglist = intrinsic.arguments.as_non_imm_arglist_rust(), - ) - })?; - } - - writeln!(w, "}}") + definitions = intrinsics.iter().format_with("", |intrinsic, fmt| { + fmt(&intrinsic + .specializations() + .format_with("\n", |imm_values, fmt| { + fmt(&format_args!( + "fn {name}_wrapper{imm_arglist}(__dst: *mut {return_ty}{arglist});", + return_ty = intrinsic.results.rust_type(), + name = intrinsic.name, + imm_arglist = imm_values + .iter() + .format_with("", |i, fmt| fmt(&format_args!("_{i}"))), + arglist = intrinsic.arguments.as_non_imm_arglist_rust(), + )) + })) + }) + ) } /// Writes a `build.rs` into `w` for each test crate that compiles the corresponding C source code diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index 0c5bf43069d00..d5d903d941e4b 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -1,5 +1,6 @@ use super::argument::ArgumentList; -use crate::common::{SupportedArchitecture, constraint::Constraint}; +use crate::common::{SupportedArchitecture, intrinsic_helpers::SimdLen}; +use itertools::Itertools; /// An intrinsic #[derive(Debug, PartialEq, Clone)] @@ -20,27 +21,6 @@ pub struct Intrinsic { pub extension: String, } -/// Invokes `f` for each combination of the values in the constraint ranges. -/// -/// For example, given `constraints=[Equal(0), Range(1..2), Set([3, 4])]` and `imm_values=[]`, this -/// produces the four calls to `f`: `f([0, 1, 3])`, `f([0, 1, 4])`, `f([0, 2, 3])`, `f([0, 2, 4])`. -fn recurse_specializations<'a, E>( - constraints: &mut (impl Iterator + Clone), - imm_values: &mut Vec, - f: &mut impl FnMut(&[i64]) -> Result<(), E>, -) -> Result<(), E> { - if let Some(current) = constraints.next() { - for i in current.iter() { - imm_values.push(i); - recurse_specializations(&mut constraints.clone(), imm_values, f)?; - imm_values.pop(); - } - Ok(()) - } else { - f(&imm_values) - } -} - impl Intrinsic { /// Invokes `f` for "specialisation" of the intrinsic - a specific instantiation of the /// constant generics of the intrinsic. `f` takes a slice where the `i`th element corresponds @@ -49,17 +29,20 @@ impl Intrinsic { /// For an intrinsic with three arguments with constraints `Equal(0)`, `Range(1..2)`, /// `Set([3, 4])` respectively, this would produce four calls to `f`: `f(0, 1, 3)`, /// `f(0, 1, 4)`, `f(0, 2, 3)`, `f(0, 2, 4)`. - pub fn iter_specializations( - &self, - mut f: impl FnMut(&[i64]) -> Result<(), E>, - ) -> Result<(), E> { - recurse_specializations( - &mut self + pub fn specializations(&self) -> impl Iterator> { + self.arguments + .iter() + .filter_map(|arg| arg.constraint.as_ref()) + .map(|constraint| constraint.into_iter()) + .multi_cartesian_product() + } + + /// Returns `true` if this intrinsic has any argument or result types that are scalable vectors + pub fn has_scalable_argument_or_result(&self) -> bool { + self.results.num_lanes() == SimdLen::Scalable + || self .arguments .iter() - .filter_map(|arg| arg.constraint.as_ref()), - &mut Vec::new(), - &mut f, - ) + .any(|a| a.ty.num_lanes() == SimdLen::Scalable) } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index b9f30af7dfd51..bc47a90d3fe16 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -116,6 +116,15 @@ pub enum SimdLen { Fixed(u32), } +impl SimdLen { + pub fn expect_fixed(&self) -> u32 { + match self { + SimdLen::Fixed(lanes) => *lanes, + SimdLen::Scalable => panic!("`expect_fixed` with scalable length"), + } + } +} + impl std::fmt::Display for SimdLen { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { @@ -173,14 +182,8 @@ impl IntrinsicType { } /// Returns the number of lanes of the type - pub fn num_lanes(&self) -> u32 { - self.simd_len - .as_ref() - .map(|len| match len { - SimdLen::Scalable => unimplemented!(), - SimdLen::Fixed(len) => *len, - }) - .unwrap_or(1) + pub fn num_lanes(&self) -> SimdLen { + self.simd_len.unwrap_or(SimdLen::Fixed(1)) } /// Returns the number of vectors of the type @@ -208,7 +211,7 @@ pub trait TypeDefinition: Clone + DerefMut { match self.simd_len { Some(SimdLen::Scalable) => unimplemented!("architecture-specific"), Some(SimdLen::Fixed(_)) | None => { - default_fixed_vector_comparison(self, self.num_lanes()) + default_fixed_vector_comparison(self, self.num_lanes().expect_fixed()) } } } @@ -227,6 +230,12 @@ pub trait TypeDefinition: Clone + DerefMut { ty.vec_len = None; ty.rust_type() } + + /// Gets a string containing the name of the scalar type corresponding to this type that should + /// be used as the element type for the test value array. + fn rust_scalar_type_for_test_value_array(&self) -> String { + self.rust_scalar_type() + } } /// Returns the default comparison between results of an intrinsic - casting the vectors to arrays diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index 73daabbd6661a..46b6bc4102bed 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -5,12 +5,14 @@ use rayon::prelude::*; use cli::ProcessedCli; use crate::common::{ + argument::Argument, gen_c::write_wrapper_c, gen_rust::{ run_rustfmt, write_bin_cargo_toml, write_build_rs, write_lib_cargo_toml, write_lib_rs, }, intrinsic::Intrinsic, intrinsic_helpers::TypeDefinition, + values::test_values_array_name, }; pub mod argument; @@ -18,10 +20,16 @@ pub mod cli; pub mod constraint; pub mod intrinsic; pub mod intrinsic_helpers; +pub mod values; mod gen_c; mod gen_rust; -mod values; + +/// Many scalable intrinsics take a predicate argument and for the purposes of intrinsic testing, +/// a predicate that enables all lanes is used for all of these intrinsic calls (i.e. loading inputs, +/// result comparison, and the intrinsic under test). This constant defines the name of the local +/// variable that contains that predicate. +pub const PREDICATE_LOCAL: &'static str = "__pred"; // The number of times each intrinsic will be called - influences the generation of the // test arrays to minimise repeated testing of the same test values. @@ -100,6 +108,19 @@ pub trait SupportedArchitecture: Sized { .collect::>() .unwrap(); } + + /// Return a call to a intrinsic to generate a predicate, if reqd. + fn predicate_function(_: u32) -> String; + + /// Return a call loading `arg`. Can assume that `arg.is_simd()` holds. + fn load_call(arg: &Argument, idx: usize) -> String { + format!( + "let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);\n", + name = arg.generate_name(), + vals_name = test_values_array_name(&arg.ty), + load = arg.ty.load_function(), + ) + } } pub fn manual_chunk(intrinsic_count: usize) -> (usize, usize) { diff --git a/library/stdarch/crates/intrinsic-test/src/common/values.rs b/library/stdarch/crates/intrinsic-test/src/common/values.rs index 8c549346ce6ed..d1f333fd19363 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/values.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/values.rs @@ -26,7 +26,7 @@ pub fn test_values_array_static( w, "static {name}: [{ty}; {load_size}] = {values};", name = test_values_array_name(ty), - ty = ty.rust_scalar_type(), + ty = ty.rust_scalar_type_for_test_value_array(), load_size = test_values_array_length(&ty), values = test_values_array(&ty) ) @@ -50,8 +50,16 @@ pub fn test_values_array_name(ty: &T) -> String { /// which is then printed as a hex value in the generated code (and if identified as a negative /// value, with the appropriate minus and corrected hex pattern). Calls to `fN::from_bits` are /// generated for floats. +/// +/// An exception to the above is when `ty` is a boolean, where this function returns +/// `[true, false]` - as there are only ever two values for a boolean. This only works because the +/// generated accesses to the test value array is always modulo the length of the test value array. pub fn test_values_array(ty: &IntrinsicType) -> String { let (bit_len, kind) = match ty { + IntrinsicType { + kind: TypeKind::Bool, + .. + } => (1, TypeKind::Bool), IntrinsicType { kind: TypeKind::Float, bit_len: Some(bit_len), @@ -75,6 +83,9 @@ pub fn test_values_array(ty: &IntrinsicType) -> String { let src = bit_pattern_for_test_values_array(bit_len, i); assert!(src == 0 || src.ilog2() < bit_len); match kind { + TypeKind::Bool if ty.num_lanes() != SimdLen::Scalable => { + fmt(&format_args!("{}", if src == 1 { true } else { false })) + } TypeKind::Float => fmt(&format_args!("f{bit_len}::from_bits({src:#x})")), TypeKind::Vector | TypeKind::Int(Sign::Signed) if (src >> (bit_len - 1)) != 0 => { // `src` is a two's complement representation of a negative value. @@ -134,7 +145,8 @@ pub fn test_values_array_length(ty: &IntrinsicType) -> u32 { pub fn bit_pattern_for_test_values_array(bits: u32, index: u32) -> u64 { let index = index as usize; match bits { - bits @ (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8) => BIT_PATTERNS_8[index % (1 << bits)].into(), + 1 => BIT_PATTERNS_1[index % BIT_PATTERNS_1.len()].into(), + bits @ (2 | 3 | 4 | 5 | 6 | 7 | 8) => BIT_PATTERNS_8[index % (1 << bits)].into(), 16 => BIT_PATTERNS_16[index % BIT_PATTERNS_16.len()].into(), 32 => BIT_PATTERNS_32[index % BIT_PATTERNS_32.len()].into(), 64 => BIT_PATTERNS_64[index % BIT_PATTERNS_64.len()], @@ -142,6 +154,9 @@ pub fn bit_pattern_for_test_values_array(bits: u32, index: u32) -> u64 { } } +// Contains every possible 1-bit value in order +pub const BIT_PATTERNS_1: &[u8] = &[0x0, 0x1]; + // Contains every possible 8-bit value in order pub const BIT_PATTERNS_8: &[u8] = &[ 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, diff --git a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs index ce4955032916d..36f4fee43741b 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs @@ -99,6 +99,10 @@ impl SupportedArchitecture for X86 { Self { intrinsics } } + + fn predicate_function(_: u32) -> String { + unimplemented!("no scalable vectors on x86") + } } const RUST_PRELUDE: &str = r#" diff --git a/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs b/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs index 52f6da786e3d7..2296dffb64a5b 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/xml_parser.rs @@ -99,8 +99,13 @@ fn xml_to_intrinsic(intr: XMLIntrinsic) -> Result, Box::new(i, param.var_name.clone(), ty.unwrap(), constraint); - Some(arg) + Some(Argument::::new( + i, + param.var_name.clone(), + ty.unwrap(), + constraint, + false, + )) } }); diff --git a/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml b/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml index 138d5ba31175b..0b2d17d3b195f 100644 --- a/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml +++ b/library/stdarch/crates/stdarch-gen-arm/spec/sve/aarch64.spec.yml @@ -1157,7 +1157,7 @@ intrinsics: doc: Interleave even elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [trn1] compose: - LLVMLink: { name: "trn1.{sve_type}" } @@ -1167,10 +1167,13 @@ intrinsics: doc: Interleave even elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [trn1] compose: - - LLVMLink: { name: "trn1.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.trn1.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svtrn1q[_{type}] attr: [*sve-unstable] @@ -1188,7 +1191,7 @@ intrinsics: doc: Interleave odd elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] + types: [b8, f32, f64, i8, i16, i32, i64, u8, u16, u32, u64] assert_instr: [trn2] compose: - LLVMLink: { name: "trn2.{sve_type}" } @@ -1198,10 +1201,13 @@ intrinsics: doc: Interleave odd elements from two inputs arguments: ["op1: {sve_type}", "op2: {sve_type}"] return_type: "{sve_type}" - types: [b8, b16, b32, b64] + types: [b16, b32, b64] assert_instr: [trn2] compose: - - LLVMLink: { name: "trn2.{sve_type}" } + - LLVMLink: + name: "llvm.aarch64.sve.trn2.b{size}" + arguments: ["op1: svbool_t", "op2: svbool_t"] + return_type: "svbool_t" - name: svtrn2q[_{type}] attr: [*sve-unstable] diff --git a/library/stdarch/crates/stdarch-gen-hexagon-scalar/Cargo.toml b/library/stdarch/crates/stdarch-gen-hexagon-scalar/Cargo.toml deleted file mode 100644 index 04bee944f4a91..0000000000000 --- a/library/stdarch/crates/stdarch-gen-hexagon-scalar/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "stdarch-gen-hexagon-scalar" -version = "0.1.0" -authors = ["The Rust Project Developers"] -license = "MIT OR Apache-2.0" -edition = "2021" - -[dependencies] -regex = "1.10" diff --git a/library/stdarch/crates/stdarch-gen-hexagon-scalar/hexagon_protos.h b/library/stdarch/crates/stdarch-gen-hexagon/hexagon_protos.h similarity index 100% rename from library/stdarch/crates/stdarch-gen-hexagon-scalar/hexagon_protos.h rename to library/stdarch/crates/stdarch-gen-hexagon/hexagon_protos.h diff --git a/library/stdarch/crates/stdarch-gen-hexagon/src/hvx.rs b/library/stdarch/crates/stdarch-gen-hexagon/src/hvx.rs new file mode 100644 index 0000000000000..4d4b03c3ac204 --- /dev/null +++ b/library/stdarch/crates/stdarch-gen-hexagon/src/hvx.rs @@ -0,0 +1,1648 @@ +//! Hexagon HVX Code Generator +//! +//! This generator creates v64.rs and v128.rs from scratch using the LLVM HVX +//! header file as the sole source of truth. It parses the C intrinsic prototypes +//! and generates Rust wrapper functions with appropriate attributes. +//! +//! The two generated files provide: +//! - v64.rs: 64-byte vector mode intrinsics (512-bit vectors) +//! - v128.rs: 128-byte vector mode intrinsics (1024-bit vectors) +//! +//! Both modules are available unconditionally, but require the appropriate +//! target features to actually use the intrinsics. +//! +//! Usage: +//! cd crates/stdarch-gen-hexagon +//! cargo run +//! # Output is written to ../core_arch/src/hexagon/v64.rs and v128.rs + +use regex::Regex; +use std::collections::{HashMap, HashSet}; +use std::fs::File; +use std::io::Write; +use std::path::Path; +use stdarch_gen_common::GENERATED_MARKER; + +/// Mappings from HVX intrinsics to architecture-independent SIMD intrinsics. +/// These intrinsics have equivalent semantics and can be lowered to the generic form. +fn get_simd_intrinsic_mappings() -> HashMap<&'static str, &'static str> { + let mut map = HashMap::new(); + // Bitwise operations (element-size independent) + map.insert("vxor", "simd_xor"); + map.insert("vand", "simd_and"); + map.insert("vor", "simd_or"); + // Word (32-bit) arithmetic operations + map.insert("vaddw", "simd_add"); + map.insert("vsubw", "simd_sub"); + map +} + +/// The tracking issue number for the stdarch_hexagon feature +const TRACKING_ISSUE: &str = "151523"; + +/// HVX vector length mode +#[derive(Debug, Clone, Copy, PartialEq)] +enum VectorMode { + /// 64-byte vectors (512 bits) + V64, + /// 128-byte vectors (1024 bits) + V128, +} + +impl VectorMode { + fn bytes(&self) -> u32 { + match self { + VectorMode::V64 => 64, + VectorMode::V128 => 128, + } + } + + fn bits(&self) -> u32 { + self.bytes() * 8 + } + + fn lanes(&self) -> u32 { + self.bytes() / 4 // 32-bit lanes + } + + fn target_feature(&self) -> &'static str { + match self { + VectorMode::V64 => "hvx-length64b", + VectorMode::V128 => "hvx-length128b", + } + } +} + +/// LLVM version the header file is from (for reference) +/// Source: https://github.com/llvm/llvm-project/blob/llvmorg-22.1.0-rc1/clang/lib/Headers/hvx_hexagon_protos.h +const LLVM_VERSION: &str = "22.1.0-rc1"; + +/// Maximum HVX architecture version supported by rustc +/// Check with: rustc --target=hexagon-unknown-linux-musl --print target-features +const MAX_SUPPORTED_ARCH: u32 = 79; + +/// Local header file path (checked into the repository) +const HEADER_FILE: &str = "hvx_hexagon_protos.h"; + +/// Intrinsic information parsed from the LLVM header +#[derive(Debug, Clone)] +struct IntrinsicInfo { + /// The Q6_* intrinsic name (e.g., "Q6_V_vadd_VV") + q6_name: String, + /// The LLVM builtin name without prefix (e.g., "V6_vaddb") + builtin_name: String, + /// The short instruction name for assert_instr (e.g., "vaddb") + instr_name: String, + /// The assembly syntax from the comment + asm_syntax: String, + /// Instruction type + instr_type: String, + /// Execution slots + exec_slots: String, + /// Minimum HVX architecture version required + min_arch: u32, + /// Return type + return_type: RustType, + /// Parameters (name, type) + params: Vec<(String, RustType)>, + /// Whether this is a compound intrinsic (multiple builtins) + is_compound: bool, + /// For compound intrinsics: the parsed expression tree + compound_expr: Option, +} + +/// Expression tree for compound intrinsics +#[derive(Debug, Clone)] +enum CompoundExpr { + /// A call to a builtin: (builtin_name without V6_ prefix, arguments) + BuiltinCall(String, Vec), + /// A parameter reference by name + Param(String), + /// An integer literal (like -1) + IntLiteral(i32), +} + +/// Rust type mappings +#[derive(Debug, Clone, PartialEq)] +enum RustType { + HvxVector, + HvxVectorPair, + HvxVectorPred, + I32, + MutPtrHvxVector, + Unit, +} + +impl RustType { + fn from_c_type(c_type: &str) -> Option { + match c_type.trim() { + "HVX_Vector" => Some(RustType::HvxVector), + "HVX_VectorPair" => Some(RustType::HvxVectorPair), + "HVX_VectorPred" => Some(RustType::HvxVectorPred), + "Word32" => Some(RustType::I32), + "HVX_Vector*" => Some(RustType::MutPtrHvxVector), + "void" => Some(RustType::Unit), + _ => None, + } + } + + fn to_rust_str(&self) -> &'static str { + match self { + RustType::HvxVector => "HvxVector", + RustType::HvxVectorPair => "HvxVectorPair", + RustType::HvxVectorPred => "HvxVectorPred", + RustType::I32 => "i32", + RustType::MutPtrHvxVector => "*mut HvxVector", + RustType::Unit => "()", + } + } + + fn to_extern_str(&self) -> &'static str { + match self { + RustType::HvxVector => "HvxVector", + RustType::HvxVectorPair => "HvxVectorPair", + RustType::HvxVectorPred => "HvxVectorPred", + RustType::I32 => "i32", + RustType::MutPtrHvxVector => "*mut HvxVector", + RustType::Unit => "()", + } + } +} + +/// Parse a compound macro expression into an expression tree +fn parse_compound_expr(expr: &str) -> Option { + let expr = expr.trim(); + + // Try to match an integer literal (like -1) + if let Ok(n) = expr.parse::() { + return Some(CompoundExpr::IntLiteral(n)); + } + + // Try to match a simple parameter name (Vu, Vv, Rt, Qs, Qt, Qx, Vx, etc.) + // These are typically short identifiers in the macro + if expr.len() <= 3 + && expr.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') + && !expr.contains("__") + { + return Some(CompoundExpr::Param(expr.to_lowercase())); + } + + // Check if it's wrapped in extra parens first + if expr.starts_with('(') && expr.ends_with(')') { + // Check if these parens wrap the entire expression + let inner = &expr[1..expr.len() - 1]; + // Count depth: if after removing outer parens the expression is balanced, + // the outer parens were enclosing everything + if is_balanced_parens(inner) { + // But we also need to verify these aren't part of a function call + // If the inner expression is balanced and the whole thing starts with ( + // and ends with ), it's a paren wrapper + let result = parse_compound_expr(inner); + if result.is_some() { + return result; + } + } + } + + // Try to match __BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_xxx)(args) + // The args portion may contain nested calls, so we need to find the matching paren + if expr.starts_with("__BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_") { + // Find the end of the builtin name (after V6_) + let prefix = "__BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_"; + let after_prefix = &expr[prefix.len()..]; + if let Some(paren_pos) = after_prefix.find(')') { + let builtin_name = &after_prefix[..paren_pos]; + let rest = &after_prefix[paren_pos + 1..]; // Skip the closing ) of the WRAP + // rest should now be "(args)" + if rest.starts_with('(') && rest.ends_with(')') { + let args_str = &rest[1..rest.len() - 1]; + let args = parse_compound_args(args_str)?; + return Some(CompoundExpr::BuiltinCall(builtin_name.to_string(), args)); + } + } + } + + // Try to match __builtin_HEXAGON_V6_xxx(args) without wrap + if expr.starts_with("__builtin_HEXAGON_V6_") { + let prefix = "__builtin_HEXAGON_V6_"; + let after_prefix = &expr[prefix.len()..]; + if let Some(paren_pos) = after_prefix.find('(') { + let builtin_name = &after_prefix[..paren_pos]; + let rest = &after_prefix[paren_pos..]; + if rest.starts_with('(') && rest.ends_with(')') { + let args_str = &rest[1..rest.len() - 1]; + let args = parse_compound_args(args_str)?; + return Some(CompoundExpr::BuiltinCall(builtin_name.to_string(), args)); + } + } + } + + None +} + +/// Check if parentheses are balanced in a string +fn is_balanced_parens(s: &str) -> bool { + let mut depth = 0; + for c in s.chars() { + match c { + '(' => depth += 1, + ')' => { + depth -= 1; + if depth < 0 { + return false; + } + } + _ => {} + } + } + depth == 0 +} + +/// Parse comma-separated arguments, respecting nested parentheses +fn parse_compound_args(args_str: &str) -> Option> { + let mut args = Vec::new(); + let mut current = String::new(); + let mut depth = 0; + + for c in args_str.chars() { + match c { + '(' => { + depth += 1; + current.push(c); + } + ')' => { + depth -= 1; + current.push(c); + } + ',' if depth == 0 => { + let arg = current.trim().to_string(); + if !arg.is_empty() { + args.push(parse_compound_expr(&arg)?); + } + current.clear(); + } + _ => current.push(c), + } + } + + // Don't forget the last argument + let arg = current.trim().to_string(); + if !arg.is_empty() { + args.push(parse_compound_expr(&arg)?); + } + + Some(args) +} + +/// Extract all builtin names used in a compound expression +fn collect_builtins_from_expr(expr: &CompoundExpr, builtins: &mut HashSet) { + match expr { + CompoundExpr::BuiltinCall(name, args) => { + builtins.insert(name.clone()); + for arg in args { + collect_builtins_from_expr(arg, builtins); + } + } + CompoundExpr::Param(_) | CompoundExpr::IntLiteral(_) => {} + } +} + +/// Read the local HVX header file +fn read_header(crate_dir: &Path) -> Result { + let header_path = crate_dir.join(HEADER_FILE); + println!("Reading HVX header from: {}", header_path.display()); + println!(" (LLVM version: {})", LLVM_VERSION); + + std::fs::read_to_string(&header_path).map_err(|e| { + format!( + "Failed to read header file {}: {}", + header_path.display(), + e + ) + }) +} + +/// Parse a C function prototype to extract return type and parameters +fn parse_prototype(prototype: &str) -> Option<(RustType, Vec<(String, RustType)>)> { + // Pattern: ReturnType FunctionName(ParamType1 Param1, ParamType2 Param2, ...) + let proto_re = Regex::new(r"(\w+(?:\*)?)\s+Q6_\w+\(([^)]*)\)").unwrap(); + + if let Some(caps) = proto_re.captures(prototype) { + let return_type_str = caps[1].trim(); + let params_str = &caps[2]; + + let return_type = RustType::from_c_type(return_type_str)?; + + let mut params = Vec::new(); + if !params_str.trim().is_empty() { + // Pattern: Type Name or Type* Name + let param_re = Regex::new(r"(\w+\*?)\s+(\w+)").unwrap(); + for param in params_str.split(',') { + let param = param.trim(); + if let Some(pcaps) = param_re.captures(param) { + let ptype_str = pcaps[1].trim(); + let pname = pcaps[2].to_lowercase(); + if let Some(ptype) = RustType::from_c_type(ptype_str) { + params.push((pname, ptype)); + } else { + return None; // Unknown type + } + } + } + } + + Some((return_type, params)) + } else { + None + } +} + +/// Parse the LLVM header file to extract intrinsic information +fn parse_header(content: &str) -> Vec { + let mut intrinsics = Vec::new(); + + let arch_re = Regex::new(r"#if __HVX_ARCH__ >= (\d+)").unwrap(); + + // Regex to extract the simple builtin name from a macro body + // Match: __BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_xxx)(args) + let simple_builtin_re = + Regex::new(r"__BUILTIN_VECTOR_WRAP\(__builtin_HEXAGON_(\w+)\)\([^)]*\)\s*$").unwrap(); + + // Also handle builtins without VECTOR_WRAP + let simple_builtin_re2 = Regex::new(r"__builtin_HEXAGON_(\w+)\([^)]*\)\s*$").unwrap(); + + // Regex to extract Q6 name from #define + let q6_name_re = Regex::new(r"#define\s+(Q6_\w+)").unwrap(); + + // Regex to extract macro expression body + let macro_expr_re = Regex::new(r"#define\s+Q6_\w+\([^)]*\)\s+(.+)").unwrap(); + + let lines: Vec<&str> = content.lines().collect(); + let mut current_arch: u32 = 60; + let mut i = 0; + + while i < lines.len() { + // Track architecture version + if let Some(caps) = arch_re.captures(lines[i]) { + if let Ok(arch) = caps[1].parse() { + current_arch = arch; + } + } + + // Look for Assembly Syntax comment block + if lines[i].contains("Assembly Syntax:") { + let mut asm_syntax = String::new(); + let mut prototype = String::new(); + let mut instr_type = String::new(); + let mut exec_slots = String::new(); + + // Parse the comment block + let mut j = i; + while j < lines.len() && !lines[j].starts_with("#define") { + let line = lines[j]; + if line.contains("Assembly Syntax:") { + if let Some(pos) = line.find("Assembly Syntax:") { + asm_syntax = line[pos + 16..].trim().to_string(); + } + } else if line.contains("C Intrinsic Prototype:") { + if let Some(pos) = line.find("C Intrinsic Prototype:") { + prototype = line[pos + 22..].trim().to_string(); + } + } else if line.contains("Instruction Type:") { + if let Some(pos) = line.find("Instruction Type:") { + instr_type = line[pos + 17..].trim().to_string(); + } + } else if line.contains("Execution Slots:") { + if let Some(pos) = line.find("Execution Slots:") { + exec_slots = line[pos + 16..].trim().to_string(); + } + } + j += 1; + } + + // Now find the #define line + while j < lines.len() && !lines[j].starts_with("#define") { + j += 1; + } + + if j < lines.len() { + let define_line = lines[j]; + + // Extract Q6 name and check if it's simple or compound + if let Some(caps) = q6_name_re.captures(define_line) { + let q6_name = caps[1].to_string(); + + // Get the full macro body (handle line continuations) + let mut macro_body = define_line.to_string(); + let mut k = j; + while macro_body.trim_end().ends_with('\\') && k + 1 < lines.len() { + k += 1; + macro_body.push_str(lines[k]); + } + + // Try to extract simple builtin name + let builtin_name = simple_builtin_re + .captures(¯o_body) + .or_else(|| simple_builtin_re2.captures(¯o_body)) + .map(|bcaps| bcaps[1].to_string()); + + // Check if it's a compound intrinsic (multiple __builtin calls) + let builtin_count = macro_body.matches("__builtin_HEXAGON_").count(); + let is_compound = builtin_count > 1; + + // Parse prototype + if let Some((return_type, params)) = parse_prototype(&prototype) { + if is_compound { + // For compound intrinsics, parse the expression + // Extract the macro body after the parameter list + if let Some(expr_caps) = macro_expr_re.captures(¯o_body) { + let expr_str = expr_caps[1].trim().replace(['\n', '\\'], " "); + let expr_str = expr_str.trim(); + + if let Some(compound_expr) = parse_compound_expr(expr_str) { + // For compound intrinsics, we use the outermost builtin + // as the "primary" for the instruction name + let (primary_builtin, instr_name) = match &compound_expr { + CompoundExpr::BuiltinCall(name, _) => { + (name.clone(), name.clone()) + } + _ => continue, + }; + + intrinsics.push(IntrinsicInfo { + q6_name, + builtin_name: format!("V6_{}", primary_builtin), + instr_name, + asm_syntax, + instr_type, + exec_slots, + min_arch: current_arch, + return_type, + params, + is_compound: true, + compound_expr: Some(compound_expr), + }); + } + } + } else if let Some(builtin) = builtin_name { + // Extract short instruction name + let instr_name = builtin + .strip_prefix("V6_") + .map(|s| s.to_string()) + .unwrap_or_else(|| builtin.clone()); + + intrinsics.push(IntrinsicInfo { + q6_name, + builtin_name: builtin, + instr_name, + asm_syntax, + instr_type, + exec_slots, + min_arch: current_arch, + return_type, + params, + is_compound: false, + compound_expr: None, + }); + } + } + } + } + i = j; + } + i += 1; + } + + intrinsics +} + +/// Generate the module documentation +fn generate_module_doc(mode: VectorMode) -> String { + format!( + r#"//! Hexagon HVX {bytes}-byte vector mode intrinsics +//! +//! This module provides intrinsics for the Hexagon Vector Extensions (HVX) +//! in {bytes}-byte vector mode ({bits}-bit vectors). +//! +//! HVX is a wide vector extension designed for high-performance signal processing. +//! [Hexagon HVX Programmer's Reference Manual](https://docs.qualcomm.com/doc/80-N2040-61) +//! +//! ## Vector Types +//! +//! In {bytes}-byte mode: +//! - `HvxVector` is {bits} bits ({bytes} bytes) containing {lanes} x 32-bit values +//! - `HvxVectorPair` is {pair_bits} bits ({pair_bytes} bytes) +//! - `HvxVectorPred` is {bits} bits ({bytes} bytes) for predicate operations +//! +//! To use this module, compile with `-C target-feature=+{target_feature}`. +//! +//! ## Naming Convention +//! +//! Function names preserve the original Q6 naming case because the convention +//! uses case to distinguish register types: +//! - `W` (uppercase) = vector pair (`HvxVectorPair`) +//! - `V` (uppercase) = vector (`HvxVector`) +//! - `Q` (uppercase) = predicate (`HvxVectorPred`) +//! - `R` = scalar register (`i32`) +//! +//! For example, `Q6_W_vcombine_VV` operates on a vector pair while +//! `Q6_V_hi_W` extracts a vector from a pair. +//! +//! ## Architecture Versions +//! +//! Different intrinsics require different HVX architecture versions. Use the +//! appropriate target feature to enable the required version: +//! - HVX v60: `-C target-feature=+hvxv60` (basic HVX operations) +//! - HVX v62: `-C target-feature=+hvxv62` +//! - HVX v65: `-C target-feature=+hvxv65` (includes floating-point support) +//! - HVX v66: `-C target-feature=+hvxv66` +//! - HVX v68: `-C target-feature=+hvxv68` +//! - HVX v69: `-C target-feature=+hvxv69` +//! - HVX v73: `-C target-feature=+hvxv73` +//! - HVX v79: `-C target-feature=+hvxv79` +//! +//! Each version includes all features from previous versions. +"#, + bytes = mode.bytes(), + bits = mode.bits(), + lanes = mode.lanes(), + pair_bytes = mode.bytes() * 2, + pair_bits = mode.bits() * 2, + target_feature = mode.target_feature(), + ) +} + +/// Generate the type definitions for a specific vector mode +fn generate_types(mode: VectorMode) -> String { + let lanes = mode.lanes(); + let pair_lanes = lanes * 2; + let bits = mode.bits(); + let bytes = mode.bytes(); + let pair_bits = bits * 2; + let pair_bytes = bytes * 2; + + format!( + r#" +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +#[cfg(test)] +use stdarch_test::assert_instr; + +use crate::intrinsics::simd::{{simd_add, simd_and, simd_or, simd_sub, simd_xor}}; + +// HVX type definitions for {bytes}-byte vector mode +types! {{ + #![unstable(feature = "stdarch_hexagon", issue = "{TRACKING_ISSUE}")] + + /// HVX vector type ({bits} bits / {bytes} bytes) + /// + /// This type represents a single HVX vector register containing {lanes} x 32-bit values. + pub struct HvxVector({lanes} x i32); + + /// HVX vector pair type ({pair_bits} bits / {pair_bytes} bytes) + /// + /// This type represents a pair of HVX vector registers, often used for + /// operations that produce double-width results. + pub struct HvxVectorPair({pair_lanes} x i32); + + /// HVX vector predicate type ({bits} bits / {bytes} bytes) + /// + /// This type represents a predicate vector used for conditional operations. + /// Each bit corresponds to a lane in the vector. + pub struct HvxVectorPred({lanes} x i32); +}} +"#, + bytes = bytes, + bits = bits, + lanes = lanes, + pair_bits = pair_bits, + pair_bytes = pair_bytes, + pair_lanes = pair_lanes, + TRACKING_ISSUE = TRACKING_ISSUE, + ) +} + +/// Builtin signature information for extern declarations +struct BuiltinSignature { + /// The V6_ prefixed name + full_name: String, + /// The short name (without V6_) + short_name: String, + /// Return type + return_type: RustType, + /// Parameter types + param_types: Vec, +} + +/// Get known signatures for builtins used in compound operations +/// These are the helper builtins that don't have their own Q6_ wrapper +fn get_compound_helper_signatures() -> HashMap { + let mut map = HashMap::new(); + + // vandvrt: HVX_Vector -> i32 -> HVX_Vector + // Converts predicate to vector representation. LLVM uses HVX_Vector for both. + map.insert( + "vandvrt".to_string(), + BuiltinSignature { + full_name: "V6_vandvrt".to_string(), + short_name: "vandvrt".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::I32], + }, + ); + + // vandqrt: HVX_Vector -> i32 -> HVX_Vector + // Converts vector representation back to predicate. LLVM uses HVX_Vector for both. + map.insert( + "vandqrt".to_string(), + BuiltinSignature { + full_name: "V6_vandqrt".to_string(), + short_name: "vandqrt".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::I32], + }, + ); + + // vandvrt_acc: HVX_Vector -> HVX_Vector -> i32 -> HVX_Vector + map.insert( + "vandvrt_acc".to_string(), + BuiltinSignature { + full_name: "V6_vandvrt_acc".to_string(), + short_name: "vandvrt_acc".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32], + }, + ); + + // vandqrt_acc: HVX_Vector -> HVX_Vector -> i32 -> HVX_Vector + map.insert( + "vandqrt_acc".to_string(), + BuiltinSignature { + full_name: "V6_vandqrt_acc".to_string(), + short_name: "vandqrt_acc".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32], + }, + ); + + // pred_and: HVX_Vector -> HVX_Vector -> HVX_Vector + map.insert( + "pred_and".to_string(), + BuiltinSignature { + full_name: "V6_pred_and".to_string(), + short_name: "pred_and".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + // pred_and_n: HVX_Vector -> HVX_Vector -> HVX_Vector + map.insert( + "pred_and_n".to_string(), + BuiltinSignature { + full_name: "V6_pred_and_n".to_string(), + short_name: "pred_and_n".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + // pred_or: HVX_Vector -> HVX_Vector -> HVX_Vector + map.insert( + "pred_or".to_string(), + BuiltinSignature { + full_name: "V6_pred_or".to_string(), + short_name: "pred_or".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + // pred_or_n: HVX_Vector -> HVX_Vector -> HVX_Vector + map.insert( + "pred_or_n".to_string(), + BuiltinSignature { + full_name: "V6_pred_or_n".to_string(), + short_name: "pred_or_n".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + // pred_xor: HVX_Vector -> HVX_Vector -> HVX_Vector + map.insert( + "pred_xor".to_string(), + BuiltinSignature { + full_name: "V6_pred_xor".to_string(), + short_name: "pred_xor".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + // pred_not: HVX_Vector -> HVX_Vector + map.insert( + "pred_not".to_string(), + BuiltinSignature { + full_name: "V6_pred_not".to_string(), + short_name: "pred_not".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector], + }, + ); + + // pred_scalar2: i32 -> HVX_Vector + map.insert( + "pred_scalar2".to_string(), + BuiltinSignature { + full_name: "V6_pred_scalar2".to_string(), + short_name: "pred_scalar2".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::I32], + }, + ); + + // Conditional store operations + map.insert( + "vS32b_qpred_ai".to_string(), + BuiltinSignature { + full_name: "V6_vS32b_qpred_ai".to_string(), + short_name: "vS32b_qpred_ai".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::HvxVector, + RustType::MutPtrHvxVector, + RustType::HvxVector, + ], + }, + ); + + map.insert( + "vS32b_nqpred_ai".to_string(), + BuiltinSignature { + full_name: "V6_vS32b_nqpred_ai".to_string(), + short_name: "vS32b_nqpred_ai".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::HvxVector, + RustType::MutPtrHvxVector, + RustType::HvxVector, + ], + }, + ); + + map.insert( + "vS32b_nt_qpred_ai".to_string(), + BuiltinSignature { + full_name: "V6_vS32b_nt_qpred_ai".to_string(), + short_name: "vS32b_nt_qpred_ai".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::HvxVector, + RustType::MutPtrHvxVector, + RustType::HvxVector, + ], + }, + ); + + map.insert( + "vS32b_nt_nqpred_ai".to_string(), + BuiltinSignature { + full_name: "V6_vS32b_nt_nqpred_ai".to_string(), + short_name: "vS32b_nt_nqpred_ai".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::HvxVector, + RustType::MutPtrHvxVector, + RustType::HvxVector, + ], + }, + ); + + // Conditional accumulation operations + for (suffix, _elem) in [("b", "byte"), ("h", "halfword"), ("w", "word")] { + // vaddbq, vaddhq, vaddwq + map.insert( + format!("vadd{}q", suffix), + BuiltinSignature { + full_name: format!("V6_vadd{}q", suffix), + short_name: format!("vadd{}q", suffix), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + // vaddbnq, vaddhnq, vaddwnq + map.insert( + format!("vadd{}nq", suffix), + BuiltinSignature { + full_name: format!("V6_vadd{}nq", suffix), + short_name: format!("vadd{}nq", suffix), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + } + + // Comparison operations with accumulation + // veqb_and, veqb_or, veqb_xor, etc. + for elem in ["b", "h", "w", "ub", "uh", "uw"] { + for op in ["and", "or", "xor"] { + // veq*_and, veq*_or, veq*_xor + map.insert( + format!("veq{}_{}", elem, op), + BuiltinSignature { + full_name: format!("V6_veq{}_{}", elem, op), + short_name: format!("veq{}_{}", elem, op), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + // vgt*_and, vgt*_or, vgt*_xor + map.insert( + format!("vgt{}_{}", elem, op), + BuiltinSignature { + full_name: format!("V6_vgt{}_{}", elem, op), + short_name: format!("vgt{}_{}", elem, op), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + } + } + + // Floating-point comparison operations (hf = half-float, sf = single-float) + for elem in ["hf", "sf"] { + // Basic comparison: vgt* + map.insert( + format!("vgt{}", elem), + BuiltinSignature { + full_name: format!("V6_vgt{}", elem), + short_name: format!("vgt{}", elem), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + for op in ["and", "or", "xor"] { + // vgt*_and, vgt*_or, vgt*_xor + map.insert( + format!("vgt{}_{}", elem, op), + BuiltinSignature { + full_name: format!("V6_vgt{}_{}", elem, op), + short_name: format!("vgt{}_{}", elem, op), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + } + } + + // Prefix operations with predicate + for elem in ["b", "h", "w"] { + map.insert( + format!("vprefixq{}", elem), + BuiltinSignature { + full_name: format!("V6_vprefixq{}", elem), + short_name: format!("vprefixq{}", elem), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector], + }, + ); + } + + // Scatter operations with predicate + map.insert( + "vscattermhq".to_string(), + BuiltinSignature { + full_name: "V6_vscattermhq".to_string(), + short_name: "vscattermhq".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::HvxVector, + RustType::I32, + RustType::I32, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + + map.insert( + "vscattermhwq".to_string(), + BuiltinSignature { + full_name: "V6_vscattermhwq".to_string(), + short_name: "vscattermhwq".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::HvxVector, + RustType::I32, + RustType::I32, + RustType::HvxVectorPair, + RustType::HvxVector, + ], + }, + ); + + map.insert( + "vscattermwq".to_string(), + BuiltinSignature { + full_name: "V6_vscattermwq".to_string(), + short_name: "vscattermwq".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::HvxVector, + RustType::I32, + RustType::I32, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + + // Add with carry saturation + map.insert( + "vaddcarrysat".to_string(), + BuiltinSignature { + full_name: "V6_vaddcarrysat".to_string(), + short_name: "vaddcarrysat".to_string(), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + + // Gather operations with predicate + map.insert( + "vgathermhq".to_string(), + BuiltinSignature { + full_name: "V6_vgathermhq".to_string(), + short_name: "vgathermhq".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::MutPtrHvxVector, + RustType::HvxVector, + RustType::I32, + RustType::I32, + RustType::HvxVector, + ], + }, + ); + + map.insert( + "vgathermhwq".to_string(), + BuiltinSignature { + full_name: "V6_vgathermhwq".to_string(), + short_name: "vgathermhwq".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::MutPtrHvxVector, + RustType::HvxVector, + RustType::I32, + RustType::I32, + RustType::HvxVectorPair, + ], + }, + ); + + map.insert( + "vgathermwq".to_string(), + BuiltinSignature { + full_name: "V6_vgathermwq".to_string(), + short_name: "vgathermwq".to_string(), + return_type: RustType::Unit, + param_types: vec![ + RustType::MutPtrHvxVector, + RustType::HvxVector, + RustType::I32, + RustType::I32, + RustType::HvxVector, + ], + }, + ); + + // Basic comparison operations (without accumulation) + for elem in ["b", "h", "w", "ub", "uh", "uw"] { + // vgt* - greater than + map.insert( + format!("vgt{}", elem), + BuiltinSignature { + full_name: format!("V6_vgt{}", elem), + short_name: format!("vgt{}", elem), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + // veq* - equal + map.insert( + format!("veq{}", elem), + BuiltinSignature { + full_name: format!("V6_veq{}", elem), + short_name: format!("veq{}", elem), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + } + + // Conditional subtraction operations (vsub*q, vsub*nq) + for elem in ["b", "h", "w"] { + map.insert( + format!("vsub{}q", elem), + BuiltinSignature { + full_name: format!("V6_vsub{}q", elem), + short_name: format!("vsub{}q", elem), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + map.insert( + format!("vsub{}nq", elem), + BuiltinSignature { + full_name: format!("V6_vsub{}nq", elem), + short_name: format!("vsub{}nq", elem), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + } + + // vmux - vector mux (select based on predicate) + map.insert( + "vmux".to_string(), + BuiltinSignature { + full_name: "V6_vmux".to_string(), + short_name: "vmux".to_string(), + return_type: RustType::HvxVector, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + + // vswap - vector swap based on predicate + map.insert( + "vswap".to_string(), + BuiltinSignature { + full_name: "V6_vswap".to_string(), + short_name: "vswap".to_string(), + return_type: RustType::HvxVectorPair, + param_types: vec![ + RustType::HvxVector, + RustType::HvxVector, + RustType::HvxVector, + ], + }, + ); + + // shuffeq operations - take vectors (internal pred representation) and return vector + for elem in ["h", "w"] { + map.insert( + format!("shuffeq{}", elem), + BuiltinSignature { + full_name: format!("V6_shuffeq{}", elem), + short_name: format!("shuffeq{}", elem), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + } + + // Predicate AND with vector operations + map.insert( + "vandvqv".to_string(), + BuiltinSignature { + full_name: "V6_vandvqv".to_string(), + short_name: "vandvqv".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + map.insert( + "vandvnqv".to_string(), + BuiltinSignature { + full_name: "V6_vandvnqv".to_string(), + short_name: "vandvnqv".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector], + }, + ); + + // vandnqrt and vandnqrt_acc + map.insert( + "vandnqrt".to_string(), + BuiltinSignature { + full_name: "V6_vandnqrt".to_string(), + short_name: "vandnqrt".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::I32], + }, + ); + + map.insert( + "vandnqrt_acc".to_string(), + BuiltinSignature { + full_name: "V6_vandnqrt_acc".to_string(), + short_name: "vandnqrt_acc".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32], + }, + ); + + // pred_scalar2v2 + map.insert( + "pred_scalar2v2".to_string(), + BuiltinSignature { + full_name: "V6_pred_scalar2v2".to_string(), + short_name: "pred_scalar2v2".to_string(), + return_type: RustType::HvxVector, + param_types: vec![RustType::I32], + }, + ); + + map +} + +/// Generate extern declarations for all intrinsics for a specific vector mode +fn generate_extern_block(intrinsics: &[IntrinsicInfo], mode: VectorMode) -> String { + let mut output = String::new(); + + // Collect unique builtins to avoid duplicates + let mut seen_builtins: HashSet = HashSet::new(); + let mut decls: Vec<(String, String, RustType, Vec)> = Vec::new(); + + // First, add simple intrinsics + for info in intrinsics.iter().filter(|i| !i.is_compound) { + if seen_builtins.contains(&info.builtin_name) { + continue; + } + seen_builtins.insert(info.builtin_name.clone()); + + let param_types: Vec = info.params.iter().map(|(_, t)| t.clone()).collect(); + decls.push(( + info.builtin_name.clone(), + info.instr_name.clone(), + info.return_type.clone(), + param_types, + )); + } + + // Then, collect all builtins used in compound expressions + let helper_sigs = get_compound_helper_signatures(); + let mut compound_builtins: HashSet = HashSet::new(); + + for info in intrinsics.iter().filter(|i| i.is_compound) { + if let Some(ref expr) = info.compound_expr { + collect_builtins_from_expr(expr, &mut compound_builtins); + } + } + + // Add compound helper builtins + let mut missing_builtins = Vec::new(); + for builtin_name in compound_builtins { + let full_name = format!("V6_{}", builtin_name); + if seen_builtins.contains(&full_name) { + continue; + } + seen_builtins.insert(full_name.clone()); + + if let Some(sig) = helper_sigs.get(&builtin_name) { + decls.push(( + sig.full_name.clone(), + sig.short_name.clone(), + sig.return_type.clone(), + sig.param_types.clone(), + )); + } else { + missing_builtins.push(builtin_name); + } + } + + // Report missing builtins (for development purposes) + if !missing_builtins.is_empty() { + eprintln!("Warning: Missing helper signatures for compound builtins:"); + for name in &missing_builtins { + eprintln!(" - {}", name); + } + } + + // Sort by builtin name for consistent output + decls.sort_by(|a, b| a.0.cmp(&b.0)); + + // Generate intrinsic declarations for the specified mode + output.push_str(&format!( + "// LLVM intrinsic declarations for {}-byte vector mode\n", + mode.bytes() + )); + output.push_str("#[allow(improper_ctypes)]\n"); + output.push_str("unsafe extern \"unadjusted\" {\n"); + + for (builtin_name, instr_name, return_type, param_types) in &decls { + let base_link = builtin_name.replace('_', "."); + // 128-byte mode uses .128B suffix, 64-byte mode doesn't + let link_name = if builtin_name.starts_with("V6_") && mode == VectorMode::V128 { + format!("llvm.hexagon.{}.128B", base_link) + } else { + format!("llvm.hexagon.{}", base_link) + }; + + let params_str = if param_types.is_empty() { + String::new() + } else { + param_types + .iter() + .map(|t| format!("_: {}", t.to_extern_str())) + .collect::>() + .join(", ") + }; + + let return_str = if *return_type == RustType::Unit { + " -> ()".to_string() + } else { + format!(" -> {}", return_type.to_extern_str()) + }; + + output.push_str(&format!( + " #[link_name = \"{}\"]\n fn {}({}){};\n", + link_name, instr_name, params_str, return_str + )); + } + + output.push_str("}\n"); + output +} + +/// Generate Rust code for a compound expression +/// `params` maps parameter names to their types in the function signature +/// Get the type of an expression +fn get_expr_type( + expr: &CompoundExpr, + params: &HashMap, + helper_sigs: &HashMap, +) -> Option { + match expr { + CompoundExpr::BuiltinCall(name, _) => { + helper_sigs.get(name).map(|sig| sig.return_type.clone()) + } + CompoundExpr::Param(name) => params.get(name).cloned(), + CompoundExpr::IntLiteral(_) => Some(RustType::I32), + } +} + +fn generate_compound_expr_code( + expr: &CompoundExpr, + params: &HashMap, + helper_sigs: &HashMap, +) -> String { + match expr { + CompoundExpr::BuiltinCall(name, args) => { + // Get the expected parameter types for this builtin + let expected_types = helper_sigs + .get(name) + .map(|sig| sig.param_types.clone()) + .unwrap_or_default(); + + let args_code: Vec = args + .iter() + .enumerate() + .map(|(i, arg)| { + let arg_code = generate_compound_expr_code(arg, params, helper_sigs); + + // Check if we need to transmute this argument + let expected_type = expected_types.get(i); + let actual_type = get_expr_type(arg, params, helper_sigs); + + // If the builtin expects HvxVector but the arg is HvxVectorPred, transmute + if expected_type == Some(&RustType::HvxVector) + && actual_type == Some(RustType::HvxVectorPred) + { + format!( + "core::mem::transmute::({})", + arg_code + ) + } else { + arg_code + } + }) + .collect(); + format!("{}({})", name, args_code.join(", ")) + } + CompoundExpr::Param(name) => name.clone(), + CompoundExpr::IntLiteral(n) => n.to_string(), + } +} + +/// Get the primary instruction name from a compound expression (innermost significant op) +fn get_compound_primary_instr(expr: &CompoundExpr) -> Option { + match expr { + CompoundExpr::BuiltinCall(name, args) => { + // For vandqrt wrapper, look inside + if name == "vandqrt" && !args.is_empty() { + if let Some(inner) = get_compound_primary_instr(&args[0]) { + return Some(inner); + } + } + // For store operations, use the store name + if name.starts_with("vS32b") { + return Some(name.clone()); + } + // For conditional accumulation, use the add name + if name.starts_with("vadd") && (name.ends_with("q") || name.ends_with("nq")) { + return Some(name.clone()); + } + // For predicate operations + if name.starts_with("pred_") { + return Some(name.clone()); + } + // For comparison operations with accumulation + if (name.starts_with("veq") || name.starts_with("vgt")) + && (name.ends_with("_and") || name.ends_with("_or") || name.ends_with("_xor")) + { + return Some(name.clone()); + } + Some(name.clone()) + } + _ => None, + } +} + +/// Get override implementations for specific compound intrinsics. +/// Some C macros rely on implicit type conversions that don't work with +/// our stricter Rust types, so we provide corrected implementations. +fn get_compound_overrides() -> HashMap<&'static str, &'static str> { + let mut map = HashMap::new(); + + // Q6_V_vand_QR: takes pred, returns vec + // Use transmute to convert pred to vec for LLVM, call vandvrt + map.insert( + "Q6_V_vand_QR", + "vandvrt(core::mem::transmute::(qu), rt)", + ); + + // Q6_V_vandor_VQR: takes vec and pred, returns vec + map.insert( + "Q6_V_vandor_VQR", + "vandvrt_acc(vx, core::mem::transmute::(qu), rt)", + ); + + // Q6_Q_vand_VR: takes vec, returns pred + map.insert( + "Q6_Q_vand_VR", + "core::mem::transmute::(vandqrt(vu, rt))", + ); + + // Q6_Q_vandor_QVR: takes pred and vec, returns pred + map.insert( + "Q6_Q_vandor_QVR", + "core::mem::transmute::(vandqrt_acc(core::mem::transmute::(qx), vu, rt))", + ); + + map +} + +/// Generate wrapper functions for all intrinsics +fn generate_functions(intrinsics: &[IntrinsicInfo]) -> String { + let mut output = String::new(); + let simd_mappings = get_simd_intrinsic_mappings(); + + // Generate simple intrinsics + for info in intrinsics.iter().filter(|i| !i.is_compound) { + let rust_name = &info.q6_name; + + // Generate doc comment + output.push_str(&format!("/// `{}`\n", info.asm_syntax)); + output.push_str("///\n"); + output.push_str(&format!("/// Instruction Type: {}\n", info.instr_type)); + output.push_str(&format!("/// Execution Slots: {}\n", info.exec_slots)); + + // Generate attributes + output.push_str("#[inline]\n"); + output.push_str(&format!( + "#[cfg_attr(target_arch = \"hexagon\", target_feature(enable = \"hvxv{}\"))]\n", + info.min_arch + )); + + // Check if we should use simd intrinsic instead + let use_simd = simd_mappings.get(info.instr_name.as_str()); + + // assert_instr uses the original instruction name + output.push_str(&format!( + "#[cfg_attr(test, assert_instr({}))]\n", + info.instr_name + )); + + output.push_str(&format!( + "#[unstable(feature = \"stdarch_hexagon\", issue = \"{}\")]\n", + TRACKING_ISSUE + )); + + // Generate function signature + let params_str = info + .params + .iter() + .map(|(name, ty)| format!("{}: {}", name, ty.to_rust_str())) + .collect::>() + .join(", "); + + let return_str = if info.return_type == RustType::Unit { + String::new() + } else { + format!(" -> {}", info.return_type.to_rust_str()) + }; + + output.push_str(&format!( + "pub unsafe fn {}({}){} {{\n", + rust_name, params_str, return_str + )); + + // Generate function body + let args_str = info + .params + .iter() + .map(|(name, _)| name.as_str()) + .collect::>() + .join(", "); + + if let Some(simd_fn) = use_simd { + // Use architecture-independent simd intrinsic + output.push_str(&format!(" {}({})\n", simd_fn, args_str)); + } else { + // Use the LLVM intrinsic + output.push_str(&format!(" {}({})\n", info.instr_name, args_str)); + } + + output.push_str("}\n\n"); + } + + // Generate compound intrinsics + let helper_sigs = get_compound_helper_signatures(); + let overrides = get_compound_overrides(); + for info in intrinsics.iter().filter(|i| i.is_compound) { + if let Some(ref compound_expr) = info.compound_expr { + let rust_name = &info.q6_name; + + // Get the primary instruction for assert_instr + let _primary_instr = get_compound_primary_instr(compound_expr) + .unwrap_or_else(|| info.instr_name.clone()); + + // Generate doc comment + output.push_str(&format!("/// `{}`\n", info.asm_syntax)); + output.push_str("///\n"); + output.push_str( + "/// This is a compound operation composed of multiple HVX instructions.\n", + ); + if !info.instr_type.is_empty() { + output.push_str(&format!("/// Instruction Type: {}\n", info.instr_type)); + } + if !info.exec_slots.is_empty() { + output.push_str(&format!("/// Execution Slots: {}\n", info.exec_slots)); + } + + // Generate attributes + output.push_str("#[inline]\n"); + output.push_str(&format!( + "#[cfg_attr(target_arch = \"hexagon\", target_feature(enable = \"hvxv{}\"))]\n", + info.min_arch + )); + + // For compound ops, we skip assert_instr since they emit multiple instructions + // output.push_str(&format!( + // "#[cfg_attr(test, assert_instr({}))]\n", + // primary_instr + // )); + + output.push_str(&format!( + "#[unstable(feature = \"stdarch_hexagon\", issue = \"{}\")]\n", + TRACKING_ISSUE + )); + + // Generate function signature + let params_str = info + .params + .iter() + .map(|(name, ty)| format!("{}: {}", name, ty.to_rust_str())) + .collect::>() + .join(", "); + + let return_str = if info.return_type == RustType::Unit { + String::new() + } else { + format!(" -> {}", info.return_type.to_rust_str()) + }; + + output.push_str(&format!( + "pub unsafe fn {}({}){} {{\n", + rust_name, params_str, return_str + )); + + // Check if we have an override for this intrinsic + let body = if let Some(override_body) = overrides.get(info.q6_name.as_str()) { + override_body.to_string() + } else { + // Build param type map for expression code generation + let param_types: HashMap = info.params.iter().cloned().collect(); + // Generate function body from compound expression + let expr_body = + generate_compound_expr_code(compound_expr, ¶m_types, &helper_sigs); + + // Check if we need to transmute the result + let expr_return_type = get_expr_type(compound_expr, ¶m_types, &helper_sigs); + if info.return_type == RustType::HvxVectorPred + && expr_return_type == Some(RustType::HvxVector) + { + format!( + "core::mem::transmute::({})", + expr_body + ) + } else { + expr_body + } + }; + output.push_str(&format!(" {}\n", body)); + + output.push_str("}\n\n"); + } + } + + output +} + +/// Generate a module file for a specific vector mode +fn generate_module_file( + intrinsics: &[IntrinsicInfo], + output_path: &Path, + mode: VectorMode, +) -> Result<(), String> { + let mut output = + File::create(output_path).map_err(|e| format!("Failed to create output: {}", e))?; + + writeln!(output, "{}", GENERATED_MARKER).map_err(|e| e.to_string())?; + writeln!(output, "{}", generate_module_doc(mode)).map_err(|e| e.to_string())?; + writeln!(output, "{}", generate_types(mode)).map_err(|e| e.to_string())?; + writeln!(output, "{}", generate_extern_block(intrinsics, mode)).map_err(|e| e.to_string())?; + writeln!(output, "{}", generate_functions(intrinsics)).map_err(|e| e.to_string())?; + + // Ensure file is flushed before running rustfmt + drop(output); + + // Run rustfmt on the generated file + let status = std::process::Command::new("rustfmt") + .arg(output_path) + .status() + .map_err(|e| format!("Failed to run rustfmt: {}", e))?; + + if !status.success() { + return Err("rustfmt failed".to_string()); + } + + Ok(()) +} + +/// Parse the HVX header in `crate_dir` and write `v64.rs` and `v128.rs` into `out_dir`. +pub fn generate(crate_dir: &std::path::Path, out_dir: &std::path::Path) -> Result<(), String> { + let header_content = read_header(crate_dir)?; + let all_intrinsics = parse_header(&header_content); + let intrinsics: Vec<_> = all_intrinsics + .into_iter() + .filter(|i| i.min_arch <= MAX_SUPPORTED_ARCH) + .collect(); + for (filename, vmode) in [("v64.rs", VectorMode::V64), ("v128.rs", VectorMode::V128)] { + let path = out_dir.join(filename); + generate_module_file(&intrinsics, &path, vmode)?; + } + Ok(()) +} diff --git a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs index c3ad153ab0a7a..88ef4fe11f332 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs @@ -1,1728 +1,35 @@ -//! Hexagon HVX Code Generator +//! Hexagon code generator. //! -//! This generator creates v64.rs and v128.rs from scratch using the LLVM HVX -//! header file as the sole source of truth. It parses the C intrinsic prototypes -//! and generates Rust wrapper functions with appropriate attributes. +//! Single binary that produces every generated file under +//! `core_arch/src/hexagon/`: scalar.rs (scalar intrinsics) and +//! v64.rs / v128.rs (HVX intrinsics). //! -//! The two generated files provide: -//! - v64.rs: 64-byte vector mode intrinsics (512-bit vectors) -//! - v128.rs: 128-byte vector mode intrinsics (1024-bit vectors) -//! -//! Both modules are available unconditionally, but require the appropriate -//! target features to actually use the intrinsics. -//! -//! Usage: -//! cd crates/stdarch-gen-hexagon -//! cargo run -//! # Output is written to ../core_arch/src/hexagon/v64.rs and v128.rs - -use regex::Regex; -use std::collections::{HashMap, HashSet}; -use std::fs::File; -use std::io::Write; -use std::path::Path; -use stdarch_gen_common::{run_generator, Mode, GENERATED_MARKER}; - -/// Mappings from HVX intrinsics to architecture-independent SIMD intrinsics. -/// These intrinsics have equivalent semantics and can be lowered to the generic form. -fn get_simd_intrinsic_mappings() -> HashMap<&'static str, &'static str> { - let mut map = HashMap::new(); - // Bitwise operations (element-size independent) - map.insert("vxor", "simd_xor"); - map.insert("vand", "simd_and"); - map.insert("vor", "simd_or"); - // Word (32-bit) arithmetic operations - map.insert("vaddw", "simd_add"); - map.insert("vsubw", "simd_sub"); - map -} - -/// The tracking issue number for the stdarch_hexagon feature -const TRACKING_ISSUE: &str = "151523"; - -/// HVX vector length mode -#[derive(Debug, Clone, Copy, PartialEq)] -enum VectorMode { - /// 64-byte vectors (512 bits) - V64, - /// 128-byte vectors (1024 bits) - V128, -} - -impl VectorMode { - fn bytes(&self) -> u32 { - match self { - VectorMode::V64 => 64, - VectorMode::V128 => 128, - } - } - - fn bits(&self) -> u32 { - self.bytes() * 8 - } - - fn lanes(&self) -> u32 { - self.bytes() / 4 // 32-bit lanes - } - - fn target_feature(&self) -> &'static str { - match self { - VectorMode::V64 => "hvx-length64b", - VectorMode::V128 => "hvx-length128b", - } - } -} - -/// LLVM version the header file is from (for reference) -/// Source: https://github.com/llvm/llvm-project/blob/llvmorg-22.1.0-rc1/clang/lib/Headers/hvx_hexagon_protos.h -const LLVM_VERSION: &str = "22.1.0-rc1"; - -/// Maximum HVX architecture version supported by rustc -/// Check with: rustc --target=hexagon-unknown-linux-musl --print target-features -const MAX_SUPPORTED_ARCH: u32 = 79; - -/// Local header file path (checked into the repository) -const HEADER_FILE: &str = "hvx_hexagon_protos.h"; - -/// Intrinsic information parsed from the LLVM header -#[derive(Debug, Clone)] -struct IntrinsicInfo { - /// The Q6_* intrinsic name (e.g., "Q6_V_vadd_VV") - q6_name: String, - /// The LLVM builtin name without prefix (e.g., "V6_vaddb") - builtin_name: String, - /// The short instruction name for assert_instr (e.g., "vaddb") - instr_name: String, - /// The assembly syntax from the comment - asm_syntax: String, - /// Instruction type - instr_type: String, - /// Execution slots - exec_slots: String, - /// Minimum HVX architecture version required - min_arch: u32, - /// Return type - return_type: RustType, - /// Parameters (name, type) - params: Vec<(String, RustType)>, - /// Whether this is a compound intrinsic (multiple builtins) - is_compound: bool, - /// For compound intrinsics: the parsed expression tree - compound_expr: Option, -} - -/// Expression tree for compound intrinsics -#[derive(Debug, Clone)] -enum CompoundExpr { - /// A call to a builtin: (builtin_name without V6_ prefix, arguments) - BuiltinCall(String, Vec), - /// A parameter reference by name - Param(String), - /// An integer literal (like -1) - IntLiteral(i32), -} - -/// Rust type mappings -#[derive(Debug, Clone, PartialEq)] -enum RustType { - HvxVector, - HvxVectorPair, - HvxVectorPred, - I32, - MutPtrHvxVector, - Unit, -} - -impl RustType { - fn from_c_type(c_type: &str) -> Option { - match c_type.trim() { - "HVX_Vector" => Some(RustType::HvxVector), - "HVX_VectorPair" => Some(RustType::HvxVectorPair), - "HVX_VectorPred" => Some(RustType::HvxVectorPred), - "Word32" => Some(RustType::I32), - "HVX_Vector*" => Some(RustType::MutPtrHvxVector), - "void" => Some(RustType::Unit), - _ => None, - } - } - - fn to_rust_str(&self) -> &'static str { - match self { - RustType::HvxVector => "HvxVector", - RustType::HvxVectorPair => "HvxVectorPair", - RustType::HvxVectorPred => "HvxVectorPred", - RustType::I32 => "i32", - RustType::MutPtrHvxVector => "*mut HvxVector", - RustType::Unit => "()", - } - } - - fn to_extern_str(&self) -> &'static str { - match self { - RustType::HvxVector => "HvxVector", - RustType::HvxVectorPair => "HvxVectorPair", - RustType::HvxVectorPred => "HvxVectorPred", - RustType::I32 => "i32", - RustType::MutPtrHvxVector => "*mut HvxVector", - RustType::Unit => "()", - } - } -} - -/// Parse a compound macro expression into an expression tree -fn parse_compound_expr(expr: &str) -> Option { - let expr = expr.trim(); - - // Try to match an integer literal (like -1) - if let Ok(n) = expr.parse::() { - return Some(CompoundExpr::IntLiteral(n)); - } - - // Try to match a simple parameter name (Vu, Vv, Rt, Qs, Qt, Qx, Vx, etc.) - // These are typically short identifiers in the macro - if expr.len() <= 3 - && expr.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') - && !expr.contains("__") - { - return Some(CompoundExpr::Param(expr.to_lowercase())); - } - - // Check if it's wrapped in extra parens first - if expr.starts_with('(') && expr.ends_with(')') { - // Check if these parens wrap the entire expression - let inner = &expr[1..expr.len() - 1]; - // Count depth: if after removing outer parens the expression is balanced, - // the outer parens were enclosing everything - if is_balanced_parens(inner) { - // But we also need to verify these aren't part of a function call - // If the inner expression is balanced and the whole thing starts with ( - // and ends with ), it's a paren wrapper - let result = parse_compound_expr(inner); - if result.is_some() { - return result; - } - } - } - - // Try to match __BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_xxx)(args) - // The args portion may contain nested calls, so we need to find the matching paren - if expr.starts_with("__BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_") { - // Find the end of the builtin name (after V6_) - let prefix = "__BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_"; - let after_prefix = &expr[prefix.len()..]; - if let Some(paren_pos) = after_prefix.find(')') { - let builtin_name = &after_prefix[..paren_pos]; - let rest = &after_prefix[paren_pos + 1..]; // Skip the closing ) of the WRAP - // rest should now be "(args)" - if rest.starts_with('(') && rest.ends_with(')') { - let args_str = &rest[1..rest.len() - 1]; - let args = parse_compound_args(args_str)?; - return Some(CompoundExpr::BuiltinCall(builtin_name.to_string(), args)); - } - } - } - - // Try to match __builtin_HEXAGON_V6_xxx(args) without wrap - if expr.starts_with("__builtin_HEXAGON_V6_") { - let prefix = "__builtin_HEXAGON_V6_"; - let after_prefix = &expr[prefix.len()..]; - if let Some(paren_pos) = after_prefix.find('(') { - let builtin_name = &after_prefix[..paren_pos]; - let rest = &after_prefix[paren_pos..]; - if rest.starts_with('(') && rest.ends_with(')') { - let args_str = &rest[1..rest.len() - 1]; - let args = parse_compound_args(args_str)?; - return Some(CompoundExpr::BuiltinCall(builtin_name.to_string(), args)); - } - } - } - - None -} - -/// Check if parentheses are balanced in a string -fn is_balanced_parens(s: &str) -> bool { - let mut depth = 0; - for c in s.chars() { - match c { - '(' => depth += 1, - ')' => { - depth -= 1; - if depth < 0 { - return false; - } - } - _ => {} - } - } - depth == 0 -} - -/// Parse comma-separated arguments, respecting nested parentheses -fn parse_compound_args(args_str: &str) -> Option> { - let mut args = Vec::new(); - let mut current = String::new(); - let mut depth = 0; - - for c in args_str.chars() { - match c { - '(' => { - depth += 1; - current.push(c); - } - ')' => { - depth -= 1; - current.push(c); - } - ',' if depth == 0 => { - let arg = current.trim().to_string(); - if !arg.is_empty() { - args.push(parse_compound_expr(&arg)?); - } - current.clear(); - } - _ => current.push(c), - } - } - - // Don't forget the last argument - let arg = current.trim().to_string(); - if !arg.is_empty() { - args.push(parse_compound_expr(&arg)?); - } - - Some(args) -} - -/// Extract all builtin names used in a compound expression -fn collect_builtins_from_expr(expr: &CompoundExpr, builtins: &mut HashSet) { - match expr { - CompoundExpr::BuiltinCall(name, args) => { - builtins.insert(name.clone()); - for arg in args { - collect_builtins_from_expr(arg, builtins); - } - } - CompoundExpr::Param(_) | CompoundExpr::IntLiteral(_) => {} - } -} - -/// Read the local HVX header file -fn read_header(crate_dir: &Path) -> Result { - let header_path = crate_dir.join(HEADER_FILE); - println!("Reading HVX header from: {}", header_path.display()); - println!(" (LLVM version: {})", LLVM_VERSION); - - std::fs::read_to_string(&header_path).map_err(|e| { - format!( - "Failed to read header file {}: {}", - header_path.display(), - e - ) - }) -} - -/// Parse a C function prototype to extract return type and parameters -fn parse_prototype(prototype: &str) -> Option<(RustType, Vec<(String, RustType)>)> { - // Pattern: ReturnType FunctionName(ParamType1 Param1, ParamType2 Param2, ...) - let proto_re = Regex::new(r"(\w+(?:\*)?)\s+Q6_\w+\(([^)]*)\)").unwrap(); - - if let Some(caps) = proto_re.captures(prototype) { - let return_type_str = caps[1].trim(); - let params_str = &caps[2]; - - let return_type = RustType::from_c_type(return_type_str)?; - - let mut params = Vec::new(); - if !params_str.trim().is_empty() { - // Pattern: Type Name or Type* Name - let param_re = Regex::new(r"(\w+\*?)\s+(\w+)").unwrap(); - for param in params_str.split(',') { - let param = param.trim(); - if let Some(pcaps) = param_re.captures(param) { - let ptype_str = pcaps[1].trim(); - let pname = pcaps[2].to_lowercase(); - if let Some(ptype) = RustType::from_c_type(ptype_str) { - params.push((pname, ptype)); - } else { - return None; // Unknown type - } - } - } - } - - Some((return_type, params)) - } else { - None - } -} - -/// Parse the LLVM header file to extract intrinsic information -fn parse_header(content: &str) -> Vec { - let mut intrinsics = Vec::new(); - - let arch_re = Regex::new(r"#if __HVX_ARCH__ >= (\d+)").unwrap(); - - // Regex to extract the simple builtin name from a macro body - // Match: __BUILTIN_VECTOR_WRAP(__builtin_HEXAGON_V6_xxx)(args) - let simple_builtin_re = - Regex::new(r"__BUILTIN_VECTOR_WRAP\(__builtin_HEXAGON_(\w+)\)\([^)]*\)\s*$").unwrap(); - - // Also handle builtins without VECTOR_WRAP - let simple_builtin_re2 = Regex::new(r"__builtin_HEXAGON_(\w+)\([^)]*\)\s*$").unwrap(); - - // Regex to extract Q6 name from #define - let q6_name_re = Regex::new(r"#define\s+(Q6_\w+)").unwrap(); - - // Regex to extract macro expression body - let macro_expr_re = Regex::new(r"#define\s+Q6_\w+\([^)]*\)\s+(.+)").unwrap(); - - let lines: Vec<&str> = content.lines().collect(); - let mut current_arch: u32 = 60; - let mut i = 0; - - while i < lines.len() { - // Track architecture version - if let Some(caps) = arch_re.captures(lines[i]) { - if let Ok(arch) = caps[1].parse() { - current_arch = arch; - } - } - - // Look for Assembly Syntax comment block - if lines[i].contains("Assembly Syntax:") { - let mut asm_syntax = String::new(); - let mut prototype = String::new(); - let mut instr_type = String::new(); - let mut exec_slots = String::new(); - - // Parse the comment block - let mut j = i; - while j < lines.len() && !lines[j].starts_with("#define") { - let line = lines[j]; - if line.contains("Assembly Syntax:") { - if let Some(pos) = line.find("Assembly Syntax:") { - asm_syntax = line[pos + 16..].trim().to_string(); - } - } else if line.contains("C Intrinsic Prototype:") { - if let Some(pos) = line.find("C Intrinsic Prototype:") { - prototype = line[pos + 22..].trim().to_string(); - } - } else if line.contains("Instruction Type:") { - if let Some(pos) = line.find("Instruction Type:") { - instr_type = line[pos + 17..].trim().to_string(); - } - } else if line.contains("Execution Slots:") { - if let Some(pos) = line.find("Execution Slots:") { - exec_slots = line[pos + 16..].trim().to_string(); - } - } - j += 1; - } - - // Now find the #define line - while j < lines.len() && !lines[j].starts_with("#define") { - j += 1; - } - - if j < lines.len() { - let define_line = lines[j]; - - // Extract Q6 name and check if it's simple or compound - if let Some(caps) = q6_name_re.captures(define_line) { - let q6_name = caps[1].to_string(); - - // Get the full macro body (handle line continuations) - let mut macro_body = define_line.to_string(); - let mut k = j; - while macro_body.trim_end().ends_with('\\') && k + 1 < lines.len() { - k += 1; - macro_body.push_str(lines[k]); - } - - // Try to extract simple builtin name - let builtin_name = simple_builtin_re - .captures(¯o_body) - .or_else(|| simple_builtin_re2.captures(¯o_body)) - .map(|bcaps| bcaps[1].to_string()); - - // Check if it's a compound intrinsic (multiple __builtin calls) - let builtin_count = macro_body.matches("__builtin_HEXAGON_").count(); - let is_compound = builtin_count > 1; - - // Parse prototype - if let Some((return_type, params)) = parse_prototype(&prototype) { - if is_compound { - // For compound intrinsics, parse the expression - // Extract the macro body after the parameter list - if let Some(expr_caps) = macro_expr_re.captures(¯o_body) { - let expr_str = expr_caps[1].trim().replace(['\n', '\\'], " "); - let expr_str = expr_str.trim(); - - if let Some(compound_expr) = parse_compound_expr(expr_str) { - // For compound intrinsics, we use the outermost builtin - // as the "primary" for the instruction name - let (primary_builtin, instr_name) = match &compound_expr { - CompoundExpr::BuiltinCall(name, _) => { - (name.clone(), name.clone()) - } - _ => continue, - }; - - intrinsics.push(IntrinsicInfo { - q6_name, - builtin_name: format!("V6_{}", primary_builtin), - instr_name, - asm_syntax, - instr_type, - exec_slots, - min_arch: current_arch, - return_type, - params, - is_compound: true, - compound_expr: Some(compound_expr), - }); - } - } - } else if let Some(builtin) = builtin_name { - // Extract short instruction name - let instr_name = builtin - .strip_prefix("V6_") - .map(|s| s.to_string()) - .unwrap_or_else(|| builtin.clone()); - - intrinsics.push(IntrinsicInfo { - q6_name, - builtin_name: builtin, - instr_name, - asm_syntax, - instr_type, - exec_slots, - min_arch: current_arch, - return_type, - params, - is_compound: false, - compound_expr: None, - }); - } - } - } - } - i = j; - } - i += 1; - } - - intrinsics -} - -/// Generate the module documentation -fn generate_module_doc(mode: VectorMode) -> String { - format!( - r#"//! Hexagon HVX {bytes}-byte vector mode intrinsics -//! -//! This module provides intrinsics for the Hexagon Vector Extensions (HVX) -//! in {bytes}-byte vector mode ({bits}-bit vectors). -//! -//! HVX is a wide vector extension designed for high-performance signal processing. -//! [Hexagon HVX Programmer's Reference Manual](https://docs.qualcomm.com/doc/80-N2040-61) -//! -//! ## Vector Types -//! -//! In {bytes}-byte mode: -//! - `HvxVector` is {bits} bits ({bytes} bytes) containing {lanes} x 32-bit values -//! - `HvxVectorPair` is {pair_bits} bits ({pair_bytes} bytes) -//! - `HvxVectorPred` is {bits} bits ({bytes} bytes) for predicate operations -//! -//! To use this module, compile with `-C target-feature=+{target_feature}`. -//! -//! ## Naming Convention -//! -//! Function names preserve the original Q6 naming case because the convention -//! uses case to distinguish register types: -//! - `W` (uppercase) = vector pair (`HvxVectorPair`) -//! - `V` (uppercase) = vector (`HvxVector`) -//! - `Q` (uppercase) = predicate (`HvxVectorPred`) -//! - `R` = scalar register (`i32`) -//! -//! For example, `Q6_W_vcombine_VV` operates on a vector pair while -//! `Q6_V_hi_W` extracts a vector from a pair. -//! -//! ## Architecture Versions -//! -//! Different intrinsics require different HVX architecture versions. Use the -//! appropriate target feature to enable the required version: -//! - HVX v60: `-C target-feature=+hvxv60` (basic HVX operations) -//! - HVX v62: `-C target-feature=+hvxv62` -//! - HVX v65: `-C target-feature=+hvxv65` (includes floating-point support) -//! - HVX v66: `-C target-feature=+hvxv66` -//! - HVX v68: `-C target-feature=+hvxv68` -//! - HVX v69: `-C target-feature=+hvxv69` -//! - HVX v73: `-C target-feature=+hvxv73` -//! - HVX v79: `-C target-feature=+hvxv79` -//! -//! Each version includes all features from previous versions. -"#, - bytes = mode.bytes(), - bits = mode.bits(), - lanes = mode.lanes(), - pair_bytes = mode.bytes() * 2, - pair_bits = mode.bits() * 2, - target_feature = mode.target_feature(), - ) -} - -/// Generate the type definitions for a specific vector mode -fn generate_types(mode: VectorMode) -> String { - let lanes = mode.lanes(); - let pair_lanes = lanes * 2; - let bits = mode.bits(); - let bytes = mode.bytes(); - let pair_bits = bits * 2; - let pair_bytes = bytes * 2; - - format!( - r#" -#![allow(non_camel_case_types)] -#![allow(non_snake_case)] - -#[cfg(test)] -use stdarch_test::assert_instr; - -use crate::intrinsics::simd::{{simd_add, simd_and, simd_or, simd_sub, simd_xor}}; - -// HVX type definitions for {bytes}-byte vector mode -types! {{ - #![unstable(feature = "stdarch_hexagon", issue = "{TRACKING_ISSUE}")] - - /// HVX vector type ({bits} bits / {bytes} bytes) - /// - /// This type represents a single HVX vector register containing {lanes} x 32-bit values. - pub struct HvxVector({lanes} x i32); - - /// HVX vector pair type ({pair_bits} bits / {pair_bytes} bytes) - /// - /// This type represents a pair of HVX vector registers, often used for - /// operations that produce double-width results. - pub struct HvxVectorPair({pair_lanes} x i32); - - /// HVX vector predicate type ({bits} bits / {bytes} bytes) - /// - /// This type represents a predicate vector used for conditional operations. - /// Each bit corresponds to a lane in the vector. - pub struct HvxVectorPred({lanes} x i32); -}} -"#, - bytes = bytes, - bits = bits, - lanes = lanes, - pair_bits = pair_bits, - pair_bytes = pair_bytes, - pair_lanes = pair_lanes, - TRACKING_ISSUE = TRACKING_ISSUE, - ) -} - -/// Builtin signature information for extern declarations -struct BuiltinSignature { - /// The V6_ prefixed name - full_name: String, - /// The short name (without V6_) - short_name: String, - /// Return type - return_type: RustType, - /// Parameter types - param_types: Vec, -} - -/// Get known signatures for builtins used in compound operations -/// These are the helper builtins that don't have their own Q6_ wrapper -fn get_compound_helper_signatures() -> HashMap { - let mut map = HashMap::new(); - - // vandvrt: HVX_Vector -> i32 -> HVX_Vector - // Converts predicate to vector representation. LLVM uses HVX_Vector for both. - map.insert( - "vandvrt".to_string(), - BuiltinSignature { - full_name: "V6_vandvrt".to_string(), - short_name: "vandvrt".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::I32], - }, - ); - - // vandqrt: HVX_Vector -> i32 -> HVX_Vector - // Converts vector representation back to predicate. LLVM uses HVX_Vector for both. - map.insert( - "vandqrt".to_string(), - BuiltinSignature { - full_name: "V6_vandqrt".to_string(), - short_name: "vandqrt".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::I32], - }, - ); - - // vandvrt_acc: HVX_Vector -> HVX_Vector -> i32 -> HVX_Vector - map.insert( - "vandvrt_acc".to_string(), - BuiltinSignature { - full_name: "V6_vandvrt_acc".to_string(), - short_name: "vandvrt_acc".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32], - }, - ); - - // vandqrt_acc: HVX_Vector -> HVX_Vector -> i32 -> HVX_Vector - map.insert( - "vandqrt_acc".to_string(), - BuiltinSignature { - full_name: "V6_vandqrt_acc".to_string(), - short_name: "vandqrt_acc".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32], - }, - ); - - // pred_and: HVX_Vector -> HVX_Vector -> HVX_Vector - map.insert( - "pred_and".to_string(), - BuiltinSignature { - full_name: "V6_pred_and".to_string(), - short_name: "pred_and".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - - // pred_and_n: HVX_Vector -> HVX_Vector -> HVX_Vector - map.insert( - "pred_and_n".to_string(), - BuiltinSignature { - full_name: "V6_pred_and_n".to_string(), - short_name: "pred_and_n".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - - // pred_or: HVX_Vector -> HVX_Vector -> HVX_Vector - map.insert( - "pred_or".to_string(), - BuiltinSignature { - full_name: "V6_pred_or".to_string(), - short_name: "pred_or".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - - // pred_or_n: HVX_Vector -> HVX_Vector -> HVX_Vector - map.insert( - "pred_or_n".to_string(), - BuiltinSignature { - full_name: "V6_pred_or_n".to_string(), - short_name: "pred_or_n".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); +//! Run in check or bless mode via `STDARCH_GEN_MODE`. - // pred_xor: HVX_Vector -> HVX_Vector -> HVX_Vector - map.insert( - "pred_xor".to_string(), - BuiltinSignature { - full_name: "V6_pred_xor".to_string(), - short_name: "pred_xor".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); +mod hvx; +mod scalar; - // pred_not: HVX_Vector -> HVX_Vector - map.insert( - "pred_not".to_string(), - BuiltinSignature { - full_name: "V6_pred_not".to_string(), - short_name: "pred_not".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector], - }, - ); - - // pred_scalar2: i32 -> HVX_Vector - map.insert( - "pred_scalar2".to_string(), - BuiltinSignature { - full_name: "V6_pred_scalar2".to_string(), - short_name: "pred_scalar2".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::I32], - }, - ); - - // Conditional store operations - map.insert( - "vS32b_qpred_ai".to_string(), - BuiltinSignature { - full_name: "V6_vS32b_qpred_ai".to_string(), - short_name: "vS32b_qpred_ai".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::HvxVector, - RustType::MutPtrHvxVector, - RustType::HvxVector, - ], - }, - ); - - map.insert( - "vS32b_nqpred_ai".to_string(), - BuiltinSignature { - full_name: "V6_vS32b_nqpred_ai".to_string(), - short_name: "vS32b_nqpred_ai".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::HvxVector, - RustType::MutPtrHvxVector, - RustType::HvxVector, - ], - }, - ); - - map.insert( - "vS32b_nt_qpred_ai".to_string(), - BuiltinSignature { - full_name: "V6_vS32b_nt_qpred_ai".to_string(), - short_name: "vS32b_nt_qpred_ai".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::HvxVector, - RustType::MutPtrHvxVector, - RustType::HvxVector, - ], - }, - ); - - map.insert( - "vS32b_nt_nqpred_ai".to_string(), - BuiltinSignature { - full_name: "V6_vS32b_nt_nqpred_ai".to_string(), - short_name: "vS32b_nt_nqpred_ai".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::HvxVector, - RustType::MutPtrHvxVector, - RustType::HvxVector, - ], - }, - ); - - // Conditional accumulation operations - for (suffix, _elem) in [("b", "byte"), ("h", "halfword"), ("w", "word")] { - // vaddbq, vaddhq, vaddwq - map.insert( - format!("vadd{}q", suffix), - BuiltinSignature { - full_name: format!("V6_vadd{}q", suffix), - short_name: format!("vadd{}q", suffix), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - // vaddbnq, vaddhnq, vaddwnq - map.insert( - format!("vadd{}nq", suffix), - BuiltinSignature { - full_name: format!("V6_vadd{}nq", suffix), - short_name: format!("vadd{}nq", suffix), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - } - - // Comparison operations with accumulation - // veqb_and, veqb_or, veqb_xor, etc. - for elem in ["b", "h", "w", "ub", "uh", "uw"] { - for op in ["and", "or", "xor"] { - // veq*_and, veq*_or, veq*_xor - map.insert( - format!("veq{}_{}", elem, op), - BuiltinSignature { - full_name: format!("V6_veq{}_{}", elem, op), - short_name: format!("veq{}_{}", elem, op), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - // vgt*_and, vgt*_or, vgt*_xor - map.insert( - format!("vgt{}_{}", elem, op), - BuiltinSignature { - full_name: format!("V6_vgt{}_{}", elem, op), - short_name: format!("vgt{}_{}", elem, op), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - } - } - - // Floating-point comparison operations (hf = half-float, sf = single-float) - for elem in ["hf", "sf"] { - // Basic comparison: vgt* - map.insert( - format!("vgt{}", elem), - BuiltinSignature { - full_name: format!("V6_vgt{}", elem), - short_name: format!("vgt{}", elem), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - - for op in ["and", "or", "xor"] { - // vgt*_and, vgt*_or, vgt*_xor - map.insert( - format!("vgt{}_{}", elem, op), - BuiltinSignature { - full_name: format!("V6_vgt{}_{}", elem, op), - short_name: format!("vgt{}_{}", elem, op), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - } - } - - // Prefix operations with predicate - for elem in ["b", "h", "w"] { - map.insert( - format!("vprefixq{}", elem), - BuiltinSignature { - full_name: format!("V6_vprefixq{}", elem), - short_name: format!("vprefixq{}", elem), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector], - }, - ); - } - - // Scatter operations with predicate - map.insert( - "vscattermhq".to_string(), - BuiltinSignature { - full_name: "V6_vscattermhq".to_string(), - short_name: "vscattermhq".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::HvxVector, - RustType::I32, - RustType::I32, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - - map.insert( - "vscattermhwq".to_string(), - BuiltinSignature { - full_name: "V6_vscattermhwq".to_string(), - short_name: "vscattermhwq".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::HvxVector, - RustType::I32, - RustType::I32, - RustType::HvxVectorPair, - RustType::HvxVector, - ], - }, - ); - - map.insert( - "vscattermwq".to_string(), - BuiltinSignature { - full_name: "V6_vscattermwq".to_string(), - short_name: "vscattermwq".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::HvxVector, - RustType::I32, - RustType::I32, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - - // Add with carry saturation - map.insert( - "vaddcarrysat".to_string(), - BuiltinSignature { - full_name: "V6_vaddcarrysat".to_string(), - short_name: "vaddcarrysat".to_string(), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - - // Gather operations with predicate - map.insert( - "vgathermhq".to_string(), - BuiltinSignature { - full_name: "V6_vgathermhq".to_string(), - short_name: "vgathermhq".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::MutPtrHvxVector, - RustType::HvxVector, - RustType::I32, - RustType::I32, - RustType::HvxVector, - ], - }, - ); - - map.insert( - "vgathermhwq".to_string(), - BuiltinSignature { - full_name: "V6_vgathermhwq".to_string(), - short_name: "vgathermhwq".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::MutPtrHvxVector, - RustType::HvxVector, - RustType::I32, - RustType::I32, - RustType::HvxVectorPair, - ], - }, - ); - - map.insert( - "vgathermwq".to_string(), - BuiltinSignature { - full_name: "V6_vgathermwq".to_string(), - short_name: "vgathermwq".to_string(), - return_type: RustType::Unit, - param_types: vec![ - RustType::MutPtrHvxVector, - RustType::HvxVector, - RustType::I32, - RustType::I32, - RustType::HvxVector, - ], - }, - ); - - // Basic comparison operations (without accumulation) - for elem in ["b", "h", "w", "ub", "uh", "uw"] { - // vgt* - greater than - map.insert( - format!("vgt{}", elem), - BuiltinSignature { - full_name: format!("V6_vgt{}", elem), - short_name: format!("vgt{}", elem), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - // veq* - equal - map.insert( - format!("veq{}", elem), - BuiltinSignature { - full_name: format!("V6_veq{}", elem), - short_name: format!("veq{}", elem), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - } - - // Conditional subtraction operations (vsub*q, vsub*nq) - for elem in ["b", "h", "w"] { - map.insert( - format!("vsub{}q", elem), - BuiltinSignature { - full_name: format!("V6_vsub{}q", elem), - short_name: format!("vsub{}q", elem), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - map.insert( - format!("vsub{}nq", elem), - BuiltinSignature { - full_name: format!("V6_vsub{}nq", elem), - short_name: format!("vsub{}nq", elem), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - } - - // vmux - vector mux (select based on predicate) - map.insert( - "vmux".to_string(), - BuiltinSignature { - full_name: "V6_vmux".to_string(), - short_name: "vmux".to_string(), - return_type: RustType::HvxVector, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - - // vswap - vector swap based on predicate - map.insert( - "vswap".to_string(), - BuiltinSignature { - full_name: "V6_vswap".to_string(), - short_name: "vswap".to_string(), - return_type: RustType::HvxVectorPair, - param_types: vec![ - RustType::HvxVector, - RustType::HvxVector, - RustType::HvxVector, - ], - }, - ); - - // shuffeq operations - take vectors (internal pred representation) and return vector - for elem in ["h", "w"] { - map.insert( - format!("shuffeq{}", elem), - BuiltinSignature { - full_name: format!("V6_shuffeq{}", elem), - short_name: format!("shuffeq{}", elem), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - } - - // Predicate AND with vector operations - map.insert( - "vandvqv".to_string(), - BuiltinSignature { - full_name: "V6_vandvqv".to_string(), - short_name: "vandvqv".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - - map.insert( - "vandvnqv".to_string(), - BuiltinSignature { - full_name: "V6_vandvnqv".to_string(), - short_name: "vandvnqv".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector], - }, - ); - - // vandnqrt and vandnqrt_acc - map.insert( - "vandnqrt".to_string(), - BuiltinSignature { - full_name: "V6_vandnqrt".to_string(), - short_name: "vandnqrt".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::I32], - }, - ); - - map.insert( - "vandnqrt_acc".to_string(), - BuiltinSignature { - full_name: "V6_vandnqrt_acc".to_string(), - short_name: "vandnqrt_acc".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::HvxVector, RustType::HvxVector, RustType::I32], - }, - ); - - // pred_scalar2v2 - map.insert( - "pred_scalar2v2".to_string(), - BuiltinSignature { - full_name: "V6_pred_scalar2v2".to_string(), - short_name: "pred_scalar2v2".to_string(), - return_type: RustType::HvxVector, - param_types: vec![RustType::I32], - }, - ); - - map -} - -/// Generate extern declarations for all intrinsics for a specific vector mode -fn generate_extern_block(intrinsics: &[IntrinsicInfo], mode: VectorMode) -> String { - let mut output = String::new(); - - // Collect unique builtins to avoid duplicates - let mut seen_builtins: HashSet = HashSet::new(); - let mut decls: Vec<(String, String, RustType, Vec)> = Vec::new(); - - // First, add simple intrinsics - for info in intrinsics.iter().filter(|i| !i.is_compound) { - if seen_builtins.contains(&info.builtin_name) { - continue; - } - seen_builtins.insert(info.builtin_name.clone()); - - let param_types: Vec = info.params.iter().map(|(_, t)| t.clone()).collect(); - decls.push(( - info.builtin_name.clone(), - info.instr_name.clone(), - info.return_type.clone(), - param_types, - )); - } - - // Then, collect all builtins used in compound expressions - let helper_sigs = get_compound_helper_signatures(); - let mut compound_builtins: HashSet = HashSet::new(); - - for info in intrinsics.iter().filter(|i| i.is_compound) { - if let Some(ref expr) = info.compound_expr { - collect_builtins_from_expr(expr, &mut compound_builtins); - } - } - - // Add compound helper builtins - let mut missing_builtins = Vec::new(); - for builtin_name in compound_builtins { - let full_name = format!("V6_{}", builtin_name); - if seen_builtins.contains(&full_name) { - continue; - } - seen_builtins.insert(full_name.clone()); - - if let Some(sig) = helper_sigs.get(&builtin_name) { - decls.push(( - sig.full_name.clone(), - sig.short_name.clone(), - sig.return_type.clone(), - sig.param_types.clone(), - )); - } else { - missing_builtins.push(builtin_name); - } - } - - // Report missing builtins (for development purposes) - if !missing_builtins.is_empty() { - eprintln!("Warning: Missing helper signatures for compound builtins:"); - for name in &missing_builtins { - eprintln!(" - {}", name); - } - } - - // Sort by builtin name for consistent output - decls.sort_by(|a, b| a.0.cmp(&b.0)); - - // Generate intrinsic declarations for the specified mode - output.push_str(&format!( - "// LLVM intrinsic declarations for {}-byte vector mode\n", - mode.bytes() - )); - output.push_str("#[allow(improper_ctypes)]\n"); - output.push_str("unsafe extern \"unadjusted\" {\n"); - - for (builtin_name, instr_name, return_type, param_types) in &decls { - let base_link = builtin_name.replace('_', "."); - // 128-byte mode uses .128B suffix, 64-byte mode doesn't - let link_name = if builtin_name.starts_with("V6_") && mode == VectorMode::V128 { - format!("llvm.hexagon.{}.128B", base_link) - } else { - format!("llvm.hexagon.{}", base_link) - }; - - let params_str = if param_types.is_empty() { - String::new() - } else { - param_types - .iter() - .map(|t| format!("_: {}", t.to_extern_str())) - .collect::>() - .join(", ") - }; - - let return_str = if *return_type == RustType::Unit { - " -> ()".to_string() - } else { - format!(" -> {}", return_type.to_extern_str()) - }; - - output.push_str(&format!( - " #[link_name = \"{}\"]\n fn {}({}){};\n", - link_name, instr_name, params_str, return_str - )); - } - - output.push_str("}\n"); - output -} - -/// Generate Rust code for a compound expression -/// `params` maps parameter names to their types in the function signature -/// Get the type of an expression -fn get_expr_type( - expr: &CompoundExpr, - params: &HashMap, - helper_sigs: &HashMap, -) -> Option { - match expr { - CompoundExpr::BuiltinCall(name, _) => { - helper_sigs.get(name).map(|sig| sig.return_type.clone()) - } - CompoundExpr::Param(name) => params.get(name).cloned(), - CompoundExpr::IntLiteral(_) => Some(RustType::I32), - } -} - -fn generate_compound_expr_code( - expr: &CompoundExpr, - params: &HashMap, - helper_sigs: &HashMap, -) -> String { - match expr { - CompoundExpr::BuiltinCall(name, args) => { - // Get the expected parameter types for this builtin - let expected_types = helper_sigs - .get(name) - .map(|sig| sig.param_types.clone()) - .unwrap_or_default(); - - let args_code: Vec = args - .iter() - .enumerate() - .map(|(i, arg)| { - let arg_code = generate_compound_expr_code(arg, params, helper_sigs); - - // Check if we need to transmute this argument - let expected_type = expected_types.get(i); - let actual_type = get_expr_type(arg, params, helper_sigs); - - // If the builtin expects HvxVector but the arg is HvxVectorPred, transmute - if expected_type == Some(&RustType::HvxVector) - && actual_type == Some(RustType::HvxVectorPred) - { - format!( - "core::mem::transmute::({})", - arg_code - ) - } else { - arg_code - } - }) - .collect(); - format!("{}({})", name, args_code.join(", ")) - } - CompoundExpr::Param(name) => name.clone(), - CompoundExpr::IntLiteral(n) => n.to_string(), - } -} - -/// Get the primary instruction name from a compound expression (innermost significant op) -fn get_compound_primary_instr(expr: &CompoundExpr) -> Option { - match expr { - CompoundExpr::BuiltinCall(name, args) => { - // For vandqrt wrapper, look inside - if name == "vandqrt" && !args.is_empty() { - if let Some(inner) = get_compound_primary_instr(&args[0]) { - return Some(inner); - } - } - // For store operations, use the store name - if name.starts_with("vS32b") { - return Some(name.clone()); - } - // For conditional accumulation, use the add name - if name.starts_with("vadd") && (name.ends_with("q") || name.ends_with("nq")) { - return Some(name.clone()); - } - // For predicate operations - if name.starts_with("pred_") { - return Some(name.clone()); - } - // For comparison operations with accumulation - if (name.starts_with("veq") || name.starts_with("vgt")) - && (name.ends_with("_and") || name.ends_with("_or") || name.ends_with("_xor")) - { - return Some(name.clone()); - } - Some(name.clone()) - } - _ => None, - } -} - -/// Get override implementations for specific compound intrinsics. -/// Some C macros rely on implicit type conversions that don't work with -/// our stricter Rust types, so we provide corrected implementations. -fn get_compound_overrides() -> HashMap<&'static str, &'static str> { - let mut map = HashMap::new(); - - // Q6_V_vand_QR: takes pred, returns vec - // Use transmute to convert pred to vec for LLVM, call vandvrt - map.insert( - "Q6_V_vand_QR", - "vandvrt(core::mem::transmute::(qu), rt)", - ); - - // Q6_V_vandor_VQR: takes vec and pred, returns vec - map.insert( - "Q6_V_vandor_VQR", - "vandvrt_acc(vx, core::mem::transmute::(qu), rt)", - ); - - // Q6_Q_vand_VR: takes vec, returns pred - map.insert( - "Q6_Q_vand_VR", - "core::mem::transmute::(vandqrt(vu, rt))", - ); - - // Q6_Q_vandor_QVR: takes pred and vec, returns pred - map.insert( - "Q6_Q_vandor_QVR", - "core::mem::transmute::(vandqrt_acc(core::mem::transmute::(qx), vu, rt))", - ); - - map -} - -/// Generate wrapper functions for all intrinsics -fn generate_functions(intrinsics: &[IntrinsicInfo]) -> String { - let mut output = String::new(); - let simd_mappings = get_simd_intrinsic_mappings(); - - // Generate simple intrinsics - for info in intrinsics.iter().filter(|i| !i.is_compound) { - let rust_name = &info.q6_name; - - // Generate doc comment - output.push_str(&format!("/// `{}`\n", info.asm_syntax)); - output.push_str("///\n"); - output.push_str(&format!("/// Instruction Type: {}\n", info.instr_type)); - output.push_str(&format!("/// Execution Slots: {}\n", info.exec_slots)); - - // Generate attributes - output.push_str("#[inline]\n"); - output.push_str(&format!( - "#[cfg_attr(target_arch = \"hexagon\", target_feature(enable = \"hvxv{}\"))]\n", - info.min_arch - )); - - // Check if we should use simd intrinsic instead - let use_simd = simd_mappings.get(info.instr_name.as_str()); - - // assert_instr uses the original instruction name - output.push_str(&format!( - "#[cfg_attr(test, assert_instr({}))]\n", - info.instr_name - )); - - output.push_str(&format!( - "#[unstable(feature = \"stdarch_hexagon\", issue = \"{}\")]\n", - TRACKING_ISSUE - )); - - // Generate function signature - let params_str = info - .params - .iter() - .map(|(name, ty)| format!("{}: {}", name, ty.to_rust_str())) - .collect::>() - .join(", "); - - let return_str = if info.return_type == RustType::Unit { - String::new() - } else { - format!(" -> {}", info.return_type.to_rust_str()) - }; - - output.push_str(&format!( - "pub unsafe fn {}({}){} {{\n", - rust_name, params_str, return_str - )); - - // Generate function body - let args_str = info - .params - .iter() - .map(|(name, _)| name.as_str()) - .collect::>() - .join(", "); - - if let Some(simd_fn) = use_simd { - // Use architecture-independent simd intrinsic - output.push_str(&format!(" {}({})\n", simd_fn, args_str)); - } else { - // Use the LLVM intrinsic - output.push_str(&format!(" {}({})\n", info.instr_name, args_str)); - } - - output.push_str("}\n\n"); - } - - // Generate compound intrinsics - let helper_sigs = get_compound_helper_signatures(); - let overrides = get_compound_overrides(); - for info in intrinsics.iter().filter(|i| i.is_compound) { - if let Some(ref compound_expr) = info.compound_expr { - let rust_name = &info.q6_name; - - // Get the primary instruction for assert_instr - let _primary_instr = get_compound_primary_instr(compound_expr) - .unwrap_or_else(|| info.instr_name.clone()); - - // Generate doc comment - output.push_str(&format!("/// `{}`\n", info.asm_syntax)); - output.push_str("///\n"); - output.push_str( - "/// This is a compound operation composed of multiple HVX instructions.\n", - ); - if !info.instr_type.is_empty() { - output.push_str(&format!("/// Instruction Type: {}\n", info.instr_type)); - } - if !info.exec_slots.is_empty() { - output.push_str(&format!("/// Execution Slots: {}\n", info.exec_slots)); - } - - // Generate attributes - output.push_str("#[inline]\n"); - output.push_str(&format!( - "#[cfg_attr(target_arch = \"hexagon\", target_feature(enable = \"hvxv{}\"))]\n", - info.min_arch - )); - - // For compound ops, we skip assert_instr since they emit multiple instructions - // output.push_str(&format!( - // "#[cfg_attr(test, assert_instr({}))]\n", - // primary_instr - // )); - - output.push_str(&format!( - "#[unstable(feature = \"stdarch_hexagon\", issue = \"{}\")]\n", - TRACKING_ISSUE - )); - - // Generate function signature - let params_str = info - .params - .iter() - .map(|(name, ty)| format!("{}: {}", name, ty.to_rust_str())) - .collect::>() - .join(", "); - - let return_str = if info.return_type == RustType::Unit { - String::new() - } else { - format!(" -> {}", info.return_type.to_rust_str()) - }; - - output.push_str(&format!( - "pub unsafe fn {}({}){} {{\n", - rust_name, params_str, return_str - )); - - // Check if we have an override for this intrinsic - let body = if let Some(override_body) = overrides.get(info.q6_name.as_str()) { - override_body.to_string() - } else { - // Build param type map for expression code generation - let param_types: HashMap = info.params.iter().cloned().collect(); - // Generate function body from compound expression - let expr_body = - generate_compound_expr_code(compound_expr, ¶m_types, &helper_sigs); - - // Check if we need to transmute the result - let expr_return_type = get_expr_type(compound_expr, ¶m_types, &helper_sigs); - if info.return_type == RustType::HvxVectorPred - && expr_return_type == Some(RustType::HvxVector) - { - format!( - "core::mem::transmute::({})", - expr_body - ) - } else { - expr_body - } - }; - output.push_str(&format!(" {}\n", body)); - - output.push_str("}\n\n"); - } - } - - output -} - -/// Generate a module file for a specific vector mode -fn generate_module_file( - intrinsics: &[IntrinsicInfo], - output_path: &Path, - mode: VectorMode, -) -> Result<(), String> { - let mut output = - File::create(output_path).map_err(|e| format!("Failed to create output: {}", e))?; - - writeln!(output, "{}", GENERATED_MARKER).map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_module_doc(mode)).map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_types(mode)).map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_extern_block(intrinsics, mode)).map_err(|e| e.to_string())?; - writeln!(output, "{}", generate_functions(intrinsics)).map_err(|e| e.to_string())?; - - // Ensure file is flushed before running rustfmt - drop(output); - - // Run rustfmt on the generated file - let status = std::process::Command::new("rustfmt") - .arg(output_path) - .status() - .map_err(|e| format!("Failed to run rustfmt: {}", e))?; - - if !status.success() { - return Err("rustfmt failed".to_string()); - } - - Ok(()) -} +use std::path::PathBuf; +use stdarch_gen_common::{run_generator, Mode}; fn main() -> Result<(), String> { - println!("=== Hexagon HVX Code Generator ===\n"); - - // Get the crate directory first (needed for both reading header and writing output) let crate_dir = std::env::var("CARGO_MANIFEST_DIR") - .map(std::path::PathBuf::from) + .map(PathBuf::from) .unwrap_or_else(|_| std::env::current_dir().unwrap()); - // Read and parse the local LLVM header - println!("Step 1: Reading LLVM HVX header..."); - let header_content = read_header(&crate_dir)?; - println!(" Read {} bytes", header_content.len()); - - println!("\nStep 2: Parsing intrinsic definitions..."); - let all_intrinsics = parse_header(&header_content); - println!(" Found {} intrinsic definitions", all_intrinsics.len()); - - // Filter out intrinsics requiring architecture versions not yet supported by rustc - let intrinsics: Vec<_> = all_intrinsics - .into_iter() - .filter(|i| i.min_arch <= MAX_SUPPORTED_ARCH) - .collect(); - let filtered_count = intrinsics.len(); - println!( - " Filtered to {} intrinsics (max supported: hvxv{})", - filtered_count, MAX_SUPPORTED_ARCH - ); - - // Count simple vs compound - let simple_count = intrinsics.iter().filter(|i| !i.is_compound).count(); - let compound_count = intrinsics.iter().filter(|i| i.is_compound).count(); - println!(" Simple intrinsics: {}", simple_count); - println!(" Compound intrinsics: {}", compound_count); - - // Print some sample intrinsics for verification - println!("\n Sample simple intrinsics:"); - for info in intrinsics.iter().filter(|i| !i.is_compound).take(5) { - println!( - " {} -> {} ({})", - info.q6_name, info.builtin_name, info.asm_syntax - ); - } - - println!("\n Sample compound intrinsics:"); - for info in intrinsics.iter().filter(|i| i.is_compound).take(5) { - println!(" {} ({})", info.q6_name, info.asm_syntax); - } - - // Count architecture versions - let mut arch_counts: HashMap = HashMap::new(); - for info in &intrinsics { - *arch_counts.entry(info.min_arch).or_insert(0) += 1; - } - println!("\n By architecture version:"); - let mut archs: Vec<_> = arch_counts.iter().collect(); - archs.sort_by_key(|(k, _)| *k); - for (arch, count) in archs { - println!(" HVX v{}: {} intrinsics", arch, count); - } - - // Generate output files let hexagon_dir = crate_dir.join("../core_arch/src/hexagon"); - // Either "check" to check the output versus the committed output, or "bless" // to update the output. let mode = Mode::from_env(); - println!("\nStep 3: Generating v64.rs and v128.rs (mode: {mode:?})..."); + run_generator(&hexagon_dir, mode, |out_dir| -> Result<(), String> { - for (filename, vmode) in [("v64.rs", VectorMode::V64), ("v128.rs", VectorMode::V128)] { - let path = out_dir.join(filename); - generate_module_file(&intrinsics, &path, vmode)?; - println!(" Output: {}", hexagon_dir.join(filename).display()); - } + // Here scalar::generate writes scalar.rs . + scalar::generate(&crate_dir, out_dir)?; + // Here hvx::generate writes v64.rs and v128.rs . + hvx::generate(&crate_dir, out_dir)?; Ok(()) }) .map_err(|e| e.to_string())?; - println!("\n=== Results ==="); - println!( - " Generated {} simple wrapper functions per module", - simple_count - ); - println!( - " Generated {} compound wrapper functions per module", - compound_count - ); - println!( - " Total: {} functions per module", - simple_count + compound_count - ); - println!(" Output files: v64.rs, v128.rs"); - Ok(()) } diff --git a/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs b/library/stdarch/crates/stdarch-gen-hexagon/src/scalar.rs similarity index 96% rename from library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs rename to library/stdarch/crates/stdarch-gen-hexagon/src/scalar.rs index 934c7e28fe374..4beee35df97ae 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon/src/scalar.rs @@ -21,6 +21,7 @@ use std::collections::HashMap; use std::fs::File; use std::io::Write; use std::path::Path; +use stdarch_gen_common::GENERATED_MARKER; /// Extract the instruction mnemonic from the assembly syntax string. /// @@ -630,6 +631,7 @@ fn generate_scalar_file(intrinsics: &[ScalarIntrinsic], output_path: &Path) -> R let mut output = File::create(output_path).map_err(|e| format!("Failed to create output: {}", e))?; + writeln!(output, "{}", GENERATED_MARKER).map_err(|e| e.to_string())?; writeln!(output, "{}", generate_module_doc()).map_err(|e| e.to_string())?; writeln!(output, "").map_err(|e| e.to_string())?; writeln!(output, "{}", generate_extern_block(intrinsics)).map_err(|e| e.to_string())?; @@ -651,28 +653,12 @@ fn generate_scalar_file(intrinsics: &[ScalarIntrinsic], output_path: &Path) -> R Ok(()) } -fn main() -> Result<(), String> { - println!("=== Hexagon Scalar Code Generator ===\n"); - - let crate_dir = std::env::var("CARGO_MANIFEST_DIR") - .map(std::path::PathBuf::from) - .unwrap_or_else(|_| std::env::current_dir().unwrap()); - - let header_content = read_header(&crate_dir)?; - println!("Read {} bytes", header_content.len()); - +/// Parse the scalar header in `crate_dir` and write `scalar.rs` into `out_dir`. +pub fn generate(crate_dir: &std::path::Path, out_dir: &std::path::Path) -> Result<(), String> { + let header_content = read_header(crate_dir)?; let intrinsics = parse_header(&header_content); - println!("Parsed {} scalar intrinsics", intrinsics.len()); - - let hexagon_dir = std::env::args() - .nth(1) - .map(std::path::PathBuf::from) - .unwrap_or_else(|| crate_dir.join("../core_arch/src/hexagon")); - std::fs::create_dir_all(&hexagon_dir).map_err(|e| e.to_string())?; - let scalar_path = hexagon_dir.join("scalar.rs"); - + std::fs::create_dir_all(out_dir).map_err(|e| e.to_string())?; + let scalar_path = out_dir.join("scalar.rs"); generate_scalar_file(&intrinsics, &scalar_path)?; - println!("Generated scalar.rs at {}", scalar_path.display()); - Ok(()) } diff --git a/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec b/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec index 41432adf2508c..d3efb6c9606a2 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec +++ b/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec @@ -1872,11 +1872,13 @@ asm-fmts = xd, xj data-types = V4DF, V4DF /// lasx_xvfrecip_s +impl = portable name = lasx_xvfrecip_s asm-fmts = xd, xj data-types = V8SF, V8SF /// lasx_xvfrecip_d +impl = portable name = lasx_xvfrecip_d asm-fmts = xd, xj data-types = V4DF, V4DF @@ -1912,11 +1914,13 @@ asm-fmts = xd, xj data-types = V4DF, V4DF /// lasx_xvfrsqrt_s +impl = portable name = lasx_xvfrsqrt_s asm-fmts = xd, xj data-types = V8SF, V8SF /// lasx_xvfrsqrt_d +impl = portable name = lasx_xvfrsqrt_d asm-fmts = xd, xj data-types = V4DF, V4DF @@ -2067,41 +2071,49 @@ asm-fmts = xd, xj data-types = V4DI, V4DI /// lasx_xvmuh_b +impl = portable name = lasx_xvmuh_b asm-fmts = xd, xj, xk data-types = V32QI, V32QI, V32QI /// lasx_xvmuh_h +impl = portable name = lasx_xvmuh_h asm-fmts = xd, xj, xk data-types = V16HI, V16HI, V16HI /// lasx_xvmuh_w +impl = portable name = lasx_xvmuh_w asm-fmts = xd, xj, xk data-types = V8SI, V8SI, V8SI /// lasx_xvmuh_d +impl = portable name = lasx_xvmuh_d asm-fmts = xd, xj, xk data-types = V4DI, V4DI, V4DI /// lasx_xvmuh_bu +impl = portable name = lasx_xvmuh_bu asm-fmts = xd, xj, xk data-types = UV32QI, UV32QI, UV32QI /// lasx_xvmuh_hu +impl = portable name = lasx_xvmuh_hu asm-fmts = xd, xj, xk data-types = UV16HI, UV16HI, UV16HI /// lasx_xvmuh_wu +impl = portable name = lasx_xvmuh_wu asm-fmts = xd, xj, xk data-types = UV8SI, UV8SI, UV8SI /// lasx_xvmuh_du +impl = portable name = lasx_xvmuh_du asm-fmts = xd, xj, xk data-types = UV4DI, UV4DI, UV4DI @@ -2560,41 +2572,49 @@ asm-fmts = xd, xj data-types = V4DF, V4DF /// lasx_xvfrintrz_s +impl = portable name = lasx_xvfrintrz_s asm-fmts = xd, xj data-types = V8SF, V8SF /// lasx_xvfrintrz_d +impl = portable name = lasx_xvfrintrz_d asm-fmts = xd, xj data-types = V4DF, V4DF /// lasx_xvfrintrp_s +impl = portable name = lasx_xvfrintrp_s asm-fmts = xd, xj data-types = V8SF, V8SF /// lasx_xvfrintrp_d +impl = portable name = lasx_xvfrintrp_d asm-fmts = xd, xj data-types = V4DF, V4DF /// lasx_xvfrintrm_s +impl = portable name = lasx_xvfrintrm_s asm-fmts = xd, xj data-types = V8SF, V8SF /// lasx_xvfrintrm_d +impl = portable name = lasx_xvfrintrm_d asm-fmts = xd, xj data-types = V4DF, V4DF /// lasx_xvld +impl = portable name = lasx_xvld asm-fmts = xd, rj, si12 data-types = V32QI, CVPOINTER, SI /// lasx_xvst +impl = portable name = lasx_xvst asm-fmts = xd, rj, si12 data-types = VOID, V32QI, CVPOINTER, SI @@ -2681,11 +2701,13 @@ asm-fmts = xd, i13 data-types = V4DI, HI /// lasx_xvldx +impl = portable name = lasx_xvldx asm-fmts = xd, rj, rk data-types = V32QI, CVPOINTER, DI /// lasx_xvstx +impl = portable name = lasx_xvstx asm-fmts = xd, rj, rk data-types = VOID, V32QI, CVPOINTER, DI @@ -3297,21 +3319,25 @@ asm-fmts = xd, xj, xk data-types = V16HI, V16HI, UV32QI, V32QI /// lasx_xvrotr_b +impl = portable name = lasx_xvrotr_b asm-fmts = xd, xj, xk data-types = V32QI, V32QI, V32QI /// lasx_xvrotr_h +impl = portable name = lasx_xvrotr_h asm-fmts = xd, xj, xk data-types = V16HI, V16HI, V16HI /// lasx_xvrotr_w +impl = portable name = lasx_xvrotr_w asm-fmts = xd, xj, xk data-types = V8SI, V8SI, V8SI /// lasx_xvrotr_d +impl = portable name = lasx_xvrotr_d asm-fmts = xd, xj, xk data-types = V4DI, V4DI, V4DI @@ -3397,21 +3423,25 @@ asm-fmts = xd, xj data-types = UV4DI, UV4DI /// lasx_xvrotri_b +impl = portable name = lasx_xvrotri_b asm-fmts = xd, xj, ui3 data-types = V32QI, V32QI, UQI /// lasx_xvrotri_h +impl = portable name = lasx_xvrotri_h asm-fmts = xd, xj, ui4 data-types = V16HI, V16HI, UQI /// lasx_xvrotri_w +impl = portable name = lasx_xvrotri_w asm-fmts = xd, xj, ui5 data-types = V8SI, V8SI, UQI /// lasx_xvrotri_d +impl = portable name = lasx_xvrotri_d asm-fmts = xd, xj, ui6 data-types = V4DI, V4DI, UQI diff --git a/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec b/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec index 211c3c0fcfe62..79dc4217da293 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec +++ b/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec @@ -1959,11 +1959,13 @@ asm-fmts = vd, vj data-types = V2DF, V2DF /// lsx_vfrecip_s +impl = portable name = lsx_vfrecip_s asm-fmts = vd, vj data-types = V4SF, V4SF /// lsx_vfrecip_d +impl = portable name = lsx_vfrecip_d asm-fmts = vd, vj data-types = V2DF, V2DF @@ -1999,11 +2001,13 @@ asm-fmts = vd, vj data-types = V2DF, V2DF /// lsx_vfrsqrt_s +impl = portable name = lsx_vfrsqrt_s asm-fmts = vd, vj data-types = V4SF, V4SF /// lsx_vfrsqrt_d +impl = portable name = lsx_vfrsqrt_d asm-fmts = vd, vj data-types = V2DF, V2DF @@ -2129,41 +2133,49 @@ asm-fmts = vd, vj data-types = V2DI, V2DI /// lsx_vmuh_b +impl = portable name = lsx_vmuh_b asm-fmts = vd, vj, vk data-types = V16QI, V16QI, V16QI /// lsx_vmuh_h +impl = portable name = lsx_vmuh_h asm-fmts = vd, vj, vk data-types = V8HI, V8HI, V8HI /// lsx_vmuh_w +impl = portable name = lsx_vmuh_w asm-fmts = vd, vj, vk data-types = V4SI, V4SI, V4SI /// lsx_vmuh_d +impl = portable name = lsx_vmuh_d asm-fmts = vd, vj, vk data-types = V2DI, V2DI, V2DI /// lsx_vmuh_bu +impl = portable name = lsx_vmuh_bu asm-fmts = vd, vj, vk data-types = UV16QI, UV16QI, UV16QI /// lsx_vmuh_hu +impl = portable name = lsx_vmuh_hu asm-fmts = vd, vj, vk data-types = UV8HI, UV8HI, UV8HI /// lsx_vmuh_wu +impl = portable name = lsx_vmuh_wu asm-fmts = vd, vj, vk data-types = UV4SI, UV4SI, UV4SI /// lsx_vmuh_du +impl = portable name = lsx_vmuh_du asm-fmts = vd, vj, vk data-types = UV2DI, UV2DI, UV2DI @@ -2623,31 +2635,37 @@ asm-fmts = vd, vj data-types = V2DF, V2DF /// lsx_vfrintrz_s +impl = portable name = lsx_vfrintrz_s asm-fmts = vd, vj data-types = V4SF, V4SF /// lsx_vfrintrz_d +impl = portable name = lsx_vfrintrz_d asm-fmts = vd, vj data-types = V2DF, V2DF /// lsx_vfrintrp_s +impl = portable name = lsx_vfrintrp_s asm-fmts = vd, vj data-types = V4SF, V4SF /// lsx_vfrintrp_d +impl = portable name = lsx_vfrintrp_d asm-fmts = vd, vj data-types = V2DF, V2DF /// lsx_vfrintrm_s +impl = portable name = lsx_vfrintrm_s asm-fmts = vd, vj data-types = V4SF, V4SF /// lsx_vfrintrm_d +impl = portable name = lsx_vfrintrm_d asm-fmts = vd, vj data-types = V2DF, V2DF @@ -3133,21 +3151,25 @@ asm-fmts = vd, vj, vk data-types = V2DI, V2DI, UV2DI, V2DI /// lsx_vrotr_b +impl = portable name = lsx_vrotr_b asm-fmts = vd, vj, vk data-types = V16QI, V16QI, V16QI /// lsx_vrotr_h +impl = portable name = lsx_vrotr_h asm-fmts = vd, vj, vk data-types = V8HI, V8HI, V8HI /// lsx_vrotr_w +impl = portable name = lsx_vrotr_w asm-fmts = vd, vj, vk data-types = V4SI, V4SI, V4SI /// lsx_vrotr_d +impl = portable name = lsx_vrotr_d asm-fmts = vd, vj, vk data-types = V2DI, V2DI, V2DI @@ -3233,21 +3255,25 @@ asm-fmts = vd, vj data-types = UV2DI, UV2DI /// lsx_vrotri_b +impl = portable name = lsx_vrotri_b asm-fmts = vd, vj, ui3 data-types = V16QI, V16QI, UQI /// lsx_vrotri_h +impl = portable name = lsx_vrotri_h asm-fmts = vd, vj, ui4 data-types = V8HI, V8HI, UQI /// lsx_vrotri_w +impl = portable name = lsx_vrotri_w asm-fmts = vd, vj, ui5 data-types = V4SI, V4SI, UQI /// lsx_vrotri_d +impl = portable name = lsx_vrotri_d asm-fmts = vd, vj, ui6 data-types = V2DI, V2DI, UQI @@ -3503,11 +3529,13 @@ asm-fmts = vd, vj, ui8 data-types = V4SI, V4SI, V4SI, USI /// lsx_vld +impl = portable name = lsx_vld asm-fmts = vd, rj, si12 data-types = V16QI, CVPOINTER, SI /// lsx_vst +impl = portable name = lsx_vst asm-fmts = vd, rj, si12 data-types = VOID, V16QI, CVPOINTER, SI @@ -3559,11 +3587,13 @@ asm-fmts = vd, vj, vk, va data-types = V16QI, V16QI, V16QI, V16QI /// lsx_vldx +impl = portable name = lsx_vldx asm-fmts = vd, rj, rk data-types = V16QI, CVPOINTER, DI /// lsx_vstx +impl = portable name = lsx_vstx asm-fmts = vd, rj, rk data-types = VOID, V16QI, CVPOINTER, DI diff --git a/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt b/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt index abbfcb3365d2b..0bd33c248bd3b 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt +++ b/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt @@ -263,6 +263,36 @@ lsx_vshuf4i_b lsx_vshuf4i_h lsx_vshuf4i_w lsx_vshuf4i_d +lsx_vld +lsx_vst +lsx_vldx +lsx_vstx +lsx_vrotr_b +lsx_vrotr_h +lsx_vrotr_w +lsx_vrotr_d +lsx_vrotri_b +lsx_vrotri_h +lsx_vrotri_w +lsx_vrotri_d +lsx_vfrecip_s +lsx_vfrecip_d +lsx_vfrsqrt_s +lsx_vfrsqrt_d +lsx_vfrintrp_s +lsx_vfrintrp_d +lsx_vfrintrm_s +lsx_vfrintrm_d +lsx_vfrintrz_s +lsx_vfrintrz_d +lsx_vmuh_b +lsx_vmuh_h +lsx_vmuh_w +lsx_vmuh_d +lsx_vmuh_bu +lsx_vmuh_hu +lsx_vmuh_wu +lsx_vmuh_du # LASX intrinsics lasx_xvsll_b @@ -527,3 +557,33 @@ lasx_xvpackod_d lasx_xvshuf4i_b lasx_xvshuf4i_h lasx_xvshuf4i_w +lasx_xvld +lasx_xvst +lasx_xvldx +lasx_xvstx +lasx_xvrotr_b +lasx_xvrotr_h +lasx_xvrotr_w +lasx_xvrotr_d +lasx_xvrotri_b +lasx_xvrotri_h +lasx_xvrotri_w +lasx_xvrotri_d +lasx_xvfrecip_s +lasx_xvfrecip_d +lasx_xvfrsqrt_s +lasx_xvfrsqrt_d +lasx_xvfrintrp_s +lasx_xvfrintrp_d +lasx_xvfrintrm_s +lasx_xvfrintrm_d +lasx_xvfrintrz_s +lasx_xvfrintrz_d +lasx_xvmuh_b +lasx_xvmuh_h +lasx_xvmuh_w +lasx_xvmuh_d +lasx_xvmuh_bu +lasx_xvmuh_hu +lasx_xvmuh_wu +lasx_xvmuh_du diff --git a/library/stdarch/crates/stdarch-test/Cargo.toml b/library/stdarch/crates/stdarch-test/Cargo.toml index e88258bfd30d0..eddd3b7f78936 100644 --- a/library/stdarch/crates/stdarch-test/Cargo.toml +++ b/library/stdarch/crates/stdarch-test/Cargo.toml @@ -8,7 +8,6 @@ edition = "2024" assert-instr-macro = { path = "../assert-instr-macro" } simd-test-macro = { path = "../simd-test-macro" } rustc-demangle = "0.1.8" -cfg-if = "1.0" [target.'cfg(windows)'.dependencies] cc = "1.0" diff --git a/library/stdarch/crates/stdarch-test/src/lib.rs b/library/stdarch/crates/stdarch-test/src/lib.rs index c468ebd12bb0d..9ef25a7957c1d 100644 --- a/library/stdarch/crates/stdarch-test/src/lib.rs +++ b/library/stdarch/crates/stdarch-test/src/lib.rs @@ -6,18 +6,16 @@ #![deny(rust_2018_idioms)] #![allow(clippy::missing_docs_in_private_items, clippy::print_stdout)] -#[macro_use] -extern crate cfg_if; - pub use assert_instr_macro::*; pub use simd_test_macro::*; use std::{cmp, collections::HashSet, env, hash, hint::black_box, str, sync::LazyLock}; -cfg_if! { - if #[cfg(target_arch = "wasm32")] { +cfg_select! { + target_arch = "wasm32" => { pub mod wasm; use wasm::disassemble_myself; - } else { + } + _ => { mod disassembly; use crate::disassembly::disassemble_myself; } diff --git a/library/stdarch/crates/stdarch-verify/tests/x86-intel.rs b/library/stdarch/crates/stdarch-verify/tests/x86-intel.rs index 32726d1bf450b..be948df541b79 100644 --- a/library/stdarch/crates/stdarch-verify/tests/x86-intel.rs +++ b/library/stdarch/crates/stdarch-verify/tests/x86-intel.rs @@ -305,7 +305,7 @@ fn verify_all_signatures() { } // FIXME: these have not been added to Intrinsics Guide yet - if ["amx-avx512", "amx-fp8", "amx-movrs", "amx-tf32", "movrs"] + if ["amx-avx512", "amx-fp8", "amx-movrs", "movrs"] .iter() .any(|f| feature.contains(f)) { diff --git a/library/unwind/src/lib.rs b/library/unwind/src/lib.rs index cb4a3593c51d6..02201f43c08ce 100644 --- a/library/unwind/src/lib.rs +++ b/library/unwind/src/lib.rs @@ -56,7 +56,14 @@ cfg_select! { } } -#[cfg(target_env = "musl")] +// `target_abi = "pauthtest"` is currently only used by the Linux/musl +// `aarch64-unknown-linux-pauthtest` target. That target only supports unwinding +// via libunwind. +#[cfg(target_abi = "pauthtest")] +#[link(name = "unwind")] +unsafe extern "C" {} + +#[cfg(all(target_env = "musl", not(target_abi = "pauthtest")))] cfg_select! { all(feature = "llvm-libunwind", feature = "system-llvm-libunwind") => { compile_error!("`llvm-libunwind` and `system-llvm-libunwind` cannot be enabled at the same time"); diff --git a/src/bootstrap/src/core/build_steps/perf.rs b/src/bootstrap/src/core/build_steps/perf.rs index 7bdcb483eb923..0ac18df2084e3 100644 --- a/src/bootstrap/src/core/build_steps/perf.rs +++ b/src/bootstrap/src/core/build_steps/perf.rs @@ -140,6 +140,18 @@ pub fn perf(builder: &Builder<'_>, args: &PerfArgs) { target: builder.config.host_target, }); + let rustc_perf_dir = builder.build.tempdir().join("rustc-perf"); + let results_dir = rustc_perf_dir.join("results"); + builder.create_dir(&results_dir); + + let mut cmd = command(collector.tool_path); + + // We need to set the working directory to `src/tools/rustc-perf`, so that it can find the directory + // with compile-time benchmarks. + cmd.current_dir(builder.src.join("src/tools/rustc-perf")); + + let db_path = results_dir.join("results.db"); + let is_profiling = match &args.cmd { PerfCommand::Eprintln { .. } | PerfCommand::Samply { .. } @@ -151,32 +163,23 @@ pub fn perf(builder: &Builder<'_>, args: &PerfArgs) { Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#); } - let compiler = builder.compiler(builder.top_stage, builder.config.host_target); - builder.std(compiler, builder.config.host_target); - - if let Some(opts) = args.cmd.shared_opts() - && opts.profiles.contains(&Profile::Doc) - { - builder.ensure(Rustdoc { target_compiler: compiler }); - } + let prepare_rustc = || { + let compiler = builder.compiler(builder.top_stage, builder.config.host_target); + builder.std(compiler, builder.config.host_target); - let sysroot = builder.ensure(Sysroot::new(compiler)); - let mut rustc = sysroot.clone(); - rustc.push("bin"); - rustc.push("rustc"); - rustc.set_extension(EXE_EXTENSION); - - let rustc_perf_dir = builder.build.tempdir().join("rustc-perf"); - let results_dir = rustc_perf_dir.join("results"); - builder.create_dir(&results_dir); - - let mut cmd = command(collector.tool_path); - - // We need to set the working directory to `src/tools/rustc-perf`, so that it can find the directory - // with compile-time benchmarks. - cmd.current_dir(builder.src.join("src/tools/rustc-perf")); + if let Some(opts) = args.cmd.shared_opts() + && opts.profiles.contains(&Profile::Doc) + { + builder.ensure(Rustdoc { target_compiler: compiler }); + } - let db_path = results_dir.join("results.db"); + let sysroot = builder.ensure(Sysroot::new(compiler)); + let mut rustc = sysroot.clone(); + rustc.push("bin"); + rustc.push("rustc"); + rustc.set_extension(EXE_EXTENSION); + rustc + }; match &args.cmd { PerfCommand::Eprintln { opts } @@ -191,7 +194,7 @@ Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#); }); cmd.arg("--out-dir").arg(&results_dir); - cmd.arg(rustc); + cmd.arg(prepare_rustc()); apply_shared_opts(&mut cmd, opts); cmd.run(builder); @@ -202,7 +205,7 @@ Consider setting `rust.debuginfo-level = 1` in `bootstrap.toml`."#); cmd.arg("bench_local"); cmd.arg("--db").arg(&db_path); cmd.arg("--id").arg(id); - cmd.arg(rustc); + cmd.arg(prepare_rustc()); apply_shared_opts(&mut cmd, opts); cmd.run(builder); diff --git a/src/ci/docker/host-x86_64/optional-x86_64-gnu-parallel-frontend/Dockerfile b/src/ci/docker/host-x86_64/optional-x86_64-gnu-parallel-frontend/Dockerfile index 8f7c7ebe35559..d8bfd6f52cb8b 100644 --- a/src/ci/docker/host-x86_64/optional-x86_64-gnu-parallel-frontend/Dockerfile +++ b/src/ci/docker/host-x86_64/optional-x86_64-gnu-parallel-frontend/Dockerfile @@ -24,13 +24,20 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ COPY scripts/sccache.sh /scripts/ RUN sh /scripts/sccache.sh +ENV PARALLEL_FRONTEND_THREADS=4 +ENV ITERATION_COUNT=2 + ENV RUST_CONFIGURE_ARGS \ --build=x86_64-unknown-linux-gnu \ --enable-sanitizers \ --enable-profiler \ --enable-compiler-docs \ - --set llvm.libzstd=true + --set llvm.libzstd=true \ + --set rust.parallel-frontend-threads=${PARALLEL_FRONTEND_THREADS} # Build the toolchain with multiple parallel frontend threads and then run tests # Tests are still compiled serially at the moment (intended to be changed in follow-ups). -ENV SCRIPT python3 ../x.py --stage 2 test --set rust.parallel-frontend-threads=4 +# Running compiletest only tests for parallel frontend, then the rest without compiletest-only +# options (i.e. `--parallel-frontend-threads=4 --iteration-count=2`) +COPY ./scripts/optional-x86_64-gnu-parallel-frontend.sh /scripts/ +ENV SCRIPT="Must specify DOCKER_SCRIPT for this image" diff --git a/src/ci/docker/scripts/optional-x86_64-gnu-parallel-frontend.sh b/src/ci/docker/scripts/optional-x86_64-gnu-parallel-frontend.sh new file mode 100755 index 0000000000000..ed9aed263f25e --- /dev/null +++ b/src/ci/docker/scripts/optional-x86_64-gnu-parallel-frontend.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +set -eux -o pipefail + +if [ ! -v RUST_TEST_THREADS ]; then + RUST_TEST_THREADS_=$(python3 -c "print(max(1, $(nproc) // ${PARALLEL_FRONTEND_THREADS}))") +else + RUST_TEST_THREADS_=${RUST_TEST_THREADS} +fi + +RUST_TEST_THREADS=${RUST_TEST_THREADS_} \ + python3 ../x.py --stage 2 test \ + tests/ui \ + -- \ + --parallel-frontend-threads="${PARALLEL_FRONTEND_THREADS}" \ + --iteration-count="${ITERATION_COUNT}" diff --git a/src/ci/docker/scripts/x86_64-gnu-llvm3.sh b/src/ci/docker/scripts/x86_64-gnu-llvm3.sh index 6ba0c738713b2..c8d67f0e80c4f 100755 --- a/src/ci/docker/scripts/x86_64-gnu-llvm3.sh +++ b/src/ci/docker/scripts/x86_64-gnu-llvm3.sh @@ -19,3 +19,7 @@ set -ex # Rebuild the stdlib with the size optimizations enabled and run tests again. RUSTFLAGS_NOT_BOOTSTRAP="--cfg feature=\"optimize_for_size\"" ../x.py --stage 1 test \ library/std library/alloc library/core + +# Rebuild the stdlib forcing the use of intrinsic fallbacks and run core tests again. +RUSTFLAGS_NOT_BOOTSTRAP="-Zforce-intrinsic-fallback" ../x.py --stage 1 test \ + library/core diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 74f1a0abdde8e..a8eebe7599fda 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -380,6 +380,8 @@ auto: - name: optional-x86_64-gnu-parallel-frontend # This test can be flaky, so do not cancel CI if it fails, for now. continue_on_error: true + env: + DOCKER_SCRIPT: optional-x86_64-gnu-parallel-frontend.sh <<: *job-linux-4c # This job ensures commits landing on nightly still pass the full diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs index 3cd34f24c6e3d..a8c27c5615007 100644 --- a/src/librustdoc/config.rs +++ b/src/librustdoc/config.rs @@ -29,33 +29,18 @@ use crate::passes::{self, Condition}; use crate::scrape_examples::{AllCallLocations, ScrapeExamplesOptions}; use crate::{html, opts, theme}; -#[derive(Clone, Copy, PartialEq, Eq, Debug, Default)] +#[derive(Clone, Copy, PartialEq, Eq, Debug)] pub(crate) enum OutputFormat { - Json, - #[default] + /// `--output-format=json` without `--show-coverage`. + /// + /// JSON description of crate API. + IrJson, + /// `--output-format=json` with `--show-coverage`. + CoverageJson, Html, Doctest, } -impl OutputFormat { - pub(crate) fn is_json(&self) -> bool { - matches!(self, OutputFormat::Json) - } -} - -impl TryFrom<&str> for OutputFormat { - type Error = String; - - fn try_from(value: &str) -> Result { - match value { - "json" => Ok(OutputFormat::Json), - "html" => Ok(OutputFormat::Html), - "doctest" => Ok(OutputFormat::Doctest), - _ => Err(format!("unknown output format `{value}`")), - } - } -} - /// Either an input crate, markdown file, or nothing (--merge=finalize). pub(crate) enum InputMode { /// The `--merge=finalize` step does not need an input crate to rustdoc. @@ -329,7 +314,8 @@ pub(crate) enum EmitType { HtmlStaticFiles, HtmlNonStaticFiles, // not explicitly nameable by the user for now - JsonFiles, + IrJsonFiles, + CoverageJsonFiles, DepInfo(Option), } @@ -338,7 +324,8 @@ impl fmt::Display for EmitType { f.write_str(match self { Self::HtmlStaticFiles => "html-static-files", Self::HtmlNonStaticFiles => "html-non-static-files", - Self::JsonFiles => "json-files", + Self::IrJsonFiles => "ir-json-files", + Self::CoverageJsonFiles => "coverage-json-files", Self::DepInfo(_) => "dep-info", }) } @@ -479,40 +466,48 @@ impl Options { let show_coverage = matches.opt_present("show-coverage"); let output_format_s = matches.opt_str("output-format"); - let output_format = match output_format_s { - Some(ref s) => match OutputFormat::try_from(s.as_str()) { - Ok(out_fmt) => out_fmt, - Err(e) => dcx.fatal(e), - }, - None => OutputFormat::default(), + let output_format = match output_format_s.as_deref() { + None | Some("html") => OutputFormat::Html, + Some("json") => { + if show_coverage { + OutputFormat::CoverageJson + } else { + OutputFormat::IrJson + } + } + Some("doctest") => OutputFormat::Doctest, + Some(other) => dcx.fatal(format!("unknown output format `{other}`")), }; - // check for `--output-format=json` + // check for `--output-format` stability, and compatibility with `--show-coverage` match ( output_format_s.as_ref().map(|_| output_format), show_coverage, nightly_options::is_unstable_enabled(matches), ) { - (None | Some(OutputFormat::Json), true, _) => {} + (None | Some(OutputFormat::CoverageJson), true, _) => {} (_, true, _) => { dcx.fatal(format!( "`--output-format={}` is not supported for the `--show-coverage` option", - output_format_s.unwrap_or_default(), + output_format_s.expect("checked for none above"), )); } // If `-Zunstable-options` is used, nothing to check after this point. (_, false, true) => {} (None | Some(OutputFormat::Html), false, _) => {} - (Some(OutputFormat::Json), false, false) => { + (Some(OutputFormat::IrJson), false, false) => { dcx.fatal( - "the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/76578)", + "the -Z unstable-options flag must be passed to enable --output-format=json for documentation generation (see https://github.com/rust-lang/rust/issues/76578)", ); } (Some(OutputFormat::Doctest), false, false) => { dcx.fatal( - "the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/134529)", + "the -Z unstable-options flag must be passed to enable --output-format=doctest (see https://github.com/rust-lang/rust/issues/134529)", ); } + (Some(OutputFormat::CoverageJson), false, _) => { + unreachable!("CoverageJson is only possible when show_coverage is true") + } } let mut emit = FxIndexMap::default(); @@ -531,18 +526,18 @@ impl Options { match typ { EmitType::DepInfo(_) => match output_format { - OutputFormat::Json | OutputFormat::Html => {} + OutputFormat::Html | OutputFormat::IrJson | OutputFormat::CoverageJson => {} OutputFormat::Doctest => unreachable!(), }, EmitType::HtmlStaticFiles | EmitType::HtmlNonStaticFiles => match output_format { OutputFormat::Html => {} - OutputFormat::Json => dcx.fatal(format!( + OutputFormat::IrJson | OutputFormat::CoverageJson => dcx.fatal(format!( "the `--emit={typ}` flag is not supported with `--output-format=json`", )), OutputFormat::Doctest => unreachable!(), }, - EmitType::JsonFiles => unreachable!(), + EmitType::IrJsonFiles | EmitType::CoverageJsonFiles => unreachable!(), } // De-duplicate emit types and the last wins. @@ -558,7 +553,8 @@ impl Options { // will have already been rejected above. if emit.is_empty() { match output_format { - OutputFormat::Json => emit.push(EmitType::JsonFiles), + OutputFormat::IrJson => emit.push(EmitType::IrJsonFiles), + OutputFormat::CoverageJson => emit.push(EmitType::CoverageJsonFiles), OutputFormat::Html => { emit.push(EmitType::HtmlStaticFiles); emit.push(EmitType::HtmlNonStaticFiles); diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index fccfa3f57e930..5bdedf7ae3d54 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -75,8 +75,6 @@ pub(crate) struct DocContext<'tcx> { pub(crate) inlined: FxHashSet, /// Used by `calculate_doc_coverage`. pub(crate) output_format: OutputFormat, - /// Used by `strip_private`. - pub(crate) show_coverage: bool, } impl<'tcx> DocContext<'tcx> { @@ -139,7 +137,7 @@ impl<'tcx> DocContext<'tcx> { /// /// If another option like `--show-coverage` is enabled, it will return `false`. pub(crate) fn is_json_output(&self) -> bool { - self.output_format.is_json() && !self.show_coverage + self.output_format == OutputFormat::IrJson } /// If `--document-private-items` was passed to rustdoc. @@ -385,7 +383,6 @@ pub(crate) fn run_global_ctxt( cache: Cache::new(render_options.document_private, render_options.document_hidden), inlined: FxHashSet::default(), output_format, - show_coverage, }; for cnum in tcx.crates(()) { diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 433e4487eb9d5..2d6865dc689b6 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -134,7 +134,7 @@ impl<'tcx> JsonRenderer<'tcx> { impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> { const DESCR: &'static str = "json"; const RUN_ON_MODULE: bool = false; - const NON_STATIC_FILE_EMIT_TYPE: EmitType = EmitType::JsonFiles; + const NON_STATIC_FILE_EMIT_TYPE: EmitType = EmitType::IrJsonFiles; type ModuleData = (); diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index a4f8e3e12e510..17f5af024ac7f 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -984,11 +984,13 @@ fn main_args(early_dcx: &mut EarlyDiagCtxt, at_args: &[String]) { }, ) }), - config::OutputFormat::Json => sess.time("render_json", || { + config::OutputFormat::IrJson => sess.time("render_json", || { run_renderer(krate, render_opts, cache, tcx, json::JsonRenderer::init) }), - // Already handled above with doctest runners. - config::OutputFormat::Doctest => unreachable!(), + // Already handled above with doctest runners or coverage early return + config::OutputFormat::Doctest | config::OutputFormat::CoverageJson => { + unreachable!() + } } }) }) diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs index 200a5a71b453a..adf98afc46cc0 100644 --- a/src/librustdoc/passes/calculate_doc_coverage.rs +++ b/src/librustdoc/passes/calculate_doc_coverage.rs @@ -11,6 +11,7 @@ use serde::Serialize; use tracing::debug; use crate::clean; +use crate::config::OutputFormat; use crate::core::DocContext; use crate::html::markdown::{ErrorCodes, find_testable_code}; use crate::passes::Pass; @@ -131,8 +132,7 @@ impl CoverageCalculator<'_, '_> { fn print_results(&self) { let output_format = self.ctx.output_format; - // In this case we want to ensure that the `OutputFormat` is JSON and NOT the `DocContext`. - if output_format.is_json() { + if output_format == OutputFormat::CoverageJson { println!("{}", self.to_json()); return; } diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs index 7400891f75b58..7b47141c9fb8a 100644 --- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs +++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs @@ -537,6 +537,7 @@ fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) { | TyKind::Pat(..) | TyKind::FieldOf(..) | TyKind::View(..) + | TyKind::DirectConstArg(..) // unused | TyKind::CVarArgs diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs index f194103ae2e88..cb0db1ed014fc 100644 --- a/src/tools/clippy/clippy_utils/src/sugg.rs +++ b/src/tools/clippy/clippy_utils/src/sugg.rs @@ -248,6 +248,7 @@ impl<'a> Sugg<'a> { | ast::ExprKind::Array(..) | ast::ExprKind::While(..) | ast::ExprKind::Await(..) + | ast::ExprKind::DirectConstArg(..) | ast::ExprKind::Err(_) | ast::ExprKind::Dummy | ast::ExprKind::UnsafeBinderCast(..) => Sugg::NonParen(snippet(expr.span)), diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 5cd64d6a4a995..e4541eb2d6061 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -525,7 +525,7 @@ fn is_uninit_value_valid_for_layout<'tcx>(cx: &LateContext<'tcx>, layout: TyAndL match layout.layout.backend_repr { BackendRepr::Scalar(s) => s.is_uninit_valid(), - BackendRepr::ScalarPair(a, b) => a.is_uninit_valid() && b.is_uninit_valid(), + BackendRepr::ScalarPair { a, b, b_offset: _ } => a.is_uninit_valid() && b.is_uninit_valid(), BackendRepr::SimdVector { element, count } => count == 0 || element.is_uninit_valid(), BackendRepr::SimdScalableVector { element, .. } => element.is_uninit_valid(), // Here validity is determined by the structural fields instead. diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index 5014541faeb01..9cca7c30817f9 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -313,7 +313,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { Immediate::ScalarPair(sc_first, sc_second) => { // The first scalar has an offset of zero; compute the offset of the 2nd. let ofs_second = { - let rustc_abi::BackendRepr::ScalarPair(a, b) = imm.layout.backend_repr + let rustc_abi::BackendRepr::ScalarPair { a: _, b: _, b_offset } = + imm.layout.backend_repr else { span_bug!( this.cur_span(), @@ -321,7 +322,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { imm.layout ) }; - a.size(this).align_to(b.default_align(this).abi).bytes_usize() + b_offset.bytes_usize() }; write_scalar(this, sc_first, 0)?; diff --git a/src/tools/miri/tests/fail/alloc/global_system_mixup.stderr b/src/tools/miri/tests/fail/alloc/global_system_mixup.stderr index 1e9d859b5cf81..1853ecbf43c70 100644 --- a/src/tools/miri/tests/fail/alloc/global_system_mixup.stderr +++ b/src/tools/miri/tests/fail/alloc/global_system_mixup.stderr @@ -1,13 +1,13 @@ error: Undefined Behavior: deallocating ALLOC, which is Rust heap memory, using PLATFORM heap deallocation operation --> RUSTLIB/std/src/sys/alloc/PLATFORM.rs:LL:CC | -LL | FREE(); +LL | FREE(); | ^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information = note: stack backtrace: - 0: std::sys::alloc::PLATFORM::::dealloc + 0: std::sys::alloc::PLATFORM::dealloc at RUSTLIB/std/src/sys/alloc/PLATFORM.rs:LL:CC 1: ::deallocate at RUSTLIB/std/src/alloc.rs:LL:CC diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs index 8a3674bff1ca6..cc7fdaefc8fee 100644 --- a/src/tools/rustfmt/src/expr.rs +++ b/src/tools/rustfmt/src/expr.rs @@ -486,7 +486,8 @@ pub(crate) fn format_expr( | ast::ExprKind::Type(..) | ast::ExprKind::IncludedBytes(..) | ast::ExprKind::OffsetOf(..) - | ast::ExprKind::UnsafeBinderCast(..) => { + | ast::ExprKind::UnsafeBinderCast(..) + | ast::ExprKind::DirectConstArg(..) => { // These don't normally occur in the AST because macros aren't expanded. However, // rustfmt tries to parse macro arguments when formatting macros, so it's not totally // impossible for rustfmt to come across one of these nodes when formatting a file. diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs index 9e7bbcc243ccf..d3a1279cfb325 100644 --- a/src/tools/rustfmt/src/types.rs +++ b/src/tools/rustfmt/src/types.rs @@ -1014,7 +1014,6 @@ impl Rewrite for ast::Ty { }) } ast::TyKind::CVarArgs => Ok("...".to_owned()), - ast::TyKind::Dummy | ast::TyKind::Err(_) => Ok(context.snippet(self.span).to_owned()), ast::TyKind::FieldOf(ref ty, ref variant, ref field) => { let ty = ty.rewrite_result(context, shape)?; if let Some(variant) = variant { @@ -1049,14 +1048,14 @@ impl Rewrite for ast::Ty { result.push_str(&rewrite); Ok(result) } - - ast::TyKind::Pat(..) | ast::TyKind::View(..) => { + ast::TyKind::Pat(..) | ast::TyKind::View(..) | ast::TyKind::DirectConstArg(..) => { // These don't normally occur in the AST because macros aren't expanded. However, // rustfmt tries to parse macro arguments when formatting macros, so it's not // totally impossible for rustfmt to come across these nodes when formatting a file. // Also, rustfmt might get passed the output from `-Zunpretty=expanded`. Err(RewriteError::Unknown) } + ast::TyKind::Dummy | ast::TyKind::Err(_) => Ok(context.snippet(self.span).to_owned()), } } } diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index 1fcc59f2a1ea4..3e06f3899d1b3 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -532,9 +532,8 @@ pub(crate) fn is_block_expr(context: &RewriteContext<'_>, expr: &ast::Expr, repr | ast::ExprKind::Index(_, ref expr, _) | ast::ExprKind::Unary(_, ref expr) | ast::ExprKind::Try(ref expr) - | ast::ExprKind::Yield(YieldKind::Prefix(Some(ref expr))) => { - is_block_expr(context, expr, repr) - } + | ast::ExprKind::Yield(YieldKind::Prefix(Some(ref expr))) + | ast::ExprKind::DirectConstArg(ref expr) => is_block_expr(context, expr, repr), ast::ExprKind::Closure(ref closure) => is_block_expr(context, &closure.body, repr), // This can only be a string lit ast::ExprKind::Lit(_) => { diff --git a/src/tools/rustfmt/tests/source/direct_const_arg.rs b/src/tools/rustfmt/tests/source/direct_const_arg.rs new file mode 100644 index 0000000000000..f6f9c25d9a0d8 --- /dev/null +++ b/src/tools/rustfmt/tests/source/direct_const_arg.rs @@ -0,0 +1,14 @@ +// direct_const_arg! is a built-in macro relevant to min_generic_const_args; its contents should be +// formatted as if its contents were passed through unchanged (the macro changes the semantics of +// the contained expression, the syntax is unchanged) + +#![feature(min_generic_const_args)] + +trait Trait { + type const TYPE_CONST: usize; +} + +struct S; + +fn parsed_as_expr_kind(_: S<{ core::direct_const_arg!( T :: TYPE_CONST ) }>) {} +fn parsed_as_ty_kind(_: S< core::direct_const_arg!( T :: TYPE_CONST ) >) {} diff --git a/src/tools/rustfmt/tests/target/direct_const_arg.rs b/src/tools/rustfmt/tests/target/direct_const_arg.rs new file mode 100644 index 0000000000000..1ebadae00ff05 --- /dev/null +++ b/src/tools/rustfmt/tests/target/direct_const_arg.rs @@ -0,0 +1,14 @@ +// direct_const_arg! is a built-in macro relevant to min_generic_const_args; its contents should be +// formatted as if its contents were passed through unchanged (the macro changes the semantics of +// the contained expression, the syntax is unchanged) + +#![feature(min_generic_const_args)] + +trait Trait { + type const TYPE_CONST: usize; +} + +struct S; + +fn parsed_as_expr_kind(_: S<{ core::direct_const_arg!(T::TYPE_CONST) }>) {} +fn parsed_as_ty_kind(_: S) {} diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml index ef6c6f0f586ae..a07c2029f4b38 100644 --- a/src/tools/wasm-component-ld/Cargo.toml +++ b/src/tools/wasm-component-ld/Cargo.toml @@ -10,4 +10,4 @@ name = "wasm-component-ld" path = "src/main.rs" [dependencies] -wasm-component-ld = "0.5.25" +wasm-component-ld = "0.5.26" diff --git a/tests/codegen-llvm/aggregate-padding-zero.rs b/tests/codegen-llvm/aggregate-padding-zero.rs new file mode 100644 index 0000000000000..9fe4b201198b7 --- /dev/null +++ b/tests/codegen-llvm/aggregate-padding-zero.rs @@ -0,0 +1,213 @@ +//@ add-minicore +//@ compile-flags: -Copt-level=3 -Cno-prepopulate-passes -Z merge-functions=disabled -Z randomize-layout=no +//@ revisions: powerpc64 x86_64 +//@[powerpc64] compile-flags: --target powerpc64-unknown-linux-gnu +//@[powerpc64] needs-llvm-components: powerpc +//@[x86_64] compile-flags: --target x86_64-unknown-linux-gnu +//@[x86_64] needs-llvm-components: x86 + +// Regression test for . +// +// These cases specifically exercise direct codegen of small non-zero constant +// aggregates as a single integer store. They are chosen so they fail without +// `try_codegen_const_aggregate_as_immediate`. + +#![crate_type = "lib"] +#![feature(no_core, lang_items)] +#![no_core] + +extern crate minicore; + +use minicore::*; + +#[inline(always)] +unsafe fn ptr_write(dest: *mut T, value: T) { + *dest = value; +} + +trait MaybeUninitExt { + fn as_mut_ptr(&mut self) -> *mut T; + fn write(&mut self, value: T); +} + +impl MaybeUninitExt for MaybeUninit { + fn as_mut_ptr(&mut self) -> *mut T { + self as *mut _ as *mut T + } + + fn write(&mut self, value: T) { + unsafe { + ptr_write(self.as_mut_ptr(), value); + } + } +} + +// Inner padding between b (offset 2, size 1) and c (offset 4, size 4). +#[repr(C)] +pub struct InnerPadded { + a: u16, + b: u8, + c: u32, +} + +#[repr(transparent)] +pub struct Nested1(InnerPadded); + +#[repr(transparent)] +pub struct Nested2(Nested1); + +// PR 157690's original ptr::write entry point, checked against the current +// aggregate-immediate codegen shape. +// CHECK-LABEL: @via_ptr_write( +#[no_mangle] +pub fn via_ptr_write(dest: &mut MaybeUninit) { + let val = InnerPadded { a: 0, b: 0, c: 0 }; + // CHECK: %val = alloca [8 x i8], align 4 + // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr %val) + // CHECK-NEXT: store i64 0, ptr %val, align 4 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %dest, ptr align 4 %val, i64 8, i1 false) + unsafe { + ptr_write(dest.as_mut_ptr(), val); + } +} + +// PR 157690's original MaybeUninit::write entry point. +// CHECK-LABEL: @via_maybe_uninit_write( +#[no_mangle] +pub fn via_maybe_uninit_write(dest: &mut MaybeUninit) { + let val = InnerPadded { a: 0, b: 0, c: 0 }; + // CHECK: %val = alloca [8 x i8], align 4 + // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr %val) + // CHECK-NEXT: store i64 0, ptr %val, align 4 + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %dest, ptr align 4 %{{.*}}, i64 8, i1 false) + dest.write(val); +} + +// Constant non-zero initialization: emitted as a single store including zero padding. +// CHECK-LABEL: @const_init_non_zero( +#[no_mangle] +pub fn const_init_non_zero(dest: *mut InnerPadded) { + let val = InnerPadded { a: 0, b: 1, c: 0 }; + // CHECK: %val = alloca [8 x i8], align 4 + // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr %val) + // x86_64-NEXT: store i64 65536, ptr %val, align 4 + // powerpc64-NEXT: store i64 1099511627776, ptr %val, align 4 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %dest, ptr align 4 %val, i64 8, i1 false) + unsafe { + ptr_write(dest, val); + } +} + +// From issue #157373: nesting wrapper structs used to change the lowering +// shape enough that LLVM would sometimes find the wide store only in the +// nested case. +// CHECK-LABEL: @bad( +#[no_mangle] +pub fn bad(a: &mut InnerPadded) { + let x = InnerPadded { a: 0, b: 1, c: 0 }; + // x86_64: store i64 65536, ptr %x, align 4 + // powerpc64: store i64 1099511627776, ptr %x, align 4 + *a = x; +} + +// CHECK-LABEL: @good( +#[no_mangle] +pub fn good(a: &mut Nested2) { + let x = InnerPadded { a: 0, b: 1, c: 0 }; + // x86_64: store i64 65536, ptr %x, align 4 + // powerpc64: store i64 1099511627776, ptr %x, align 4 + *a = Nested2(Nested1(x)); +} + +// The same direct constant aggregate packing should apply through ptr::write on MaybeUninit. +// CHECK-LABEL: @via_ptr_write_non_zero( +#[no_mangle] +pub fn via_ptr_write_non_zero(dest: &mut MaybeUninit) { + let val = InnerPadded { a: 0, b: 1, c: 0 }; + // CHECK: %val = alloca [8 x i8], align 4 + // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr %val) + // x86_64-NEXT: store i64 65536, ptr %val, align 4 + // powerpc64-NEXT: store i64 1099511627776, ptr %val, align 4 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %dest, ptr align 4 %val, i64 8, i1 false) + unsafe { + ptr_write(dest.as_mut_ptr(), val); + } +} + +// The same direct constant aggregate packing should apply through MaybeUninit::write. +// CHECK-LABEL: @via_maybe_uninit_write_non_zero( +#[no_mangle] +pub fn via_maybe_uninit_write_non_zero(dest: &mut MaybeUninit) { + let val = InnerPadded { a: 0, b: 1, c: 0 }; + // CHECK: %val = alloca [8 x i8], align 4 + // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr %val) + // x86_64-NEXT: store i64 65536, ptr %val, align 4 + // powerpc64-NEXT: store i64 1099511627776, ptr %val, align 4 + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %dest, ptr align 4 %{{.*}}, i64 8, i1 false) + dest.write(val); +} + +// CHECK-LABEL: @bad_non_zero( +#[no_mangle] +pub fn bad_non_zero(a: &mut InnerPadded) { + let x = InnerPadded { a: 0, b: 1, c: 0 }; + // CHECK: %x = alloca [8 x i8], align 4 + // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr %x) + // x86_64-NEXT: store i64 65536, ptr %x, align 4 + // powerpc64-NEXT: store i64 1099511627776, ptr %x, align 4 + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a, ptr align 4 %x, i64 8, i1 false) + *a = x; +} + +// CHECK-LABEL: @good_non_zero( +#[no_mangle] +pub fn good_non_zero(a: &mut Nested2) { + let x = InnerPadded { a: 0, b: 1, c: 0 }; + // CHECK: %x = alloca [8 x i8], align 4 + // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr %x) + // x86_64-NEXT: store i64 65536, ptr %x, align 4 + // powerpc64-NEXT: store i64 1099511627776, ptr %x, align 4 + // CHECK: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %a, ptr align 4 %{{.*}}, i64 8, i1 false) + *a = Nested2(Nested1(x)); +} + +// Trailing padding only (no inter-field padding): a (offset 0, size 4), +// b (offset 4, size 2), c (offset 6, size 1), trailing pad (offset 7, size 1). +#[repr(C)] +pub struct TailOnly { + a: u32, + b: u16, + c: u8, +} + +type TupleTailOnly = (u32, u16, u8); + +// PR 157690's trailing-padding-only entry point. +// CHECK-LABEL: @tail_only_write( +#[no_mangle] +pub fn tail_only_write(dest: &mut MaybeUninit) { + let val = TailOnly { a: 0, b: 0, c: 0 }; + // CHECK: %val = alloca [8 x i8], align 4 + // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr %val) + // CHECK-NEXT: store i64 0, ptr %val, align 4 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %dest, ptr align 4 %val, i64 8, i1 false) + unsafe { + ptr_write(dest.as_mut_ptr(), val); + } +} + +// Tuple aggregates should use the same const-packing path as structs when the +// whole tuple is constant and small enough to fit in an integer store. +// CHECK-LABEL: @tuple_tail_only_non_zero( +#[no_mangle] +pub fn tuple_tail_only_non_zero(dest: *mut TupleTailOnly) { + let val: TupleTailOnly = (0, 1, 0); + // CHECK: %val = alloca [8 x i8], align 4 + // CHECK-NEXT: call void @llvm.lifetime.start.p0({{(i64 8, )?}}ptr %val) + // x86_64-NEXT: store i64 4294967296, ptr %val, align 4 + // powerpc64-NEXT: store i64 65536, ptr %val, align 4 + // CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 %dest, ptr align 4 %val, i64 8, i1 false) + unsafe { + ptr_write(dest, val); + } +} diff --git a/tests/mir-opt/coroutine/async_drop.array-{closure#0}.ElaborateDrops.diff b/tests/mir-opt/coroutine/async_drop.array-{closure#0}.ElaborateDrops.diff new file mode 100644 index 0000000000000..bad684407ae57 --- /dev/null +++ b/tests/mir-opt/coroutine/async_drop.array-{closure#0}.ElaborateDrops.diff @@ -0,0 +1,220 @@ +- // MIR for `array::{closure#0}` before ElaborateDrops ++ // MIR for `array::{closure#0}` after ElaborateDrops + + fn array::{closure#0}(_1: {async fn body of array()}, _2: std::future::ResumeTy) -> () + yields () + { + debug _task_context => _2; + let mut _0: (); + let _3: [AsyncInt; 2]; + let mut _4: AsyncInt; + let mut _5: AsyncInt; ++ let mut _6: impl std::future::Future; ++ let mut _7: std::future::ResumeTy; ++ let mut _8: std::task::Poll<()>; ++ let mut _9: isize; ++ let mut _10: std::pin::Pin<&mut impl std::future::Future>; ++ let mut _11: &mut std::task::Context<'_>; ++ let mut _12: std::future::ResumeTy; ++ let mut _13: &mut impl std::future::Future; ++ let mut _14: std::future::ResumeTy; ++ let mut _15: std::task::Poll<()>; ++ let mut _16: isize; ++ let mut _17: std::pin::Pin<&mut impl std::future::Future>; ++ let mut _18: &mut std::task::Context<'_>; ++ let mut _19: std::future::ResumeTy; ++ let mut _20: &mut impl std::future::Future; ++ let mut _21: std::pin::Pin<&mut [AsyncInt; 2]>; ++ let mut _22: &mut [AsyncInt; 2]; + scope 1 { + debug array => _3; + } + + bb0: { + StorageLive(_3); + StorageLive(_4); + _4 = AsyncInt(const 1_i32); + StorageLive(_5); + _5 = AsyncInt(const 2_i32); + _3 = [move _4, move _5]; +- drop(_5) -> [return: bb1, unwind: bb9, drop: bb5]; ++ goto -> bb1; + } + + bb1: { + StorageDead(_5); +- drop(_4) -> [return: bb2, unwind: bb10, drop: bb6]; ++ goto -> bb2; + } + + bb2: { + StorageDead(_4); + _0 = const (); +- drop(_3) -> [return: bb3, unwind: bb11, drop: bb7]; ++ goto -> bb34; + } + + bb3: { + StorageDead(_3); +- drop(_1) -> [return: bb4, drop: bb8, unwind continue]; ++ drop(_1) -> [return: bb4, unwind: bb12]; + } + + bb4: { + return; + } + + bb5: { + StorageDead(_5); +- drop(_4) -> [return: bb6, unwind: bb13]; ++ goto -> bb6; + } + + bb6: { + StorageDead(_4); + goto -> bb7; + } + + bb7: { + StorageDead(_3); +- drop(_1) -> [return: bb8, unwind continue]; ++ goto -> bb8; + } + + bb8: { + coroutine_drop; + } + + bb9 (cleanup): { + StorageDead(_5); +- drop(_4) -> [return: bb10, unwind terminate(cleanup)]; ++ goto -> bb10; + } + + bb10 (cleanup): { + StorageDead(_4); + goto -> bb11; + } + + bb11 (cleanup): { + StorageDead(_3); + drop(_1) -> [return: bb12, unwind terminate(cleanup)]; + } + + bb12 (cleanup): { + resume; + } + + bb13 (cleanup): { + StorageDead(_4); + StorageDead(_3); +- drop(_1) -> [return: bb12, unwind terminate(cleanup)]; ++ goto -> bb12; ++ } ++ ++ bb14: { ++ StorageDead(_6); ++ goto -> bb3; ++ } ++ ++ bb15: { ++ StorageDead(_6); ++ goto -> bb7; ++ } ++ ++ bb16 (cleanup): { ++ StorageDead(_6); ++ goto -> bb11; ++ } ++ ++ bb17: { ++ assert(const false, "`async fn` resumed after async drop") -> [success: bb17, unwind: bb16]; ++ } ++ ++ bb18: { ++ _2 = move _7; ++ StorageDead(_7); ++ goto -> bb17; ++ } ++ ++ bb19: { ++ _2 = move _7; ++ StorageDead(_7); ++ goto -> bb25; ++ } ++ ++ bb20: { ++ StorageLive(_7); ++ _7 = yield(const ()) -> [resume: bb18, drop: bb19]; ++ } ++ ++ bb21: { ++ unreachable; ++ } ++ ++ bb22: { ++ _9 = discriminant(_8); ++ switchInt(move _9) -> [0: bb15, 1: bb20, otherwise: bb21]; ++ } ++ ++ bb23: { ++ _8 = as Future>::poll(move _10, move _11) -> [return: bb22, unwind: bb16]; ++ } ++ ++ bb24: { ++ _12 = move _2; ++ _11 = std::future::get_context::<'_, '_>(move _12) -> [return: bb23, unwind: bb16]; ++ } ++ ++ bb25: { ++ _13 = &mut _6; ++ _10 = Pin::<&mut impl Future>::new_unchecked(move _13) -> [return: bb24, unwind: bb16]; ++ } ++ ++ bb26: { ++ _2 = move _14; ++ StorageDead(_14); ++ goto -> bb32; ++ } ++ ++ bb27: { ++ _2 = move _14; ++ StorageDead(_14); ++ goto -> bb25; ++ } ++ ++ bb28: { ++ StorageLive(_14); ++ _14 = yield(const ()) -> [resume: bb26, drop: bb27]; ++ } ++ ++ bb29: { ++ _16 = discriminant(_15); ++ switchInt(move _16) -> [0: bb14, 1: bb28, otherwise: bb21]; ++ } ++ ++ bb30: { ++ _15 = as Future>::poll(move _17, move _18) -> [return: bb29, unwind: bb16]; ++ } ++ ++ bb31: { ++ _19 = move _2; ++ _18 = std::future::get_context::<'_, '_>(move _19) -> [return: bb30, unwind: bb16]; ++ } ++ ++ bb32: { ++ _20 = &mut _6; ++ _17 = Pin::<&mut impl Future>::new_unchecked(move _20) -> [return: bb31, unwind: bb16]; ++ } ++ ++ bb33: { ++ StorageLive(_6); ++ _6 = async_drop_in_place::<[AsyncInt; 2]>(copy (_21.0: &mut [AsyncInt; 2])) -> [return: bb32, unwind: bb16]; ++ } ++ ++ bb34: { ++ _22 = &mut _3; ++ _21 = Pin::<&mut [AsyncInt; 2]>::new_unchecked(move _22) -> [return: bb33, unwind: bb11]; + } + } + diff --git a/tests/mir-opt/coroutine/async_drop.array-{closure#0}.StateTransform.diff b/tests/mir-opt/coroutine/async_drop.array-{closure#0}.StateTransform.diff new file mode 100644 index 0000000000000..5b4e617251396 --- /dev/null +++ b/tests/mir-opt/coroutine/async_drop.array-{closure#0}.StateTransform.diff @@ -0,0 +1,273 @@ +- // MIR for `array::{closure#0}` before StateTransform ++ // MIR for `array::{closure#0}` after StateTransform + +- fn array::{closure#0}(_1: {async fn body of array()}, _2: std::future::ResumeTy) -> () +- yields () +- { +- debug _task_context => _2; +- let mut _0: (); ++ fn array::{closure#0}(_1: Pin<&mut {async fn body of array()}>, _2: &mut Context<'_>) -> Poll<()> { ++ coroutine layout { ++ field _s0: (); ++ field _s1: [AsyncInt; 2]; ++ field _s2: impl Future; ++ variant_fields = { ++ Unresumed(0): [], ++ Returned (1): [], ++ Panicked (2): [], ++ Suspend0 (3): [_s1, _s2], ++ Suspend1 (4): [_s0, _s1, _s2], ++ } ++ storage_conflicts = BitMatrix(3x3) {(_s0, _s0), (_s0, _s1), (_s0, _s2), (_s1, _s0), (_s1, _s1), (_s1, _s2), (_s2, _s0), (_s2, _s1), (_s2, _s2)} ++ } ++ debug _task_context => _26; ++ coroutine debug array => _s1; ++ let mut _0: std::task::Poll<()>; + let _3: [AsyncInt; 2]; + let mut _4: AsyncInt; + let mut _5: AsyncInt; + let mut _6: impl std::future::Future; + let mut _7: std::future::ResumeTy; + let mut _8: std::task::Poll<()>; + let mut _9: isize; + let mut _10: std::pin::Pin<&mut impl std::future::Future>; + let mut _11: &mut std::task::Context<'_>; + let mut _12: std::future::ResumeTy; + let mut _13: &mut impl std::future::Future; + let mut _14: std::future::ResumeTy; + let mut _15: std::task::Poll<()>; + let mut _16: isize; + let mut _17: std::pin::Pin<&mut impl std::future::Future>; + let mut _18: &mut std::task::Context<'_>; + let mut _19: std::future::ResumeTy; + let mut _20: &mut impl std::future::Future; + let mut _21: std::pin::Pin<&mut [AsyncInt; 2]>; + let mut _22: &mut [AsyncInt; 2]; ++ let mut _23: (); ++ let mut _24: u32; ++ let mut _25: &mut {async fn body of array()}; ++ let mut _26: std::future::ResumeTy; ++ let mut _27: std::ptr::NonNull>; + scope 1 { +- debug array => _3; ++ debug array => (((*_25) as variant#4).1: [AsyncInt; 2]); + } + + bb0: { +- StorageLive(_3); +- StorageLive(_4); +- _4 = AsyncInt(const 1_i32); +- StorageLive(_5); +- _5 = AsyncInt(const 2_i32); +- _3 = [move _4, move _5]; +- goto -> bb1; ++ _27 = move _2 as std::ptr::NonNull> (Transmute); ++ _26 = std::future::ResumeTy(move _27); ++ _25 = copy (_1.0: &mut {async fn body of array()}); ++ _24 = discriminant((*_25)); ++ switchInt(move _24) -> [0: bb26, 1: bb25, 2: bb24, 3: bb22, 4: bb23, otherwise: bb11]; + } + + bb1: { + StorageDead(_5); + goto -> bb2; + } + + bb2: { + StorageDead(_4); +- _0 = const (); +- goto -> bb29; ++ (((*_25) as variant#4).0: ()) = const (); ++ goto -> bb19; + } + + bb3: { +- StorageDead(_3); +- drop(_1) -> [return: bb4, unwind: bb8]; ++ nop; ++ goto -> bb20; + } + + bb4: { ++ _0 = Poll::<()>::Ready(move (((*_25) as variant#4).0: ())); ++ discriminant((*_25)) = 1; + return; + } + +- bb5: { +- StorageDead(_3); ++ bb5 (cleanup): { ++ nop; + goto -> bb6; + } + +- bb6: { +- coroutine_drop; ++ bb6 (cleanup): { ++ goto -> bb21; + } + +- bb7 (cleanup): { +- StorageDead(_3); +- drop(_1) -> [return: bb8, unwind terminate(cleanup)]; ++ bb7: { ++ nop; ++ goto -> bb3; + } + + bb8 (cleanup): { +- resume; ++ nop; ++ goto -> bb5; + } + + bb9: { +- StorageDead(_6); +- goto -> bb3; ++ assert(const false, "`async fn` resumed after async drop") -> [success: bb9, unwind: bb8]; + } + + bb10: { +- StorageDead(_6); +- goto -> bb5; ++ _26 = move _7; ++ StorageDead(_7); ++ goto -> bb9; + } + +- bb11 (cleanup): { +- StorageDead(_6); +- goto -> bb7; ++ bb11: { ++ unreachable; + } + + bb12: { +- assert(const false, "`async fn` resumed after async drop") -> [success: bb12, unwind: bb11]; ++ _26 = move _14; ++ StorageDead(_14); ++ goto -> bb17; + } + + bb13: { +- _2 = move _7; +- StorageDead(_7); +- goto -> bb12; ++ StorageLive(_14); ++ _0 = Poll::<()>::Pending; ++ StorageDead(_14); ++ discriminant((*_25)) = 4; ++ return; + } + + bb14: { +- _2 = move _7; +- StorageDead(_7); +- goto -> bb20; ++ _16 = discriminant(_15); ++ switchInt(move _16) -> [0: bb7, 1: bb13, otherwise: bb11]; + } + + bb15: { +- StorageLive(_7); +- _7 = yield(const ()) -> [resume: bb13, drop: bb14]; ++ _15 = as Future>::poll(move _17, move _18) -> [return: bb14, unwind: bb8]; + } + + bb16: { +- unreachable; ++ _19 = move _26; ++ _18 = copy (_19.0: std::ptr::NonNull>) as &mut std::task::Context<'_> (Transmute); ++ goto -> bb15; + } + + bb17: { +- _9 = discriminant(_8); +- switchInt(move _9) -> [0: bb10, 1: bb15, otherwise: bb16]; ++ _20 = &mut (((*_25) as variant#4).2: impl std::future::Future); ++ _17 = Pin::<&mut impl Future>::new_unchecked(move _20) -> [return: bb16, unwind: bb8]; + } + + bb18: { +- _8 = as Future>::poll(move _10, move _11) -> [return: bb17, unwind: bb11]; ++ nop; ++ (((*_25) as variant#4).2: impl std::future::Future) = async_drop_in_place::<[AsyncInt; 2]>(copy (_21.0: &mut [AsyncInt; 2])) -> [return: bb17, unwind: bb8]; + } + + bb19: { +- _12 = move _2; +- _11 = std::future::get_context::<'_, '_>(move _12) -> [return: bb18, unwind: bb11]; ++ _22 = &mut (((*_25) as variant#4).1: [AsyncInt; 2]); ++ _21 = Pin::<&mut [AsyncInt; 2]>::new_unchecked(move _22) -> [return: bb18, unwind: bb5]; + } + + bb20: { +- _13 = &mut _6; +- _10 = Pin::<&mut impl Future>::new_unchecked(move _13) -> [return: bb19, unwind: bb11]; ++ goto -> bb4; + } + +- bb21: { +- _2 = move _14; +- StorageDead(_14); +- goto -> bb27; ++ bb21 (cleanup): { ++ discriminant((*_25)) = 2; ++ resume; + } + + bb22: { +- _2 = move _14; +- StorageDead(_14); +- goto -> bb20; ++ StorageLive(_7); ++ _7 = move _26; ++ goto -> bb10; + } + + bb23: { + StorageLive(_14); +- _14 = yield(const ()) -> [resume: bb21, drop: bb22]; ++ _14 = move _26; ++ goto -> bb12; + } + + bb24: { +- _16 = discriminant(_15); +- switchInt(move _16) -> [0: bb9, 1: bb23, otherwise: bb16]; ++ assert(const false, "`async fn` resumed after panicking") -> [success: bb24, unwind continue]; + } + + bb25: { +- _15 = as Future>::poll(move _17, move _18) -> [return: bb24, unwind: bb11]; ++ assert(const false, "`async fn` resumed after completion") -> [success: bb25, unwind continue]; + } + + bb26: { +- _19 = move _2; +- _18 = std::future::get_context::<'_, '_>(move _19) -> [return: bb25, unwind: bb11]; +- } +- +- bb27: { +- _20 = &mut _6; +- _17 = Pin::<&mut impl Future>::new_unchecked(move _20) -> [return: bb26, unwind: bb11]; +- } +- +- bb28: { +- StorageLive(_6); +- _6 = async_drop_in_place::<[AsyncInt; 2]>(copy (_21.0: &mut [AsyncInt; 2])) -> [return: bb27, unwind: bb11]; +- } +- +- bb29: { +- _22 = &mut _3; +- _21 = Pin::<&mut [AsyncInt; 2]>::new_unchecked(move _22) -> [return: bb28, unwind: bb7]; ++ nop; ++ StorageLive(_4); ++ _4 = AsyncInt(const 1_i32); ++ StorageLive(_5); ++ _5 = AsyncInt(const 2_i32); ++ (((*_25) as variant#4).1: [AsyncInt; 2]) = [move _4, move _5]; ++ goto -> bb1; + } + } + diff --git a/tests/mir-opt/coroutine/async_drop.array-{closure#0}.coroutine_drop_async.0.mir b/tests/mir-opt/coroutine/async_drop.array-{closure#0}.coroutine_drop_async.0.mir new file mode 100644 index 0000000000000..9fc1ebbe39d14 --- /dev/null +++ b/tests/mir-opt/coroutine/async_drop.array-{closure#0}.coroutine_drop_async.0.mir @@ -0,0 +1,154 @@ +// MIR for `array::{closure#0}` 0 coroutine_drop_async + +fn array::{closure#0}(_1: Pin<&mut {async fn body of array()}>, _2: &mut Context<'_>) -> Poll<()> { + debug _task_context => _26; + let mut _0: std::task::Poll<()>; + let _3: [AsyncInt; 2]; + let mut _4: AsyncInt; + let mut _5: AsyncInt; + let mut _6: impl std::future::Future; + let mut _7: std::future::ResumeTy; + let mut _8: std::task::Poll<()>; + let mut _9: isize; + let mut _10: std::pin::Pin<&mut impl std::future::Future>; + let mut _11: &mut std::task::Context<'_>; + let mut _12: std::future::ResumeTy; + let mut _13: &mut impl std::future::Future; + let mut _14: std::future::ResumeTy; + let mut _15: std::task::Poll<()>; + let mut _16: isize; + let mut _17: std::pin::Pin<&mut impl std::future::Future>; + let mut _18: &mut std::task::Context<'_>; + let mut _19: std::future::ResumeTy; + let mut _20: &mut impl std::future::Future; + let mut _21: std::pin::Pin<&mut [AsyncInt; 2]>; + let mut _22: &mut [AsyncInt; 2]; + let mut _23: (); + let mut _24: u32; + let mut _25: &mut {async fn body of array()}; + let mut _26: std::future::ResumeTy; + let mut _27: std::ptr::NonNull>; + scope 1 { + debug array => (((*_25) as variant#4).1: [AsyncInt; 2]); + } + + bb0: { + _27 = move _2 as std::ptr::NonNull> (Transmute); + _26 = std::future::ResumeTy(move _27); + _25 = copy (_1.0: &mut {async fn body of array()}); + _24 = discriminant((*_25)); + switchInt(move _24) -> [0: bb16, 2: bb21, 3: bb19, 4: bb20, otherwise: bb22]; + } + + bb1: { + nop; + goto -> bb2; + } + + bb2: { + _0 = Poll::<()>::Ready(const ()); + return; + } + + bb3 (cleanup): { + nop; + goto -> bb4; + } + + bb4 (cleanup): { + goto -> bb18; + } + + bb5: { + nop; + goto -> bb1; + } + + bb6 (cleanup): { + nop; + goto -> bb3; + } + + bb7: { + _26 = move _7; + StorageDead(_7); + goto -> bb13; + } + + bb8: { + StorageLive(_7); + _0 = Poll::<()>::Pending; + StorageDead(_7); + discriminant((*_25)) = 3; + return; + } + + bb9: { + unreachable; + } + + bb10: { + _9 = discriminant(_8); + switchInt(move _9) -> [0: bb5, 1: bb8, otherwise: bb9]; + } + + bb11: { + _8 = as Future>::poll(move _10, move _11) -> [return: bb10, unwind: bb6]; + } + + bb12: { + _12 = move _26; + _11 = copy (_12.0: std::ptr::NonNull>) as &mut std::task::Context<'_> (Transmute); + goto -> bb11; + } + + bb13: { + _13 = &mut (((*_25) as variant#4).2: impl std::future::Future); + _10 = Pin::<&mut impl Future>::new_unchecked(move _13) -> [return: bb12, unwind: bb6]; + } + + bb14: { + _26 = move _14; + StorageDead(_14); + goto -> bb13; + } + + bb15: { + _0 = Poll::<()>::Ready(const ()); + return; + } + + bb16: { + goto -> bb17; + } + + bb17: { + goto -> bb15; + } + + bb18 (cleanup): { + discriminant((*_25)) = 2; + resume; + } + + bb19: { + StorageLive(_7); + _7 = move _26; + goto -> bb7; + } + + bb20: { + StorageLive(_14); + _14 = move _26; + goto -> bb14; + } + + bb21: { + assert(const false, "`async fn` resumed after panicking") -> [success: bb21, unwind continue]; + } + + bb22: { + _0 = Poll::<()>::Ready(const ()); + return; + } +} diff --git a/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.[AsyncInt;2].StateTransform.diff b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.[AsyncInt;2].StateTransform.diff new file mode 100644 index 0000000000000..7718554895afd --- /dev/null +++ b/tests/mir-opt/coroutine/async_drop.core.future-async_drop-async_drop_in_place-{closure#0}.[AsyncInt;2].StateTransform.diff @@ -0,0 +1,357 @@ +- // MIR for `std::future::async_drop_in_place::{closure#0}` before StateTransform ++ // MIR for `std::future::async_drop_in_place::{closure#0}` after StateTransform + +- fn async_drop_in_place::{closure#0}(_1: {async fn body of async_drop_in_place<[AsyncInt; 2]>()}, _2: std::future::ResumeTy) -> () +- yields () +- { +- let mut _0: (); ++ fn async_drop_in_place::{closure#0}(_1: Pin<&mut {async fn body of async_drop_in_place<[AsyncInt; 2]>()}>, _2: &mut Context<'_>) -> Poll<()> { ++ coroutine layout { ++ field _s0: *mut [AsyncInt]; ++ field _s1: usize; ++ field _s2: usize; ++ field _s3: impl Future; ++ field _s4: impl Future; ++ variant_fields = { ++ Unresumed(0): [], ++ Returned (1): [], ++ Panicked (2): [], ++ Suspend0 (3): [_s0, _s1, _s2, _s3], ++ Suspend1 (4): [_s0, _s1, _s2, _s4], ++ Suspend2 (5): [_s0, _s1, _s2, _s4], ++ } ++ storage_conflicts = BitMatrix(5x5) {(_s0, _s0), (_s0, _s1), (_s0, _s2), (_s0, _s3), (_s0, _s4), (_s1, _s0), (_s1, _s1), (_s1, _s2), (_s1, _s3), (_s1, _s4), (_s2, _s0), (_s2, _s1), (_s2, _s2), (_s2, _s3), (_s2, _s4), (_s3, _s0), (_s3, _s1), (_s3, _s2), (_s3, _s3), (_s4, _s0), (_s4, _s1), (_s4, _s2), (_s4, _s4)} ++ } ++ let mut _0: std::task::Poll<()>; + let mut _3: &mut [AsyncInt; 2]; + let mut _4: *mut [AsyncInt; 2]; + let mut _5: *mut [AsyncInt]; + let mut _6: usize; + let mut _7: usize; + let mut _8: *mut AsyncInt; + let mut _9: bool; + let mut _10: *mut AsyncInt; + let mut _11: bool; + let mut _12: impl std::future::Future; + let mut _13: std::future::ResumeTy; + let mut _14: std::task::Poll<()>; + let mut _15: isize; + let mut _16: std::pin::Pin<&mut impl std::future::Future>; + let mut _17: &mut std::task::Context<'_>; + let mut _18: std::future::ResumeTy; + let mut _19: &mut impl std::future::Future; + let mut _20: std::pin::Pin<&mut AsyncInt>; + let mut _21: &mut AsyncInt; + let mut _22: *mut AsyncInt; + let mut _23: bool; + let mut _24: impl std::future::Future; + let mut _25: std::future::ResumeTy; + let mut _26: std::task::Poll<()>; + let mut _27: isize; + let mut _28: std::pin::Pin<&mut impl std::future::Future>; + let mut _29: &mut std::task::Context<'_>; + let mut _30: std::future::ResumeTy; + let mut _31: &mut impl std::future::Future; + let mut _32: std::future::ResumeTy; + let mut _33: std::task::Poll<()>; + let mut _34: isize; + let mut _35: std::pin::Pin<&mut impl std::future::Future>; + let mut _36: &mut std::task::Context<'_>; + let mut _37: std::future::ResumeTy; + let mut _38: &mut impl std::future::Future; + let mut _39: std::pin::Pin<&mut AsyncInt>; + let mut _40: &mut AsyncInt; ++ let mut _41: (); ++ let mut _42: u32; ++ let mut _43: &mut {async fn body of std::future::async_drop_in_place<[AsyncInt; 2]>()}; ++ let mut _44: *mut [AsyncInt]; ++ let mut _45: *mut [AsyncInt]; ++ let _46: std::future::ResumeTy; ++ let mut _47: std::ptr::NonNull>; + + bb0: { +- _3 = move (_1.0: &mut [AsyncInt; 2]); +- _4 = &raw mut (*_3); +- _5 = move _4 as *mut [AsyncInt] (PointerCoercion(Unsize, Implicit)); +- _6 = PtrMetadata(copy _5); +- _7 = const 0_usize; +- goto -> bb20; ++ _47 = move _2 as std::ptr::NonNull> (Transmute); ++ _46 = std::future::ResumeTy(move _47); ++ _43 = copy (_1.0: &mut {async fn body of std::future::async_drop_in_place<[AsyncInt; 2]>()}); ++ _42 = discriminant((*_43)); ++ switchInt(move _42) -> [0: bb28, 1: bb27, 2: bb26, 3: bb23, 4: bb24, 5: bb25, otherwise: bb8]; + } + + bb1: { ++ _0 = Poll::<()>::Ready(move _41); ++ discriminant((*_43)) = 1; + return; + } + + bb2 (cleanup): { +- resume; ++ goto -> bb22; + } + + bb3 (cleanup): { +- _8 = &raw mut (*_5)[_7]; +- _7 = Add(move _7, const 1_usize); ++ _7 = no_retag copy (((*_43) as variant#5).2: usize); ++ _44 = no_retag copy (((*_43) as variant#5).0: *mut [AsyncInt]); ++ _8 = &raw mut (*_44)[_7]; ++ (((*_43) as variant#5).2: usize) = Add(move (((*_43) as variant#5).2: usize), const 1_usize); + drop((*_8)) -> [return: bb4, unwind terminate(cleanup)]; + } + + bb4 (cleanup): { +- _9 = Eq(copy _7, copy _6); ++ _9 = Eq(copy (((*_43) as variant#5).2: usize), copy (((*_43) as variant#5).1: usize)); + switchInt(move _9) -> [0: bb3, otherwise: bb2]; + } + +- bb5: { +- _10 = &raw mut (*_5)[_7]; +- _7 = Add(move _7, const 1_usize); +- _21 = &mut (*_10); +- _20 = Pin::<&mut AsyncInt>::new_unchecked(move _21) -> [return: bb18, unwind: bb4]; ++ bb5 (cleanup): { ++ nop; ++ goto -> bb4; + } + + bb6: { +- _11 = Eq(copy _7, copy _6); +- switchInt(move _11) -> [0: bb5, otherwise: bb1]; ++ assert(const false, "`async fn` resumed after async drop") -> [success: bb6, unwind: bb5]; + } + + bb7: { +- StorageDead(_12); ++ _46 = move _13; ++ StorageDead(_13); + goto -> bb6; + } + +- bb8 (cleanup): { +- StorageDead(_12); +- goto -> bb4; ++ bb8: { ++ unreachable; + } + + bb9: { +- assert(const false, "`async fn` resumed after async drop") -> [success: bb9, unwind: bb8]; ++ _7 = no_retag copy (((*_43) as variant#5).2: usize); ++ _45 = no_retag copy (((*_43) as variant#5).0: *mut [AsyncInt]); ++ _22 = &raw mut (*_45)[_7]; ++ (((*_43) as variant#5).2: usize) = Add(move (((*_43) as variant#5).2: usize), const 1_usize); ++ _40 = &mut (*_22); ++ _39 = Pin::<&mut AsyncInt>::new_unchecked(move _40) -> [return: bb21, unwind: bb4]; + } + + bb10: { +- _2 = move _13; +- StorageDead(_13); +- goto -> bb9; ++ _23 = Eq(copy (((*_43) as variant#5).2: usize), copy (((*_43) as variant#5).1: usize)); ++ switchInt(move _23) -> [0: bb9, otherwise: bb1]; + } + + bb11: { +- _2 = move _13; +- StorageDead(_13); +- goto -> bb17; ++ nop; ++ goto -> bb10; + } + +- bb12: { +- StorageLive(_13); +- _13 = yield(const ()) -> [resume: bb10, drop: bb11]; ++ bb12 (cleanup): { ++ nop; ++ goto -> bb4; + } + + bb13: { +- unreachable; ++ assert(const false, "`async fn` resumed after async drop") -> [success: bb13, unwind: bb12]; + } + + bb14: { +- _15 = discriminant(_14); +- switchInt(move _15) -> [0: bb7, 1: bb12, otherwise: bb13]; ++ _46 = move _25; ++ StorageDead(_25); ++ goto -> bb13; + } + + bb15: { +- _14 = as Future>::poll(move _16, move _17) -> [return: bb14, unwind: bb8]; ++ _46 = move _32; ++ StorageDead(_32); ++ goto -> bb20; + } + + bb16: { +- _18 = move _2; +- _17 = std::future::get_context::<'_, '_>(move _18) -> [return: bb15, unwind: bb8]; ++ StorageLive(_32); ++ _0 = Poll::<()>::Pending; ++ StorageDead(_32); ++ discriminant((*_43)) = 5; ++ return; + } + + bb17: { +- _19 = &mut _12; +- _16 = Pin::<&mut impl Future>::new_unchecked(move _19) -> [return: bb16, unwind: bb8]; ++ _34 = discriminant(_33); ++ switchInt(move _34) -> [0: bb11, 1: bb16, otherwise: bb8]; + } + + bb18: { +- StorageLive(_12); +- _12 = async_drop_in_place::(copy (_20.0: &mut AsyncInt)) -> [return: bb17, unwind: bb8]; ++ _33 = as Future>::poll(move _35, move _36) -> [return: bb17, unwind: bb12]; + } + + bb19: { +- _22 = &raw mut (*_5)[_7]; +- _7 = Add(move _7, const 1_usize); +- _40 = &mut (*_22); +- _39 = Pin::<&mut AsyncInt>::new_unchecked(move _40) -> [return: bb39, unwind: bb4]; ++ _37 = move _46; ++ _36 = copy (_37.0: std::ptr::NonNull>) as &mut std::task::Context<'_> (Transmute); ++ goto -> bb18; + } + + bb20: { +- _23 = Eq(copy _7, copy _6); +- switchInt(move _23) -> [0: bb19, otherwise: bb1]; ++ _38 = &mut (((*_43) as variant#5).3: impl std::future::Future); ++ _35 = Pin::<&mut impl Future>::new_unchecked(move _38) -> [return: bb19, unwind: bb12]; + } + + bb21: { +- StorageDead(_24); +- goto -> bb20; ++ nop; ++ (((*_43) as variant#5).3: impl std::future::Future) = async_drop_in_place::(copy (_39.0: &mut AsyncInt)) -> [return: bb20, unwind: bb12]; + } + +- bb22: { +- StorageDead(_24); +- goto -> bb6; ++ bb22 (cleanup): { ++ discriminant((*_43)) = 2; ++ resume; + } + +- bb23 (cleanup): { +- StorageDead(_24); +- goto -> bb4; ++ bb23: { ++ StorageLive(_13); ++ _13 = move _46; ++ goto -> bb7; + } + + bb24: { +- assert(const false, "`async fn` resumed after async drop") -> [success: bb24, unwind: bb23]; ++ StorageLive(_25); ++ _25 = move _46; ++ goto -> bb14; + } + + bb25: { +- _2 = move _25; +- StorageDead(_25); +- goto -> bb24; ++ StorageLive(_32); ++ _32 = move _46; ++ goto -> bb15; + } + + bb26: { +- _2 = move _25; +- StorageDead(_25); +- goto -> bb31; ++ assert(const false, "`async fn` resumed after panicking") -> [success: bb26, unwind continue]; + } + + bb27: { +- StorageLive(_25); +- _25 = yield(const ()) -> [resume: bb25, drop: bb26]; ++ _0 = Poll::<()>::Ready(const ()); ++ return; + } + + bb28: { +- _27 = discriminant(_26); +- switchInt(move _27) -> [0: bb22, 1: bb27, otherwise: bb13]; +- } +- +- bb29: { +- _26 = as Future>::poll(move _28, move _29) -> [return: bb28, unwind: bb23]; +- } +- +- bb30: { +- _30 = move _2; +- _29 = std::future::get_context::<'_, '_>(move _30) -> [return: bb29, unwind: bb23]; +- } +- +- bb31: { +- _31 = &mut _24; +- _28 = Pin::<&mut impl Future>::new_unchecked(move _31) -> [return: bb30, unwind: bb23]; +- } +- +- bb32: { +- _2 = move _32; +- StorageDead(_32); +- goto -> bb38; +- } +- +- bb33: { +- _2 = move _32; +- StorageDead(_32); +- goto -> bb31; +- } +- +- bb34: { +- StorageLive(_32); +- _32 = yield(const ()) -> [resume: bb32, drop: bb33]; +- } +- +- bb35: { +- _34 = discriminant(_33); +- switchInt(move _34) -> [0: bb21, 1: bb34, otherwise: bb13]; +- } +- +- bb36: { +- _33 = as Future>::poll(move _35, move _36) -> [return: bb35, unwind: bb23]; +- } +- +- bb37: { +- _37 = move _2; +- _36 = std::future::get_context::<'_, '_>(move _37) -> [return: bb36, unwind: bb23]; +- } +- +- bb38: { +- _38 = &mut _24; +- _35 = Pin::<&mut impl Future>::new_unchecked(move _38) -> [return: bb37, unwind: bb23]; +- } +- +- bb39: { +- StorageLive(_24); +- _24 = async_drop_in_place::(copy (_39.0: &mut AsyncInt)) -> [return: bb38, unwind: bb23]; ++ _3 = move ((*_43).0: &mut [AsyncInt; 2]); ++ _4 = &raw mut (*_3); ++ (((*_43) as variant#5).0: *mut [AsyncInt]) = move _4 as *mut [AsyncInt] (PointerCoercion(Unsize, Implicit)); ++ (((*_43) as variant#5).1: usize) = PtrMetadata(copy (((*_43) as variant#5).0: *mut [AsyncInt])); ++ (((*_43) as variant#5).2: usize) = const 0_usize; ++ goto -> bb10; + } + } + diff --git a/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.ElaborateDrops.diff b/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.ElaborateDrops.diff index dd65409f0db13..4e0cbbfc4357c 100644 --- a/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.ElaborateDrops.diff +++ b/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.ElaborateDrops.diff @@ -33,8 +33,8 @@ + let mut _39: &mut std::task::Context<'_>; + let mut _40: std::future::ResumeTy; + let mut _41: &mut impl std::future::Future; -+ let mut _42: std::pin::Pin<&mut {async closure@$DIR/async_drop.rs:78:27: 78:35}>; -+ let mut _43: &mut {async closure@$DIR/async_drop.rs:78:27: 78:35}; ++ let mut _42: std::pin::Pin<&mut {async closure@$DIR/async_drop.rs:86:27: 86:35}>; ++ let mut _43: &mut {async closure@$DIR/async_drop.rs:86:27: 86:35}; + let mut _44: impl std::future::Future; + let mut _45: std::future::ResumeTy; + let mut _46: std::task::Poll<()>; @@ -50,8 +50,8 @@ + let mut _56: &mut std::task::Context<'_>; + let mut _57: std::future::ResumeTy; + let mut _58: &mut impl std::future::Future; -+ let mut _59: std::pin::Pin<&mut {closure@$DIR/async_drop.rs:70:25: 70:27}>; -+ let mut _60: &mut {closure@$DIR/async_drop.rs:70:25: 70:27}; ++ let mut _59: std::pin::Pin<&mut {closure@$DIR/async_drop.rs:78:25: 78:27}>; ++ let mut _60: &mut {closure@$DIR/async_drop.rs:78:25: 78:27}; + let mut _61: impl std::future::Future; + let mut _62: std::future::ResumeTy; + let mut _63: std::task::Poll<()>; @@ -200,13 +200,13 @@ let _23: AsyncInt; scope 10 { debug foo => _23; - let _24: {closure@$DIR/async_drop.rs:70:25: 70:27}; + let _24: {closure@$DIR/async_drop.rs:78:25: 78:27}; scope 11 { debug async_closure => _24; let _25: AsyncInt; scope 12 { debug foo => _25; - let _26: {async closure@$DIR/async_drop.rs:78:27: 78:35}; + let _26: {async closure@$DIR/async_drop.rs:86:27: 86:35}; scope 13 { debug async_coroutine => _26; } @@ -321,11 +321,11 @@ StorageLive(_23); _23 = AsyncInt(const 14_i32); StorageLive(_24); - _24 = {closure@$DIR/async_drop.rs:70:25: 70:27} { foo: move _23 }; + _24 = {closure@$DIR/async_drop.rs:78:25: 78:27} { foo: move _23 }; StorageLive(_25); _25 = AsyncInt(const 15_i32); StorageLive(_26); - _26 = {closure@$DIR/async_drop.rs:78:27: 78:35} { foo: move _25 }; + _26 = {closure@$DIR/async_drop.rs:86:27: 86:35} { foo: move _25 }; _0 = const (); - drop(_26) -> [return: bb10, unwind: bb44, drop: bb23]; + goto -> bb103; @@ -838,12 +838,12 @@ + + bb102: { + StorageLive(_27); -+ _27 = async_drop_in_place::<{async closure@$DIR/async_drop.rs:78:27: 78:35}>(copy (_42.0: &mut {async closure@$DIR/async_drop.rs:78:27: 78:35})) -> [return: bb101, unwind: bb85]; ++ _27 = async_drop_in_place::<{async closure@$DIR/async_drop.rs:86:27: 86:35}>(copy (_42.0: &mut {async closure@$DIR/async_drop.rs:86:27: 86:35})) -> [return: bb101, unwind: bb85]; + } + + bb103: { + _43 = &mut _26; -+ _42 = Pin::<&mut {async closure@$DIR/async_drop.rs:78:27: 78:35}>::new_unchecked(move _43) -> [return: bb102, unwind: bb44]; ++ _42 = Pin::<&mut {async closure@$DIR/async_drop.rs:86:27: 86:35}>::new_unchecked(move _43) -> [return: bb102, unwind: bb44]; + } + + bb104: { @@ -939,12 +939,12 @@ + + bb122: { + StorageLive(_44); -+ _44 = async_drop_in_place::<{closure@$DIR/async_drop.rs:70:25: 70:27}>(copy (_59.0: &mut {closure@$DIR/async_drop.rs:70:25: 70:27})) -> [return: bb121, unwind: bb106]; ++ _44 = async_drop_in_place::<{closure@$DIR/async_drop.rs:78:25: 78:27}>(copy (_59.0: &mut {closure@$DIR/async_drop.rs:78:25: 78:27})) -> [return: bb121, unwind: bb106]; + } + + bb123: { + _60 = &mut _24; -+ _59 = Pin::<&mut {closure@$DIR/async_drop.rs:70:25: 70:27}>::new_unchecked(move _60) -> [return: bb122, unwind: bb46]; ++ _59 = Pin::<&mut {closure@$DIR/async_drop.rs:78:25: 78:27}>::new_unchecked(move _60) -> [return: bb122, unwind: bb46]; + } + + bb124: { diff --git a/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.StateTransform.diff b/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.StateTransform.diff index 5fb3dba08ad87..34ea0eeaa07a2 100644 --- a/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.StateTransform.diff +++ b/tests/mir-opt/coroutine/async_drop.elaborate_drops-{closure#0}.StateTransform.diff @@ -17,8 +17,8 @@ + field _s6: AsyncEnum; + field _s7: AsyncInt; + field _s8: AsyncReference<'_>; -+ field _s9: {closure@$DIR/async_drop.rs:70:25: 70:27}; -+ field _s10: {async closure@$DIR/async_drop.rs:78:27: 78:35}; ++ field _s9: {closure@$DIR/async_drop.rs:78:25: 78:27}; ++ field _s10: {async closure@$DIR/async_drop.rs:86:27: 86:35}; + field _s11: impl Future; + field _s12: impl Future; + field _s13: impl Future; @@ -83,8 +83,8 @@ let mut _39: &mut std::task::Context<'_>; let mut _40: std::future::ResumeTy; let mut _41: &mut impl std::future::Future; - let mut _42: std::pin::Pin<&mut {async closure@$DIR/async_drop.rs:78:27: 78:35}>; - let mut _43: &mut {async closure@$DIR/async_drop.rs:78:27: 78:35}; + let mut _42: std::pin::Pin<&mut {async closure@$DIR/async_drop.rs:86:27: 86:35}>; + let mut _43: &mut {async closure@$DIR/async_drop.rs:86:27: 86:35}; let mut _44: impl std::future::Future; let mut _45: std::future::ResumeTy; let mut _46: std::task::Poll<()>; @@ -100,8 +100,8 @@ let mut _56: &mut std::task::Context<'_>; let mut _57: std::future::ResumeTy; let mut _58: &mut impl std::future::Future; - let mut _59: std::pin::Pin<&mut {closure@$DIR/async_drop.rs:70:25: 70:27}>; - let mut _60: &mut {closure@$DIR/async_drop.rs:70:25: 70:27}; + let mut _59: std::pin::Pin<&mut {closure@$DIR/async_drop.rs:78:25: 78:27}>; + let mut _60: &mut {closure@$DIR/async_drop.rs:78:25: 78:27}; let mut _61: impl std::future::Future; let mut _62: std::future::ResumeTy; let mut _63: std::task::Poll<()>; @@ -271,18 +271,18 @@ scope 10 { debug foo => _23; + coroutine debug async_closure => _s9; - let _24: {closure@$DIR/async_drop.rs:70:25: 70:27}; + let _24: {closure@$DIR/async_drop.rs:78:25: 78:27}; scope 11 { - debug async_closure => _24; -+ debug async_closure => (((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:70:25: 70:27}); ++ debug async_closure => (((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:78:25: 78:27}); let _25: AsyncInt; scope 12 { debug foo => _25; + coroutine debug async_coroutine => _s10; - let _26: {async closure@$DIR/async_drop.rs:78:27: 78:35}; + let _26: {async closure@$DIR/async_drop.rs:86:27: 86:35}; scope 13 { - debug async_coroutine => _26; -+ debug async_coroutine => (((*_182) as variant#4).10: {async closure@$DIR/async_drop.rs:78:27: 78:35}); ++ debug async_coroutine => (((*_182) as variant#4).10: {async closure@$DIR/async_drop.rs:86:27: 86:35}); } } } @@ -404,17 +404,17 @@ StorageLive(_23); _23 = AsyncInt(const 14_i32); - StorageLive(_24); -- _24 = {closure@$DIR/async_drop.rs:70:25: 70:27} { foo: move _23 }; +- _24 = {closure@$DIR/async_drop.rs:78:25: 78:27} { foo: move _23 }; + nop; -+ (((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:70:25: 70:27}) = {closure@$DIR/async_drop.rs:70:25: 70:27} { foo: move _23 }; ++ (((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:78:25: 78:27}) = {closure@$DIR/async_drop.rs:78:25: 78:27} { foo: move _23 }; StorageLive(_25); _25 = AsyncInt(const 15_i32); - StorageLive(_26); -- _26 = {closure@$DIR/async_drop.rs:78:27: 78:35} { foo: move _25 }; +- _26 = {closure@$DIR/async_drop.rs:86:27: 86:35} { foo: move _25 }; - _0 = const (); - goto -> bb72; + nop; -+ (((*_182) as variant#4).10: {async closure@$DIR/async_drop.rs:78:27: 78:35}) = {closure@$DIR/async_drop.rs:78:27: 78:35} { foo: move _25 }; ++ (((*_182) as variant#4).10: {async closure@$DIR/async_drop.rs:86:27: 86:35}) = {closure@$DIR/async_drop.rs:86:27: 86:35} { foo: move _25 }; + (((*_182) as variant#20).0: ()) = const (); + goto -> bb51; } @@ -517,7 +517,7 @@ + bb24 (cleanup): { StorageDead(_25); - goto -> bb25; -+ drop((((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:70:25: 70:27})) -> [return: bb25, unwind terminate(cleanup)]; ++ drop((((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:78:25: 78:27})) -> [return: bb25, unwind terminate(cleanup)]; } - bb25: { @@ -720,14 +720,14 @@ - drop(_1) -> [return: bb51, unwind terminate(cleanup)]; + bb50: { + nop; -+ (((*_182) as variant#4).11: impl std::future::Future) = async_drop_in_place::<{async closure@$DIR/async_drop.rs:78:27: 78:35}>(copy (_42.0: &mut {async closure@$DIR/async_drop.rs:78:27: 78:35})) -> [return: bb49, unwind: bb40]; ++ (((*_182) as variant#4).11: impl std::future::Future) = async_drop_in_place::<{async closure@$DIR/async_drop.rs:86:27: 86:35}>(copy (_42.0: &mut {async closure@$DIR/async_drop.rs:86:27: 86:35})) -> [return: bb49, unwind: bb40]; } - bb51 (cleanup): { - resume; + bb51: { -+ _43 = &mut (((*_182) as variant#4).10: {async closure@$DIR/async_drop.rs:78:27: 78:35}); -+ _42 = Pin::<&mut {async closure@$DIR/async_drop.rs:78:27: 78:35}>::new_unchecked(move _43) -> [return: bb50, unwind: bb23]; ++ _43 = &mut (((*_182) as variant#4).10: {async closure@$DIR/async_drop.rs:86:27: 86:35}); ++ _42 = Pin::<&mut {async closure@$DIR/async_drop.rs:86:27: 86:35}>::new_unchecked(move _43) -> [return: bb50, unwind: bb23]; } bb52: { @@ -811,14 +811,14 @@ - _33 = move _2; - _32 = std::future::get_context::<'_, '_>(move _33) -> [return: bb61, unwind: bb54]; + nop; -+ (((*_182) as variant#6).10: impl std::future::Future) = async_drop_in_place::<{closure@$DIR/async_drop.rs:70:25: 70:27}>(copy (_59.0: &mut {closure@$DIR/async_drop.rs:70:25: 70:27})) -> [return: bb61, unwind: bb53]; ++ (((*_182) as variant#6).10: impl std::future::Future) = async_drop_in_place::<{closure@$DIR/async_drop.rs:78:25: 78:27}>(copy (_59.0: &mut {closure@$DIR/async_drop.rs:78:25: 78:27})) -> [return: bb61, unwind: bb53]; } bb63: { - _34 = &mut _27; - _31 = Pin::<&mut impl Future>::new_unchecked(move _34) -> [return: bb62, unwind: bb54]; -+ _60 = &mut (((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:70:25: 70:27}); -+ _59 = Pin::<&mut {closure@$DIR/async_drop.rs:70:25: 70:27}>::new_unchecked(move _60) -> [return: bb62, unwind: bb25]; ++ _60 = &mut (((*_182) as variant#6).9: {closure@$DIR/async_drop.rs:78:25: 78:27}); ++ _59 = Pin::<&mut {closure@$DIR/async_drop.rs:78:25: 78:27}>::new_unchecked(move _60) -> [return: bb62, unwind: bb25]; } bb64: { @@ -879,13 +879,13 @@ bb71: { - StorageLive(_27); -- _27 = async_drop_in_place::<{async closure@$DIR/async_drop.rs:78:27: 78:35}>(copy (_42.0: &mut {async closure@$DIR/async_drop.rs:78:27: 78:35})) -> [return: bb70, unwind: bb54]; +- _27 = async_drop_in_place::<{async closure@$DIR/async_drop.rs:86:27: 86:35}>(copy (_42.0: &mut {async closure@$DIR/async_drop.rs:86:27: 86:35})) -> [return: bb70, unwind: bb54]; + _70 = as Future>::poll(move _72, move _73) -> [return: bb70, unwind: bb65]; } bb72: { - _43 = &mut _26; -- _42 = Pin::<&mut {async closure@$DIR/async_drop.rs:78:27: 78:35}>::new_unchecked(move _43) -> [return: bb71, unwind: bb36]; +- _42 = Pin::<&mut {async closure@$DIR/async_drop.rs:86:27: 86:35}>::new_unchecked(move _43) -> [return: bb71, unwind: bb36]; + _74 = move _183; + _73 = copy (_74.0: std::ptr::NonNull>) as &mut std::task::Context<'_> (Transmute); + goto -> bb71; @@ -1027,7 +1027,7 @@ bb91: { - StorageLive(_44); -- _44 = async_drop_in_place::<{closure@$DIR/async_drop.rs:70:25: 70:27}>(copy (_59.0: &mut {closure@$DIR/async_drop.rs:70:25: 70:27})) -> [return: bb90, unwind: bb75]; +- _44 = async_drop_in_place::<{closure@$DIR/async_drop.rs:78:25: 78:27}>(copy (_59.0: &mut {closure@$DIR/async_drop.rs:78:25: 78:27})) -> [return: bb90, unwind: bb75]; + _183 = move _96; + StorageDead(_96); + goto -> bb90; @@ -1035,7 +1035,7 @@ bb92: { - _60 = &mut _24; -- _59 = Pin::<&mut {closure@$DIR/async_drop.rs:70:25: 70:27}>::new_unchecked(move _60) -> [return: bb91, unwind: bb38]; +- _59 = Pin::<&mut {closure@$DIR/async_drop.rs:78:25: 78:27}>::new_unchecked(move _60) -> [return: bb91, unwind: bb38]; + _183 = move _103; + StorageDead(_103); + goto -> bb97; diff --git a/tests/mir-opt/coroutine/async_drop.rs b/tests/mir-opt/coroutine/async_drop.rs index 506baac1dabd8..dfba9e14708db 100644 --- a/tests/mir-opt/coroutine/async_drop.rs +++ b/tests/mir-opt/coroutine/async_drop.rs @@ -51,6 +51,14 @@ async fn double() { let async_int_again = AsyncInt(0); } +// EMIT_MIR async_drop.array-{closure#0}.ElaborateDrops.diff +// EMIT_MIR async_drop.array-{closure#0}.StateTransform.diff +// EMIT_MIR async_drop.array-{closure#0}.coroutine_drop_async.0.mir +// EMIT_MIR core.future-async_drop-async_drop_in_place-{closure#0}.[AsyncInt;2].StateTransform.diff +async fn array() { + let array = [AsyncInt(1), AsyncInt(2)]; +} + // EMIT_MIR async_drop.elaborate_drops-{closure#0}.ElaborateDrops.diff // EMIT_MIR async_drop.elaborate_drops-{closure#0}.StateTransform.diff async fn elaborate_drops() { @@ -90,6 +98,7 @@ fn main() { let i = 13; let fut = pin!(async { + array().await; elaborate_drops().await; test_async_drop(Int(0)).await; diff --git a/tests/pretty/direct-const-arg.pp b/tests/pretty/direct-const-arg.pp new file mode 100644 index 0000000000000..a76ed9a480e71 --- /dev/null +++ b/tests/pretty/direct-const-arg.pp @@ -0,0 +1,15 @@ +#![feature(prelude_import)] +#![no_std] +//@ pretty-mode:expanded +//@ pp-exact:direct-const-arg.pp +#![feature(min_generic_const_args)] +extern crate std; +#[prelude_import] +use ::std::prelude::rust_2015::*; + +fn f() {} + +fn main() { + f::(); + f::<{ core::direct_const_arg! (2) }>(); +} diff --git a/tests/pretty/direct-const-arg.rs b/tests/pretty/direct-const-arg.rs new file mode 100644 index 0000000000000..330c1cac024de --- /dev/null +++ b/tests/pretty/direct-const-arg.rs @@ -0,0 +1,10 @@ +//@ pretty-mode:expanded +//@ pp-exact:direct-const-arg.pp +#![feature(min_generic_const_args)] + +fn f() {} + +fn main() { + f::(); + f::<{ core::direct_const_arg!(2) }>(); +} diff --git a/tests/run-make/unstable-flag-required/README.md b/tests/run-make/unstable-flag-required/README.md deleted file mode 100644 index e5251fdf9b5d9..0000000000000 --- a/tests/run-make/unstable-flag-required/README.md +++ /dev/null @@ -1,3 +0,0 @@ -This is a collection of tests that verify `--unstable-options` is required. -It should eventually be removed in favor of UI tests once compiletest stops -passing --unstable-options by default (#82639). diff --git a/tests/run-make/unstable-flag-required/output-format-json.stderr b/tests/run-make/unstable-flag-required/output-format-json.stderr deleted file mode 100644 index fb4079beb2799..0000000000000 --- a/tests/run-make/unstable-flag-required/output-format-json.stderr +++ /dev/null @@ -1,2 +0,0 @@ -error: the -Z unstable-options flag must be passed to enable --output-format for documentation generation (see https://github.com/rust-lang/rust/issues/76578) - diff --git a/tests/run-make/unstable-flag-required/rmake.rs b/tests/run-make/unstable-flag-required/rmake.rs deleted file mode 100644 index c521436c203d5..0000000000000 --- a/tests/run-make/unstable-flag-required/rmake.rs +++ /dev/null @@ -1,12 +0,0 @@ -// The flag `--output-format` is unauthorized on beta and stable releases, which led -// to confusion for maintainers doing testing on nightly. Tying it to an unstable flag -// elucidates this, and this test checks that `--output-format` cannot be passed on its -// own. -// See https://github.com/rust-lang/rust/pull/82497 - -use run_make_support::{diff, rustdoc}; - -fn main() { - let out = rustdoc().output_format("json").input("x.html").run_fail().stderr_utf8(); - diff().expected_file("output-format-json.stderr").actual_text("actual-json", out).run(); -} diff --git a/tests/run-make/unstable-flag-required/x.rs b/tests/run-make/unstable-flag-required/x.rs deleted file mode 100644 index 5df7576133a68..0000000000000 --- a/tests/run-make/unstable-flag-required/x.rs +++ /dev/null @@ -1 +0,0 @@ -// nothing to see here diff --git a/tests/run-make/wasm-emscripten-main-symbol/main.rs b/tests/run-make/wasm-emscripten-main-symbol/main.rs new file mode 100644 index 0000000000000..f328e4d9d04c3 --- /dev/null +++ b/tests/run-make/wasm-emscripten-main-symbol/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/run-make/wasm-emscripten-main-symbol/rmake.rs b/tests/run-make/wasm-emscripten-main-symbol/rmake.rs new file mode 100644 index 0000000000000..03bbdd0bcb62b --- /dev/null +++ b/tests/run-make/wasm-emscripten-main-symbol/rmake.rs @@ -0,0 +1,45 @@ +//@ only-wasm32-unknown-emscripten + +// On wasm the age-old C trick of a `main` that is either `int main(void)` or +// `int main(int, char**)` doesn't work, because wasm requires caller and callee +// signatures to match. The platform ABI (as used by Clang) therefore mangles +// the entry point by signature; the argc/argv form is emitted as +// `__main_argc_argv`. Rust's emscripten target uses the argc/argv form, so its +// entry point must be emitted under that name rather than as a raw `main`. +// +// This matters beyond cosmetics: emscripten's own crt references +// `__main_argc_argv` directly on some entry paths (notably `crt1_proxy_main.o`, +// used by `-sPROXY_TO_PTHREAD`), which fail to link against a raw `main`. +// +// The JS-visible name of the entry point stays `_main`; emscripten bridges that +// to the wasm `__main_argc_argv` export internally. + +use run_make_support::{rfs, rustc, wasmparser}; +use wasmparser::ExternalKind; + +fn main() { + rustc().input("main.rs").target("wasm32-unknown-emscripten").output("main.js").run(); + + let file = rfs::read("main.wasm"); + let mut entry = None; + let mut has_plain_main = false; + for payload in wasmparser::Parser::new(0).parse_all(&file) { + if let wasmparser::Payload::ExportSection(s) = payload.unwrap() { + for export in s { + let export = export.unwrap(); + match export.name { + "__main_argc_argv" => entry = Some(export.kind), + "main" => has_plain_main = true, + _ => {} + } + } + } + } + + assert_eq!( + entry, + Some(ExternalKind::Func), + "the emscripten entry point must be exported as the wasm C-ABI symbol `__main_argc_argv`", + ); + assert!(!has_plain_main, "a raw `main` symbol must not be exported on wasm"); +} diff --git a/tests/rustdoc-ui/output-format-coveragejson-emit-depinfo.rs b/tests/rustdoc-ui/output-format-coveragejson-emit-depinfo.rs new file mode 100644 index 0000000000000..0609b515e5ca8 --- /dev/null +++ b/tests/rustdoc-ui/output-format-coveragejson-emit-depinfo.rs @@ -0,0 +1,2 @@ +//@ compile-flags: --show-coverage --output-format=json --emit=dep-info -Zunstable-options +//@ build-pass diff --git a/tests/rustdoc-ui/output-format-coveragejson-emit-depinfo.stdout b/tests/rustdoc-ui/output-format-coveragejson-emit-depinfo.stdout new file mode 100644 index 0000000000000..f3ba8205ddedf --- /dev/null +++ b/tests/rustdoc-ui/output-format-coveragejson-emit-depinfo.stdout @@ -0,0 +1 @@ +{"$DIR/output-format-coveragejson-emit-depinfo.rs":{"total":1,"with_docs":0,"total_examples":0,"with_examples":0}} diff --git a/tests/rustdoc-ui/output-format-doctests-unstable.rs b/tests/rustdoc-ui/output-format-doctests-unstable.rs new file mode 100644 index 0000000000000..b409f619fcc42 --- /dev/null +++ b/tests/rustdoc-ui/output-format-doctests-unstable.rs @@ -0,0 +1,4 @@ +//@ compile-flags: --output-format=doctest +pub struct Foo; + +//~? ERROR the -Z unstable-options flag must be passed to enable --output-format=doctest (see https://github.com/rust-lang/rust/issues/134529) diff --git a/tests/rustdoc-ui/output-format-doctests-unstable.stderr b/tests/rustdoc-ui/output-format-doctests-unstable.stderr new file mode 100644 index 0000000000000..b4e920efb2b0f --- /dev/null +++ b/tests/rustdoc-ui/output-format-doctests-unstable.stderr @@ -0,0 +1,2 @@ +error: the -Z unstable-options flag must be passed to enable --output-format=doctest (see https://github.com/rust-lang/rust/issues/134529) + diff --git a/tests/rustdoc-ui/output-format-irjson-emit-depinfo.rs b/tests/rustdoc-ui/output-format-irjson-emit-depinfo.rs new file mode 100644 index 0000000000000..7b5e7eaf998d4 --- /dev/null +++ b/tests/rustdoc-ui/output-format-irjson-emit-depinfo.rs @@ -0,0 +1,2 @@ +//@ compile-flags: --output-format=json --emit=dep-info -Zunstable-options +//@ build-pass diff --git a/tests/rustdoc-ui/output-format-irjson-unstable.rs b/tests/rustdoc-ui/output-format-irjson-unstable.rs new file mode 100644 index 0000000000000..88ba753616bb9 --- /dev/null +++ b/tests/rustdoc-ui/output-format-irjson-unstable.rs @@ -0,0 +1,4 @@ +//@ compile-flags: --output-format=json +pub struct Foo; + +//~? ERROR the -Z unstable-options flag must be passed to enable --output-format=json for documentation diff --git a/tests/rustdoc-ui/output-format-irjson-unstable.stderr b/tests/rustdoc-ui/output-format-irjson-unstable.stderr new file mode 100644 index 0000000000000..dd9cea9301189 --- /dev/null +++ b/tests/rustdoc-ui/output-format-irjson-unstable.stderr @@ -0,0 +1,2 @@ +error: the -Z unstable-options flag must be passed to enable --output-format=json for documentation generation (see https://github.com/rust-lang/rust/issues/76578) + diff --git a/tests/rustdoc-ui/output-format-json-emit-html.html_non_static_coverage.stderr b/tests/rustdoc-ui/output-format-json-emit-html.html_non_static_coverage.stderr new file mode 100644 index 0000000000000..8d8e8c6d12281 --- /dev/null +++ b/tests/rustdoc-ui/output-format-json-emit-html.html_non_static_coverage.stderr @@ -0,0 +1,2 @@ +error: the `--emit=html-non-static-files` flag is not supported with `--output-format=json` + diff --git a/tests/rustdoc-ui/output-format-json-emit-html.html_static_coverage.stderr b/tests/rustdoc-ui/output-format-json-emit-html.html_static_coverage.stderr new file mode 100644 index 0000000000000..bc1ddc1b155d1 --- /dev/null +++ b/tests/rustdoc-ui/output-format-json-emit-html.html_static_coverage.stderr @@ -0,0 +1,2 @@ +error: the `--emit=html-static-files` flag is not supported with `--output-format=json` + diff --git a/tests/rustdoc-ui/output-format-json-emit-html.rs b/tests/rustdoc-ui/output-format-json-emit-html.rs index 7ce3bf756ec92..f97392bab5b68 100644 --- a/tests/rustdoc-ui/output-format-json-emit-html.rs +++ b/tests/rustdoc-ui/output-format-json-emit-html.rs @@ -1,7 +1,11 @@ -//@ revisions: html_static html_non_static +//@ revisions: html_static html_non_static html_static_coverage html_non_static_coverage //@ compile-flags: -Zunstable-options --output-format json //@[html_static] compile-flags: --emit html-static-files +//@[html_static_coverage] compile-flags: --emit html-static-files --show-coverage //@[html_non_static] compile-flags: --emit html-non-static-files +//@[html_non_static_coverage] compile-flags: --emit html-non-static-files --show-coverage //[html_static]~? ERROR the `--emit=html-static-files` flag is not supported with `--output-format=json` +//[html_static_coverage]~? ERROR the `--emit=html-static-files` flag is not supported with `--output-format=json` //[html_non_static]~? ERROR the `--emit=html-non-static-files` flag is not supported with `--output-format=json` +//[html_non_static_coverage]~? ERROR the `--emit=html-non-static-files` flag is not supported with `--output-format=json` diff --git a/tests/rustdoc-ui/output-format-unknown.rs b/tests/rustdoc-ui/output-format-unknown.rs new file mode 100644 index 0000000000000..281a9ceb4cfd7 --- /dev/null +++ b/tests/rustdoc-ui/output-format-unknown.rs @@ -0,0 +1,4 @@ +//@ compile-flags: --output-format=da-doo-ron-ron + +pub struct Foo; +//~? ERROR unknown output format `da-doo-ron-ron` diff --git a/tests/rustdoc-ui/output-format-unknown.stderr b/tests/rustdoc-ui/output-format-unknown.stderr new file mode 100644 index 0000000000000..0f34e62b628ef --- /dev/null +++ b/tests/rustdoc-ui/output-format-unknown.stderr @@ -0,0 +1,2 @@ +error: unknown output format `da-doo-ron-ron` + diff --git a/tests/ui/allocator/not-an-allocator.u.stderr b/tests/ui/allocator/not-an-allocator.u.stderr index f7400d16b1fd1..9be5dd495db02 100644 --- a/tests/ui/allocator/not-an-allocator.u.stderr +++ b/tests/ui/allocator/not-an-allocator.u.stderr @@ -4,10 +4,11 @@ error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied LL | #[global_allocator] | ------------------- in this attribute macro expansion LL | static A: usize = 0; - | ^^^^^ the trait `GlobalAlloc` is not implemented for `usize` + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` | -help: the trait `GlobalAlloc` is implemented for `System` - --> $SRC_DIR/std/src/sys/alloc/unix.rs:LL:COL +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied --> $DIR/not-an-allocator.rs:5:11 @@ -15,10 +16,11 @@ error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied LL | #[global_allocator] | ------------------- in this attribute macro expansion LL | static A: usize = 0; - | ^^^^^ the trait `GlobalAlloc` is not implemented for `usize` + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` | -help: the trait `GlobalAlloc` is implemented for `System` - --> $SRC_DIR/std/src/sys/alloc/unix.rs:LL:COL +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied @@ -27,10 +29,11 @@ error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied LL | #[global_allocator] | ------------------- in this attribute macro expansion LL | static A: usize = 0; - | ^^^^^ the trait `GlobalAlloc` is not implemented for `usize` + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` | -help: the trait `GlobalAlloc` is implemented for `System` - --> $SRC_DIR/std/src/sys/alloc/unix.rs:LL:COL +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied @@ -39,10 +42,11 @@ error[E0277]: the trait bound `usize: GlobalAlloc` is not satisfied LL | #[global_allocator] | ------------------- in this attribute macro expansion LL | static A: usize = 0; - | ^^^^^ the trait `GlobalAlloc` is not implemented for `usize` + | ^^^^^ the nightly-only, unstable trait `GlobalAllocator` is not implemented for `usize` | -help: the trait `GlobalAlloc` is implemented for `System` - --> $SRC_DIR/std/src/sys/alloc/unix.rs:LL:COL +help: the trait `GlobalAllocator` is implemented for `System` + --> $SRC_DIR/std/src/alloc.rs:LL:COL + = note: required for `usize` to implement `GlobalAlloc` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: aborting due to 4 previous errors diff --git a/tests/ui/async-await/async-drop/async-drop-array-step.rs b/tests/ui/async-await/async-drop/async-drop-array-step.rs new file mode 100644 index 0000000000000..9bb26ccde6c10 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-array-step.rs @@ -0,0 +1,94 @@ +//@ run-pass +//@ check-run-results +//@ edition: 2021 + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::future::{async_drop_in_place, AsyncDrop, Future}; +use std::mem::ManuallyDrop; +use std::pin::{pin, Pin}; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::sync::{mpsc, Arc}; +use std::task::{Context, Poll, Wake, Waker}; + +static DROP_COUNT: AtomicUsize = AtomicUsize::new(0); + +struct YieldOnce(bool); + +impl Future for YieldOnce { + type Output = (); + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> { + if !self.0 { + self.0 = true; + cx.waker().wake_by_ref(); + Poll::Pending + } else { + Poll::Ready(()) + } + } +} + +struct Item(usize); + +impl Drop for Item { + fn drop(&mut self) {} +} + +impl AsyncDrop for Item { + async fn drop(self: Pin<&mut Self>) { + let count = DROP_COUNT.fetch_add(1, Ordering::Relaxed); + if count >= 8 { + panic!("Infinite loop detected: array drop index reset across yield points!"); + } + YieldOnce(false).await; + println!("Dropping {}", self.0); + } +} + +fn block_on(fut_unpin: F) -> F::Output { + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} + +async fn run() { + let _arr: [Item; 4] = [Item(0), Item(1), Item(2), Item(3)]; +} + +fn main() { + block_on(run()); +} diff --git a/tests/ui/async-await/async-drop/async-drop-array-step.run.stdout b/tests/ui/async-await/async-drop/async-drop-array-step.run.stdout new file mode 100644 index 0000000000000..50423d4dc19d4 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-array-step.run.stdout @@ -0,0 +1,4 @@ +Dropping 0 +Dropping 1 +Dropping 2 +Dropping 3 diff --git a/tests/ui/async-await/async-drop/async-drop-initial.rs b/tests/ui/async-await/async-drop/async-drop-initial.rs index 7da87a78701b5..873256890f6a7 100644 --- a/tests/ui/async-await/async-drop/async-drop-initial.rs +++ b/tests/ui/async-await/async-drop/async-drop-initial.rs @@ -54,7 +54,7 @@ fn main() { let fut = pin!(async { test_async_drop(Int(0), 16).await; test_async_drop(AsyncInt(0), 32).await; - test_async_drop([AsyncInt(1), AsyncInt(2)], 104).await; + test_async_drop([AsyncInt(1), AsyncInt(2)], 112).await; test_async_drop((AsyncInt(3), AsyncInt(4)), 120).await; test_async_drop(5, 16).await; let j = 42; diff --git a/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr b/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr index 62bebd53b14a6..6cf4e881adae8 100644 --- a/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr +++ b/tests/ui/const-generics/generic_const_exprs/assoc_const_unification/doesnt_unify_evaluatable.stderr @@ -1,8 +1,8 @@ error: unconstrained generic constant - --> $DIR/doesnt_unify_evaluatable.rs:9:13 + --> $DIR/doesnt_unify_evaluatable.rs:9:11 | LL | bar::<{ T::ASSOC }>(); - | ^^^^^^^^ + | ^^^^^^^^^^^^ | help: try adding a `where` bound | diff --git a/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs b/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs index 8470b933cadd2..b09d17fb11262 100644 --- a/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs +++ b/tests/ui/const-generics/mgca/adt_expr_arg_simple.rs @@ -10,7 +10,6 @@ use Option::Some; fn foo>() {} - trait Trait { type const ASSOC: u32; } @@ -26,7 +25,7 @@ fn bar() { // this on the other hand is not allowed as `N + 1` is not a legal // const argument - foo::<{ Some:: { 0: N + 1 } }>(); + foo::<{ core::direct_const_arg!(Some:: { 0: N + 1 }) }>(); //~^ ERROR: complex const arguments must be placed inside of a `const` block // this also is not allowed as generic parameters cannot be used diff --git a/tests/ui/const-generics/mgca/adt_expr_arg_simple.stderr b/tests/ui/const-generics/mgca/adt_expr_arg_simple.stderr index 0060c94875b5c..8f02ce0f82315 100644 --- a/tests/ui/const-generics/mgca/adt_expr_arg_simple.stderr +++ b/tests/ui/const-generics/mgca/adt_expr_arg_simple.stderr @@ -1,11 +1,11 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/adt_expr_arg_simple.rs:29:30 + --> $DIR/adt_expr_arg_simple.rs:28:54 | -LL | foo::<{ Some:: { 0: N + 1 } }>(); - | ^^^^^ +LL | foo::<{ core::direct_const_arg!(Some:: { 0: N + 1 }) }>(); + | ^^^^^ error: generic parameters may not be used in const operations - --> $DIR/adt_expr_arg_simple.rs:34:38 + --> $DIR/adt_expr_arg_simple.rs:33:38 | LL | foo::<{ Some:: { 0: const { N + 1 } } }>(); | ^ diff --git a/tests/ui/const-generics/mgca/array-expr-complex.r1.stderr b/tests/ui/const-generics/mgca/array-expr-complex.r1.stderr index 0c931af8d8b1c..a226d7ff0c225 100644 --- a/tests/ui/const-generics/mgca/array-expr-complex.r1.stderr +++ b/tests/ui/const-generics/mgca/array-expr-complex.r1.stderr @@ -1,8 +1,8 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array-expr-complex.rs:11:28 + --> $DIR/array-expr-complex.rs:11:52 | -LL | takes_array::<{ [1, 2, 1 + 2] }>(); - | ^^^^^ +LL | takes_array::<{ core::direct_const_arg!([1, 2, 1 + 2]) }>(); + | ^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/mgca/array-expr-complex.r2.stderr b/tests/ui/const-generics/mgca/array-expr-complex.r2.stderr index 335af9235e0c9..cc1e70c1d9a7a 100644 --- a/tests/ui/const-generics/mgca/array-expr-complex.r2.stderr +++ b/tests/ui/const-generics/mgca/array-expr-complex.r2.stderr @@ -1,8 +1,8 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array-expr-complex.rs:14:21 + --> $DIR/array-expr-complex.rs:14:45 | -LL | takes_array::<{ [X; 3] }>(); - | ^^^^^^ +LL | takes_array::<{ core::direct_const_arg!([X; 3]) }>(); + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/mgca/array-expr-complex.r3.stderr b/tests/ui/const-generics/mgca/array-expr-complex.r3.stderr index 02d74c1b5d0c5..cc52abe0e8fb8 100644 --- a/tests/ui/const-generics/mgca/array-expr-complex.r3.stderr +++ b/tests/ui/const-generics/mgca/array-expr-complex.r3.stderr @@ -1,8 +1,8 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array-expr-complex.rs:17:21 + --> $DIR/array-expr-complex.rs:17:45 | -LL | takes_array::<{ [0; Y] }>(); - | ^^^^^^ +LL | takes_array::<{ core::direct_const_arg!([0; Y]) }>(); + | ^^^^^^ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/mgca/array-expr-complex.rs b/tests/ui/const-generics/mgca/array-expr-complex.rs index 26f4700b5885c..7f5a77fdff3df 100644 --- a/tests/ui/const-generics/mgca/array-expr-complex.rs +++ b/tests/ui/const-generics/mgca/array-expr-complex.rs @@ -8,13 +8,13 @@ fn takes_array() {} fn generic_caller() { // not supported yet #[cfg(r1)] - takes_array::<{ [1, 2, 1 + 2] }>(); + takes_array::<{ core::direct_const_arg!([1, 2, 1 + 2]) }>(); //[r1]~^ ERROR: complex const arguments must be placed inside of a `const` block #[cfg(r2)] - takes_array::<{ [X; 3] }>(); + takes_array::<{ core::direct_const_arg!([X; 3]) }>(); //[r2]~^ ERROR: complex const arguments must be placed inside of a `const` block #[cfg(r3)] - takes_array::<{ [0; Y] }>(); + takes_array::<{ core::direct_const_arg!([0; Y]) }>(); //[r3]~^ ERROR: complex const arguments must be placed inside of a `const` block } diff --git a/tests/ui/const-generics/mgca/array_expr_arg_complex.rs b/tests/ui/const-generics/mgca/array_expr_arg_complex.rs index 6d57e4f4b9686..4adb2bf6e429d 100644 --- a/tests/ui/const-generics/mgca/array_expr_arg_complex.rs +++ b/tests/ui/const-generics/mgca/array_expr_arg_complex.rs @@ -9,8 +9,8 @@ fn takes_array() {} fn takes_tuple_with_array() {} fn generic_caller() { - takes_array::<{ [N, N + 1] }>(); //~ ERROR complex const arguments must be placed inside of a `const` block - takes_tuple_with_array::<{ ([N, N + 1], N) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block + takes_array::<{ core::direct_const_arg!([N, N + 1]) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block + takes_tuple_with_array::<{ core::direct_const_arg!(([N, N + 1], N)) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block } fn main() {} diff --git a/tests/ui/const-generics/mgca/array_expr_arg_complex.stderr b/tests/ui/const-generics/mgca/array_expr_arg_complex.stderr index f40d4b5035d86..c7b13351d453c 100644 --- a/tests/ui/const-generics/mgca/array_expr_arg_complex.stderr +++ b/tests/ui/const-generics/mgca/array_expr_arg_complex.stderr @@ -1,14 +1,14 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array_expr_arg_complex.rs:12:25 + --> $DIR/array_expr_arg_complex.rs:12:49 | -LL | takes_array::<{ [N, N + 1] }>(); - | ^^^^^ +LL | takes_array::<{ core::direct_const_arg!([N, N + 1]) }>(); + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/array_expr_arg_complex.rs:13:37 + --> $DIR/array_expr_arg_complex.rs:13:61 | -LL | takes_tuple_with_array::<{ ([N, N + 1], N) }>(); - | ^^^^^ +LL | takes_tuple_with_array::<{ core::direct_const_arg!(([N, N + 1], N)) }>(); + | ^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/const-generics/mgca/auxiliary/anon-const-def-id-on-const-arg-with-anon.rs b/tests/ui/const-generics/mgca/auxiliary/anon-const-def-id-on-const-arg-with-anon.rs new file mode 100644 index 0000000000000..c1ebfc18e0816 --- /dev/null +++ b/tests/ui/const-generics/mgca/auxiliary/anon-const-def-id-on-const-arg-with-anon.rs @@ -0,0 +1,11 @@ +#![feature(min_generic_const_args)] +#![allow(incomplete_features)] +pub struct S; +// this is a directly represented anon const... it's a bit weird. +// imagine `S<{ (2, const { 1 + 1 }) }>`. a directly represented tuple, containing an anon const. +// now, replace `(2, _)` with `_`. it's a directly represented anon const. +// this is different from const argument lowering failing to represent an argument directly and +// falling back to representing it as an anon const instead. +pub fn f() -> S<{ const { 1 + 1 } }> { + S +} diff --git a/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.rs b/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.rs index 7c7ffd9a9bd5f..e3d1f577d4f60 100644 --- a/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.rs +++ b/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.rs @@ -2,10 +2,11 @@ trait Iter< const FN: fn() = { - || { //~ ERROR complex const arguments must be placed inside of a `const` block + core::direct_const_arg!(|| { + //~^ ERROR complex const arguments must be placed inside of a `const` block use std::io::*; write!(_, "") - } + }) }, > { diff --git a/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.stderr b/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.stderr index 60774c4a3efea..96fcea9e906cf 100644 --- a/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.stderr +++ b/tests/ui/const-generics/mgca/bad-const-arg-fn-154539.stderr @@ -1,10 +1,12 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/bad-const-arg-fn-154539.rs:5:9 + --> $DIR/bad-const-arg-fn-154539.rs:5:33 | -LL | / || { +LL | core::direct_const_arg!(|| { + | _________________________________^ +LL | | LL | | use std::io::*; LL | | write!(_, "") -LL | | } +LL | | }) | |_________^ error: aborting due to 1 previous error diff --git a/tests/ui/const-generics/mgca/bad-direct-const-arg.rs b/tests/ui/const-generics/mgca/bad-direct-const-arg.rs new file mode 100644 index 0000000000000..451806ca6e1db --- /dev/null +++ b/tests/ui/const-generics/mgca/bad-direct-const-arg.rs @@ -0,0 +1,8 @@ +//! Simple error message test, nothing special here +#![feature(min_generic_const_args)] + +fn main(x: core::direct_const_arg!(2)) { + //~^ ERROR expected type, found `direct_const_arg!()` constant + let _ = core::direct_const_arg!(2); + //~^ ERROR expected expression, found `direct_const_arg!()` constant +} diff --git a/tests/ui/const-generics/mgca/bad-direct-const-arg.stderr b/tests/ui/const-generics/mgca/bad-direct-const-arg.stderr new file mode 100644 index 0000000000000..b77791ad5b3ee --- /dev/null +++ b/tests/ui/const-generics/mgca/bad-direct-const-arg.stderr @@ -0,0 +1,14 @@ +error: expected expression, found `direct_const_arg!()` constant + --> $DIR/bad-direct-const-arg.rs:6:13 + | +LL | let _ = core::direct_const_arg!(2); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: expected type, found `direct_const_arg!()` constant + --> $DIR/bad-direct-const-arg.rs:4:12 + | +LL | fn main(x: core::direct_const_arg!(2)) { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const-generics/mgca/direct-const-arg-feature-gate.rs b/tests/ui/const-generics/mgca/direct-const-arg-feature-gate.rs new file mode 100644 index 0000000000000..16ba349a29be6 --- /dev/null +++ b/tests/ui/const-generics/mgca/direct-const-arg-feature-gate.rs @@ -0,0 +1,5 @@ +fn foo(_: [(); core::direct_const_arg!(N)]) {} +//~^ ERROR use of unstable library feature `min_generic_const_args` +//~| ERROR expected expression, found `direct_const_arg!()` constant +//~| ERROR generic parameters may not be used in const operations +fn main() {} diff --git a/tests/ui/const-generics/mgca/direct-const-arg-feature-gate.stderr b/tests/ui/const-generics/mgca/direct-const-arg-feature-gate.stderr new file mode 100644 index 0000000000000..f5dc211c2f71a --- /dev/null +++ b/tests/ui/const-generics/mgca/direct-const-arg-feature-gate.stderr @@ -0,0 +1,28 @@ +error[E0658]: use of unstable library feature `min_generic_const_args` + --> $DIR/direct-const-arg-feature-gate.rs:1:32 + | +LL | fn foo(_: [(); core::direct_const_arg!(N)]) {} + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #132980 for more information + = help: add `#![feature(min_generic_const_args)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: generic parameters may not be used in const operations + --> $DIR/direct-const-arg-feature-gate.rs:1:56 + | +LL | fn foo(_: [(); core::direct_const_arg!(N)]) {} + | ^ cannot perform const operation using `N` + | + = help: const parameters may only be used as standalone arguments here, i.e. `N` + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + +error: expected expression, found `direct_const_arg!()` constant + --> $DIR/direct-const-arg-feature-gate.rs:1:32 + | +LL | fn foo(_: [(); core::direct_const_arg!(N)]) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/const-generics/mgca/direct-const-arg-multiple-exprs.rs b/tests/ui/const-generics/mgca/direct-const-arg-multiple-exprs.rs new file mode 100644 index 0000000000000..8c802e563db7c --- /dev/null +++ b/tests/ui/const-generics/mgca/direct-const-arg-multiple-exprs.rs @@ -0,0 +1,5 @@ +#![feature(min_generic_const_args)] +struct S; +fn foo(_: S) {} +//~^ ERROR direct_const_arg! takes 1 argument +fn main() {} diff --git a/tests/ui/const-generics/mgca/direct-const-arg-multiple-exprs.stderr b/tests/ui/const-generics/mgca/direct-const-arg-multiple-exprs.stderr new file mode 100644 index 0000000000000..6f54a563ffbfc --- /dev/null +++ b/tests/ui/const-generics/mgca/direct-const-arg-multiple-exprs.stderr @@ -0,0 +1,8 @@ +error: direct_const_arg! takes 1 argument + --> $DIR/direct-const-arg-multiple-exprs.rs:3:45 + | +LL | fn foo(_: S) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/mgca/double-wrapped-path-not-accidentally-stabilized.rs b/tests/ui/const-generics/mgca/double-wrapped-path-not-accidentally-stabilized.rs new file mode 100644 index 0000000000000..09bce5c7f9aa2 --- /dev/null +++ b/tests/ui/const-generics/mgca/double-wrapped-path-not-accidentally-stabilized.rs @@ -0,0 +1,15 @@ +//! When doing some refactorings in mGCA, it was easy to accidentally stabilize doubly-brace-wrapped +//! paths. Check to make sure we don't accidentally do so. +//! +//! Feel free to delete this test if/when mGCA is stabilized and we support this syntax on stable, +//! it's testing nothing useful beyond that point. + +fn f() {} + +fn g() { + f::<{ N }>(); // ok + f::<{ { N } }>(); + //~^ ERROR: generic parameters may not be used in const operations +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/double-wrapped-path-not-accidentally-stabilized.stderr b/tests/ui/const-generics/mgca/double-wrapped-path-not-accidentally-stabilized.stderr new file mode 100644 index 0000000000000..6168ec242a38c --- /dev/null +++ b/tests/ui/const-generics/mgca/double-wrapped-path-not-accidentally-stabilized.stderr @@ -0,0 +1,11 @@ +error: generic parameters may not be used in const operations + --> $DIR/double-wrapped-path-not-accidentally-stabilized.rs:11:13 + | +LL | f::<{ { N } }>(); + | ^ cannot perform const operation using `N` + | + = help: const parameters may only be used as standalone arguments here, i.e. `N` + = help: add `#![feature(generic_const_exprs)]` to allow generic const expressions + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/mgca/explicit_anon_consts.rs b/tests/ui/const-generics/mgca/explicit_anon_consts.rs index 2b9909b43dfbb..9b642b286fa3e 100644 --- a/tests/ui/const-generics/mgca/explicit_anon_consts.rs +++ b/tests/ui/const-generics/mgca/explicit_anon_consts.rs @@ -10,7 +10,7 @@ type Adt1 = Foo; type Adt2 = Foo<{ N }>; type Adt3 = Foo; //~^ ERROR: generic parameters may not be used in const operations -type Adt4 = Foo<{ 1 + 1 }>; +type Adt4 = Foo; //~^ ERROR: complex const arguments must be placed inside of a `const` block type Adt5 = Foo; @@ -18,7 +18,7 @@ type Arr = [(); N]; type Arr2 = [(); { N }]; type Arr3 = [(); const { N }]; //~^ ERROR: generic parameters may not be used in const operations -type Arr4 = [(); 1 + 1]; +type Arr4 = [(); core::direct_const_arg!(1 + 1)]; //~^ ERROR: complex const arguments must be placed inside of a `const` block type Arr5 = [(); const { 1 + 1 }]; @@ -27,7 +27,7 @@ fn repeats() -> [(); N] { let _2 = [(); { N }]; let _3 = [(); const { N }]; //~^ ERROR: generic parameters may not be used in const operations - let _4 = [(); 1 + 1]; + let _4 = [(); core::direct_const_arg!(1 + 1)]; //~^ ERROR: complex const arguments must be placed inside of a `const` block let _5 = [(); const { 1 + 1 }]; let _6: [(); const { N }] = todo!(); @@ -42,10 +42,10 @@ type const ITEM2: usize = { N }; type const ITEM3: usize = const { N }; //~^ ERROR: generic parameters may not be used in const operations -type const ITEM4: usize = { 1 + 1 }; +type const ITEM4: usize = core::direct_const_arg!(1 + 1); //~^ ERROR: complex const arguments must be placed inside of a `const` block -type const ITEM5: usize = const { 1 + 1}; +type const ITEM5: usize = const { 1 + 1 }; trait Trait { @@ -59,7 +59,7 @@ fn ace_bounds< T2: Trait, T3: Trait, //~^ ERROR: generic parameters may not be used in const operations - T4: Trait, + T4: Trait, //~^ ERROR: complex const arguments must be placed inside of a `const` block T5: Trait, >() {} @@ -68,6 +68,6 @@ struct Default1; struct Default2; struct Default3; //~^ ERROR: generic parameters may not be used in const operations -struct Default4; +struct Default4; //~^ ERROR: complex const arguments must be placed inside of a `const` block -struct Default5; +struct Default5; diff --git a/tests/ui/const-generics/mgca/explicit_anon_consts.stderr b/tests/ui/const-generics/mgca/explicit_anon_consts.stderr index f634ec1cf12e4..9ab6af010bf21 100644 --- a/tests/ui/const-generics/mgca/explicit_anon_consts.stderr +++ b/tests/ui/const-generics/mgca/explicit_anon_consts.stderr @@ -1,38 +1,38 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:13:35 + --> $DIR/explicit_anon_consts.rs:13:57 | -LL | type Adt4 = Foo<{ 1 + 1 }>; - | ^^^^^ +LL | type Adt4 = Foo; + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:21:34 + --> $DIR/explicit_anon_consts.rs:21:58 | -LL | type Arr4 = [(); 1 + 1]; - | ^^^^^ +LL | type Arr4 = [(); core::direct_const_arg!(1 + 1)]; + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:30:19 + --> $DIR/explicit_anon_consts.rs:30:43 | -LL | let _4 = [(); 1 + 1]; - | ^^^^^ +LL | let _4 = [(); core::direct_const_arg!(1 + 1)]; + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:45:45 + --> $DIR/explicit_anon_consts.rs:45:67 | -LL | type const ITEM4: usize = { 1 + 1 }; - | ^^^^^ +LL | type const ITEM4: usize = core::direct_const_arg!(1 + 1); + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:62:25 + --> $DIR/explicit_anon_consts.rs:62:49 | -LL | T4: Trait, - | ^^^^^ +LL | T4: Trait, + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/explicit_anon_consts.rs:71:52 + --> $DIR/explicit_anon_consts.rs:71:76 | -LL | struct Default4; - | ^^^^^ +LL | struct Default4; + | ^^^^^ error: generic parameters may not be used in const operations --> $DIR/explicit_anon_consts.rs:42:51 diff --git a/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.rs b/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.rs new file mode 100644 index 0000000000000..1653fd63ed3f8 --- /dev/null +++ b/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.rs @@ -0,0 +1,17 @@ +//! Diagnostics for expressions that contain things that must be a mGCA direct expression, *and* +//! things that must be an anon const, are currently less than ideal. This test merely asserts the +//! current (bad) state of diagnostics, so we can track improvements over time. + +#![feature(min_generic_const_args, min_adt_const_params)] +#![allow(incomplete_features)] + +fn f() {} + +fn g() { + f::<{ (N, 1 + 1) }>(); + //~^ ERROR: generic parameters may not be used in const operations + f::<{ core::direct_const_arg!((N, 1 + 1)) }>(); + //~^ ERROR: complex const arguments must be placed inside of a `const` block +} + +fn main() {} diff --git a/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.stderr b/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.stderr new file mode 100644 index 0000000000000..ae297de108493 --- /dev/null +++ b/tests/ui/const-generics/mgca/mixed-direct-anon-expression-diagnostics.stderr @@ -0,0 +1,16 @@ +error: complex const arguments must be placed inside of a `const` block + --> $DIR/mixed-direct-anon-expression-diagnostics.rs:13:39 + | +LL | f::<{ core::direct_const_arg!((N, 1 + 1)) }>(); + | ^^^^^ + +error: generic parameters may not be used in const operations + --> $DIR/mixed-direct-anon-expression-diagnostics.rs:11:12 + | +LL | f::<{ (N, 1 + 1) }>(); + | ^ + | + = help: add `#![feature(generic_const_args)]` to allow generic expressions as the RHS of const items + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const-generics/mgca/serialized-direct-const-with-anon-const-body.rs b/tests/ui/const-generics/mgca/serialized-direct-const-with-anon-const-body.rs new file mode 100644 index 0000000000000..985e14fddb676 --- /dev/null +++ b/tests/ui/const-generics/mgca/serialized-direct-const-with-anon-const-body.rs @@ -0,0 +1,16 @@ +//@ check-pass +//@ aux-build:anon-const-def-id-on-const-arg-with-anon.rs +//! The DefCollector sometimes generates "fake" DefKind::AnonConst DefIds for AST AnonConsts that +//! are not actually lowered to HIR AnonConsts, and so the DefId is placed on a hir::Node::ConstArg +//! (just to place it *somewhere*). These "fake" DefIds should not be serialized. Previously, the +//! logic to skip serializing them was incorrect (we were still serializing fake DefIds for +//! `ConstArg(ConstArgKind::Anon)` if the directly represented expression contained within it +//! *another*, unrelated, anon const). This test checks that case, a directly-represented +//! fake-anon-const directly containing another anon const. + +#![feature(min_generic_const_args)] +#![allow(incomplete_features)] +extern crate anon_const_def_id_on_const_arg_with_anon; +fn main() { + anon_const_def_id_on_const_arg_with_anon::f(); +} diff --git a/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs index 2e39f8952b11d..460c8af840a1c 100644 --- a/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs +++ b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.rs @@ -9,7 +9,7 @@ struct Point(u32, u32); fn with_point() {} fn test() { - with_point::<{ Point(N + 1, N) }>(); + with_point::<{ core::direct_const_arg!(Point(N + 1, N)) }>(); //~^ ERROR complex const arguments must be placed inside of a `const` block with_point::<{ Point(const { N + 1 }, N) }>(); diff --git a/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr index 3a873ec33fb19..a4e7cb94c57c3 100644 --- a/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr +++ b/tests/ui/const-generics/mgca/tuple_ctor_complex_args.stderr @@ -1,8 +1,8 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/tuple_ctor_complex_args.rs:12:26 + --> $DIR/tuple_ctor_complex_args.rs:12:50 | -LL | with_point::<{ Point(N + 1, N) }>(); - | ^^^^^ +LL | with_point::<{ core::direct_const_arg!(Point(N + 1, N)) }>(); + | ^^^^^ error: generic parameters may not be used in const operations --> $DIR/tuple_ctor_complex_args.rs:15:34 diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_complex.rs b/tests/ui/const-generics/mgca/tuple_expr_arg_complex.rs index d7cab17bad124..0d99b8ae345d6 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_complex.rs +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_complex.rs @@ -9,11 +9,11 @@ fn takes_tuple() {} fn takes_nested_tuple() {} fn generic_caller() { - takes_tuple::<{ (N, N + 1) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block - takes_tuple::<{ (N, T::ASSOC + 1) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block + takes_tuple::<{ core::direct_const_arg!((N, N + 1)) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block + takes_tuple::<{ core::direct_const_arg!((N, T::ASSOC + 1)) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block - takes_nested_tuple::<{ (N, (N, N + 1)) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block - takes_nested_tuple::<{ (N, (N, const { N + 1 })) }>(); //~ ERROR generic parameters may not be used in const operations + takes_nested_tuple::<{ core::direct_const_arg!((N, (N, N + 1))) }>(); //~ ERROR complex const arguments must be placed inside of a `const` block + takes_nested_tuple::<{ core::direct_const_arg!((N, (N, const { N + 1 }))) }>(); //~ ERROR generic parameters may not be used in const operations } fn main() {} diff --git a/tests/ui/const-generics/mgca/tuple_expr_arg_complex.stderr b/tests/ui/const-generics/mgca/tuple_expr_arg_complex.stderr index a9d412964da29..4bed120284c08 100644 --- a/tests/ui/const-generics/mgca/tuple_expr_arg_complex.stderr +++ b/tests/ui/const-generics/mgca/tuple_expr_arg_complex.stderr @@ -1,26 +1,26 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/tuple_expr_arg_complex.rs:12:25 + --> $DIR/tuple_expr_arg_complex.rs:12:49 | -LL | takes_tuple::<{ (N, N + 1) }>(); - | ^^^^^ +LL | takes_tuple::<{ core::direct_const_arg!((N, N + 1)) }>(); + | ^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/tuple_expr_arg_complex.rs:13:25 + --> $DIR/tuple_expr_arg_complex.rs:13:49 | -LL | takes_tuple::<{ (N, T::ASSOC + 1) }>(); - | ^^^^^^^^^^^^ +LL | takes_tuple::<{ core::direct_const_arg!((N, T::ASSOC + 1)) }>(); + | ^^^^^^^^^^^^ error: complex const arguments must be placed inside of a `const` block - --> $DIR/tuple_expr_arg_complex.rs:15:36 + --> $DIR/tuple_expr_arg_complex.rs:15:60 | -LL | takes_nested_tuple::<{ (N, (N, N + 1)) }>(); - | ^^^^^ +LL | takes_nested_tuple::<{ core::direct_const_arg!((N, (N, N + 1))) }>(); + | ^^^^^ error: generic parameters may not be used in const operations - --> $DIR/tuple_expr_arg_complex.rs:16:44 + --> $DIR/tuple_expr_arg_complex.rs:16:68 | -LL | takes_nested_tuple::<{ (N, (N, const { N + 1 })) }>(); - | ^ +LL | takes_nested_tuple::<{ core::direct_const_arg!((N, (N, const { N + 1 }))) }>(); + | ^ | = help: add `#![feature(generic_const_args)]` to allow generic expressions as the RHS of const items diff --git a/tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.stderr b/tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.stderr index d0339d09cdc7a..52ef108a84dbf 100644 --- a/tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.stderr +++ b/tests/ui/const-generics/mgca/type-const-free-anon-const-mismatch.stderr @@ -4,7 +4,7 @@ error[E0284]: type annotations needed LL | type const X: usize = const { N }; | ^^^^^^^^^^^^^^^^^^^ cannot infer the value of the constant `_` | - = note: cannot satisfy `X::{constant#0} == _` + = note: cannot satisfy `X::{constant#0}::{constant#0} == _` error: the constant `"this isn't a usize"` is not of type `usize` --> $DIR/type-const-free-anon-const-mismatch.rs:8:1 diff --git a/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.next.stderr b/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.next.stderr index d96726829ee0b..3ef6ede90d933 100644 --- a/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.next.stderr +++ b/tests/ui/const-generics/mgca/type-const-free-value-type-mismatch.next.stderr @@ -10,7 +10,7 @@ error[E0284]: type annotations needed LL | fn f() -> [u8; const { N }] {} | ^^^^^^^^^^^^^^^^^ cannot infer the value of the constant `_` | - = note: cannot satisfy `f::{constant#0} == _` + = note: cannot satisfy `f::{constant#0}::{constant#0} == _` error[E0308]: mismatched types --> $DIR/type-const-free-value-type-mismatch.rs:11:11 diff --git a/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.next.stderr b/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.next.stderr index eeabde2d06320..551a1d496e910 100644 --- a/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.next.stderr +++ b/tests/ui/const-generics/mgca/type-const-inherent-value-type-mismatch.next.stderr @@ -4,7 +4,7 @@ error[E0284]: type annotations needed LL | fn f() -> [u8; const { Struct::N }] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer the value of the constant `_` | - = note: cannot satisfy `f::{constant#0} == _` + = note: cannot satisfy `f::{constant#0}::{constant#0} == _` error: the constant `"this isn't a usize"` is not of type `usize` --> $DIR/type-const-inherent-value-type-mismatch.rs:13:5 diff --git a/tests/ui/const-generics/mgca/type-const-value-type-mismatch.next.stderr b/tests/ui/const-generics/mgca/type-const-value-type-mismatch.next.stderr index 123c442a3938d..748ffe1294e0a 100644 --- a/tests/ui/const-generics/mgca/type-const-value-type-mismatch.next.stderr +++ b/tests/ui/const-generics/mgca/type-const-value-type-mismatch.next.stderr @@ -16,7 +16,7 @@ error[E0284]: type annotations needed LL | fn arr() -> [u8; const { Self::LEN }] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer the value of the constant `_` | - = note: cannot satisfy `::arr::{constant#0} == _` + = note: cannot satisfy `::arr::{constant#0}::{constant#0} == _` error[E0308]: mismatched types --> $DIR/type-const-value-type-mismatch.rs:21:17 diff --git a/tests/ui/delegation/generics/const-predicates-ice-158675.rs b/tests/ui/delegation/generics/const-predicates-ice-158675.rs new file mode 100644 index 0000000000000..2e88ca8135534 --- /dev/null +++ b/tests/ui/delegation/generics/const-predicates-ice-158675.rs @@ -0,0 +1,30 @@ +#![feature(fn_delegation)] + +mod original_ice { + struct S; + + trait Trait { + fn fun(); + } + + impl S { + reuse Trait::, N>::fun; + //~^ ERROR: the constant `N` is not of type `bool` + } +} + +mod with_child_constants { + struct S; + + trait Trait { + fn fun(); + } + + impl S { + reuse Trait::, N>::fun::; + //~^ ERROR: the constant `N` is not of type `bool` + //~| ERROR: the constant `N` is not of type `char` + } +} + +fn main() {} diff --git a/tests/ui/delegation/generics/const-predicates-ice-158675.stderr b/tests/ui/delegation/generics/const-predicates-ice-158675.stderr new file mode 100644 index 0000000000000..6dbf017294822 --- /dev/null +++ b/tests/ui/delegation/generics/const-predicates-ice-158675.stderr @@ -0,0 +1,42 @@ +error: the constant `N` is not of type `bool` + --> $DIR/const-predicates-ice-158675.rs:11:29 + | +LL | reuse Trait::, N>::fun; + | ^ expected `bool`, found `usize` + | +note: required by a const generic parameter in `original_ice::Trait::fun` + --> $DIR/const-predicates-ice-158675.rs:6:20 + | +LL | trait Trait { + | ^^^^^^^^^^^^^ required by this const generic parameter in `Trait::fun` +LL | fn fun(); + | --- required by a bound in this associated function + +error: the constant `N` is not of type `bool` + --> $DIR/const-predicates-ice-158675.rs:24:29 + | +LL | reuse Trait::, N>::fun::; + | ^ expected `bool`, found `usize` + | +note: required by a const generic parameter in `with_child_constants::Trait::fun` + --> $DIR/const-predicates-ice-158675.rs:19:20 + | +LL | trait Trait { + | ^^^^^^^^^^^^^ required by this const generic parameter in `Trait::fun` +LL | fn fun(); + | --- required by a bound in this associated function + +error: the constant `N` is not of type `char` + --> $DIR/const-predicates-ice-158675.rs:24:39 + | +LL | reuse Trait::, N>::fun::; + | ^ expected `char`, found `usize` + | +note: required by a const generic parameter in `with_child_constants::Trait::fun` + --> $DIR/const-predicates-ice-158675.rs:20:16 + | +LL | fn fun(); + | ^^^^^^^^^^^^^ required by this const generic parameter in `Trait::fun` + +error: aborting due to 3 previous errors + diff --git a/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155125.stderr b/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155125.stderr index d2f0e87ecb593..6675831bddc86 100644 --- a/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155125.stderr +++ b/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155125.stderr @@ -9,13 +9,14 @@ LL | reuse foo; = note: `foo` must be defined only once in the value namespace of this block error: complex const arguments must be placed inside of a `const` block - --> $DIR/hir-crate-items-before-lowering-ices.rs:10:13 + --> $DIR/hir-crate-items-before-lowering-ices.rs:10:37 | -LL | / { +LL | core::direct_const_arg!({ + | _____________________________________^ LL | | fn foo() {} LL | | reuse foo; LL | | 2 -LL | | }, +LL | | }), | |_____________^ error: aborting due to 2 previous errors diff --git a/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155164.stderr b/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155164.stderr index 34d1a92ccd225..6164dabe74bff 100644 --- a/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155164.stderr +++ b/tests/ui/delegation/hir-crate-items-before-lowering-ices.ice_155164.stderr @@ -1,12 +1,13 @@ error: complex const arguments must be placed inside of a `const` block - --> $DIR/hir-crate-items-before-lowering-ices.rs:47:13 + --> $DIR/hir-crate-items-before-lowering-ices.rs:47:37 | -LL | / { +LL | core::direct_const_arg!({ + | _____________________________________^ LL | | LL | | struct W; LL | | impl W { ... | -LL | | }, +LL | | }), | |_____________^ error: aborting due to 1 previous error diff --git a/tests/ui/delegation/hir-crate-items-before-lowering-ices.rs b/tests/ui/delegation/hir-crate-items-before-lowering-ices.rs index b9a7a73732cf3..07f5a1ad1712f 100644 --- a/tests/ui/delegation/hir-crate-items-before-lowering-ices.rs +++ b/tests/ui/delegation/hir-crate-items-before-lowering-ices.rs @@ -7,11 +7,11 @@ mod ice_155125 { struct S; impl S< - { //[ice_155125]~ ERROR: complex const arguments must be placed inside of a `const` block + core::direct_const_arg!({ //[ice_155125]~ ERROR: complex const arguments must be placed inside of a `const` block fn foo() {} reuse foo; //[ice_155125]~ ERROR: the name `foo` is defined multiple times 2 - }, + }), > { } @@ -44,13 +44,13 @@ mod ice_155128 { mod ice_155164 { struct X { inner: std::iter::Map< - { + core::direct_const_arg!({ //[ice_155164]~^ ERROR: complex const arguments must be placed inside of a `const` block struct W; impl W { reuse Iterator::fold; } - }, + }), F, >, } diff --git a/tests/ui/delegation/inside-const-body-ice-155300.rs b/tests/ui/delegation/inside-const-body-ice-155300.rs index 06addf7e5412d..2d13007bc807b 100644 --- a/tests/ui/delegation/inside-const-body-ice-155300.rs +++ b/tests/ui/delegation/inside-const-body-ice-155300.rs @@ -5,12 +5,13 @@ pub struct S; impl S< - { //~ ERROR: complex const arguments must be placed inside of a `const` block + core::direct_const_arg!({ + //~^ ERROR: complex const arguments must be placed inside of a `const` block fn foo() {} reuse foo::<> as bar; reuse bar; //~^ ERROR: the name `bar` is defined multiple times - }, + }), > { } diff --git a/tests/ui/delegation/inside-const-body-ice-155300.stderr b/tests/ui/delegation/inside-const-body-ice-155300.stderr index 0d2020c997200..f0cb830109f8a 100644 --- a/tests/ui/delegation/inside-const-body-ice-155300.stderr +++ b/tests/ui/delegation/inside-const-body-ice-155300.stderr @@ -1,5 +1,5 @@ error[E0428]: the name `bar` is defined multiple times - --> $DIR/inside-const-body-ice-155300.rs:11:13 + --> $DIR/inside-const-body-ice-155300.rs:12:13 | LL | reuse foo::<> as bar; | --------------------- previous definition of the value `bar` here @@ -9,14 +9,15 @@ LL | reuse bar; = note: `bar` must be defined only once in the value namespace of this block error: complex const arguments must be placed inside of a `const` block - --> $DIR/inside-const-body-ice-155300.rs:8:9 + --> $DIR/inside-const-body-ice-155300.rs:8:33 | -LL | / { +LL | core::direct_const_arg!({ + | _________________________________^ +LL | | LL | | fn foo() {} LL | | reuse foo::<> as bar; -LL | | reuse bar; -LL | | -LL | | }, +... | +LL | | }), | |_________^ error: aborting due to 2 previous errors diff --git a/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.current.stderr b/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.current.stderr new file mode 100644 index 0000000000000..f9e33ff4fac58 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.current.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `X: const Y<_>` is not satisfied + --> $DIR/generic_parameters_handling.rs:22:6 + | +LL | .blah(); + | ^^^^ + | + = note: Self = X, Z = Z, A = u8, B = &str + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.next.stderr b/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.next.stderr new file mode 100644 index 0000000000000..f9e33ff4fac58 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.next.stderr @@ -0,0 +1,11 @@ +error[E0277]: the trait bound `X: const Y<_>` is not satisfied + --> $DIR/generic_parameters_handling.rs:22:6 + | +LL | .blah(); + | ^^^^ + | + = note: Self = X, Z = Z, A = u8, B = &str + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.rs b/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.rs new file mode 100644 index 0000000000000..32a0caf96988a --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/generic_parameters_handling.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +#![crate_type = "lib"] +#![feature(diagnostic_on_const, const_trait_impl)] + +pub struct X { + field: (A, B), +} + +pub const trait Y { + fn blah(&self) {} +} + +#[diagnostic::on_const(note = "Self = {Self}, Z = {Z}, A = {A}, B = {B}")] +impl Y for X {} + +const _: () = { + X { + field: (42_u8, "hello"), + } + .blah(); + //~^ ERROR the trait bound `X: const Y<_>` is not satisfied [E0277] + //~| NOTE Self = X, Z = Z, A = u8, B = &str +}; diff --git a/tests/ui/diagnostic_namespace/on_const/malformed_format_literals.rs b/tests/ui/diagnostic_namespace/on_const/malformed_format_literals.rs new file mode 100644 index 0000000000000..0c36ce25febb5 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/malformed_format_literals.rs @@ -0,0 +1,25 @@ +#![crate_type = "lib"] +#![feature(diagnostic_on_const)] +#![feature(const_trait_impl)] + +pub struct X; + +const trait Y { + fn blah(&self) {} +} + +#[diagnostic::on_const( + message = "my message {Foo}", + //~^ WARN unknown parameter `Foo` + label = "my label {Bar}", + //~^ WARN unknown parameter `Bar` + note = "my label {Baz}", + //~^ WARN unknown parameter `Baz` +)] +impl Y for X {} + +const _: () = { + X {}.blah(); + //~^ ERROR my message {Foo} [E0277] + +}; diff --git a/tests/ui/diagnostic_namespace/on_const/malformed_format_literals.stderr b/tests/ui/diagnostic_namespace/on_const/malformed_format_literals.stderr new file mode 100644 index 0000000000000..48bdc2c239a85 --- /dev/null +++ b/tests/ui/diagnostic_namespace/on_const/malformed_format_literals.stderr @@ -0,0 +1,36 @@ +warning: unknown parameter `Foo` + --> $DIR/malformed_format_literals.rs:12:28 + | +LL | message = "my message {Foo}", + | ^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + = note: `#[warn(malformed_diagnostic_format_literals)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default + +warning: unknown parameter `Bar` + --> $DIR/malformed_format_literals.rs:14:24 + | +LL | label = "my label {Bar}", + | ^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: unknown parameter `Baz` + --> $DIR/malformed_format_literals.rs:16:23 + | +LL | note = "my label {Baz}", + | ^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +error[E0277]: my message {Foo} + --> $DIR/malformed_format_literals.rs:22:10 + | +LL | X {}.blah(); + | ^^^^ my label {Bar} + | + = note: my label {Baz} + +error: aborting due to 1 previous error; 3 warnings emitted + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/eii/default/auxiliary/decl_with_default.rs b/tests/ui/eii/default/auxiliary/decl_with_default.rs index ba855cb854afd..8d962c19c94d9 100644 --- a/tests/ui/eii/default/auxiliary/decl_with_default.rs +++ b/tests/ui/eii/default/auxiliary/decl_with_default.rs @@ -1,3 +1,4 @@ +//@ no-prefer-dynamic #![crate_type = "rlib"] #![feature(extern_item_impls)] diff --git a/tests/ui/eii/default/auxiliary/impl1.rs b/tests/ui/eii/default/auxiliary/impl1.rs index 84edf24e12816..3510ea1eb3f27 100644 --- a/tests/ui/eii/default/auxiliary/impl1.rs +++ b/tests/ui/eii/default/auxiliary/impl1.rs @@ -1,3 +1,4 @@ +//@ no-prefer-dynamic //@ aux-build: decl_with_default.rs #![crate_type = "rlib"] #![feature(extern_item_impls)] diff --git a/tests/ui/eii/duplicate/auxiliary/dylib_default.rs b/tests/ui/eii/duplicate/auxiliary/dylib_default.rs new file mode 100644 index 0000000000000..d1136b0ebd636 --- /dev/null +++ b/tests/ui/eii/duplicate/auxiliary/dylib_default.rs @@ -0,0 +1,5 @@ +#![crate_type = "dylib"] +#![feature(extern_item_impls)] + +#[eii(eii1)] +fn decl1(x: u64) {} diff --git a/tests/ui/eii/duplicate/dylib_default_duplicate.rs b/tests/ui/eii/duplicate/dylib_default_duplicate.rs new file mode 100644 index 0000000000000..0ac3669715f85 --- /dev/null +++ b/tests/ui/eii/duplicate/dylib_default_duplicate.rs @@ -0,0 +1,20 @@ +//@ aux-build: dylib_default.rs +//@ needs-crate-type: dylib +//@ compile-flags: --emit link +//@ ignore-backends: gcc +// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 +//@ ignore-windows +// Regression test for https://github.com/rust-lang/rust/issues/156320. +// A default implementation from an upstream dylib has already been selected and +// must not be overridden by a downstream explicit implementation. +#![feature(extern_item_impls)] + +extern crate dylib_default; + +#[unsafe(dylib_default::eii1)] +fn other(x: u64) { + //~^ ERROR multiple implementations of `#[eii1]` + println!("1{x}"); +} + +fn main() {} diff --git a/tests/ui/eii/duplicate/dylib_default_duplicate.stderr b/tests/ui/eii/duplicate/dylib_default_duplicate.stderr new file mode 100644 index 0000000000000..04c6a13710e28 --- /dev/null +++ b/tests/ui/eii/duplicate/dylib_default_duplicate.stderr @@ -0,0 +1,15 @@ +error: multiple implementations of `#[eii1]` + --> $DIR/dylib_default_duplicate.rs:15:1 + | +LL | fn other(x: u64) { + | ^^^^^^^^^^^^^^^^ first implemented here in crate `dylib_default_duplicate` + | + ::: $DIR/auxiliary/dylib_default.rs:5:1 + | +LL | fn decl1(x: u64) {} + | ---------------- also implemented here in crate `dylib_default` + | + = help: an "externally implementable item" can only have a single implementation in the final artifact. When multiple implementations are found, also in different crates, they conflict + +error: aborting due to 1 previous error + diff --git a/tests/ui/layout/debug.stderr b/tests/ui/layout/debug.stderr index f08d1200b9fbc..74d2bf16190de 100644 --- a/tests/ui/layout/debug.stderr +++ b/tests/ui/layout/debug.stderr @@ -102,22 +102,23 @@ error: layout_of(S) = Layout { align: AbiAlign { abi: Align(4 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, true, ), valid_range: 0..=4294967295, }, - Initialized { + b: Initialized { value: Int( I32, true, ), valid_range: 0..=4294967295, }, - ), + b_offset: Size(4 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -174,22 +175,23 @@ error: layout_of(Result) = Layout { align: AbiAlign { abi: Align(4 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Initialized { + b: Initialized { value: Int( I32, true, ), valid_range: 0..=4294967295, }, - ), + b_offset: Size(4 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -222,22 +224,23 @@ error: layout_of(Result) = Layout { variants: [ VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Initialized { + b: Initialized { value: Int( I32, true, ), valid_range: 0..=4294967295, }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], @@ -249,22 +252,23 @@ error: layout_of(Result) = Layout { }, VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Initialized { + b: Initialized { value: Int( I32, true, ), valid_range: 0..=4294967295, }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], diff --git a/tests/ui/layout/enum-scalar-pair-int-ptr.rs b/tests/ui/layout/enum-scalar-pair-int-ptr.rs index 184f61fe79653..ff16c7e7c3af8 100644 --- a/tests/ui/layout/enum-scalar-pair-int-ptr.rs +++ b/tests/ui/layout/enum-scalar-pair-int-ptr.rs @@ -1,6 +1,7 @@ //@ normalize-stderr: "pref: Align\([1-8] bytes\)" -> "pref: $$PREF_ALIGN" //@ normalize-stderr: "Int\(I[0-9]+," -> "Int(I?," //@ normalize-stderr: "valid_range: 0..=[0-9]+" -> "valid_range: $$VALID_RANGE" +//@ normalize-stderr: "b_offset: Size\([0-9]+ bytes\)" -> "b_offset: Size(? bytes)" //! Enum layout tests related to scalar pairs with an int/ptr common primitive. diff --git a/tests/ui/layout/enum-scalar-pair-int-ptr.stderr b/tests/ui/layout/enum-scalar-pair-int-ptr.stderr index 5d54fd432371e..afba5a24ee23d 100644 --- a/tests/ui/layout/enum-scalar-pair-int-ptr.stderr +++ b/tests/ui/layout/enum-scalar-pair-int-ptr.stderr @@ -1,11 +1,11 @@ -error: backend_repr: ScalarPair(Initialized { value: Int(I?, false), valid_range: $VALID_RANGE }, Initialized { value: Pointer(AddressSpace(0)), valid_range: $VALID_RANGE }) - --> $DIR/enum-scalar-pair-int-ptr.rs:12:1 +error: backend_repr: ScalarPair { a: Initialized { value: Int(I?, false), valid_range: $VALID_RANGE }, b: Initialized { value: Pointer(AddressSpace(0)), valid_range: $VALID_RANGE }, b_offset: Size(? bytes) } + --> $DIR/enum-scalar-pair-int-ptr.rs:13:1 | LL | enum ScalarPairPointerWithInt { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: backend_repr: Memory { sized: true } - --> $DIR/enum-scalar-pair-int-ptr.rs:21:1 + --> $DIR/enum-scalar-pair-int-ptr.rs:22:1 | LL | enum NotScalarPairPointerWithSmallerInt { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/layout/enum.stderr b/tests/ui/layout/enum.stderr index 3b41129456dda..e9732d9373a48 100644 --- a/tests/ui/layout/enum.stderr +++ b/tests/ui/layout/enum.stderr @@ -10,7 +10,7 @@ error: size: Size(16 bytes) LL | enum UninhabitedVariantSpace { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: backend_repr: ScalarPair(Initialized { value: Int(I8, false), valid_range: 0..=1 }, Initialized { value: Int(I8, false), valid_range: 0..=255 }) +error: backend_repr: ScalarPair { a: Initialized { value: Int(I8, false), valid_range: 0..=1 }, b: Initialized { value: Int(I8, false), valid_range: 0..=255 }, b_offset: Size(1 bytes) } --> $DIR/enum.rs:21:1 | LL | enum ScalarPairDifferingSign { diff --git a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr index 7e6294f894c3e..021d7b36ac5de 100644 --- a/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr +++ b/tests/ui/layout/issue-96158-scalarpair-payload-might-be-uninit.stderr @@ -3,21 +3,22 @@ error: layout_of(MissingPayloadField) = Layout { align: AbiAlign { abi: Align(1 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -50,21 +51,22 @@ error: layout_of(MissingPayloadField) = Layout { variants: [ VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], @@ -100,22 +102,23 @@ error: layout_of(CommonPayloadField) = Layout { align: AbiAlign { abi: Align(1 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Initialized { + b: Initialized { value: Int( I8, false, ), valid_range: 0..=255, }, - ), + b_offset: Size(1 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -148,22 +151,23 @@ error: layout_of(CommonPayloadField) = Layout { variants: [ VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Initialized { + b: Initialized { value: Int( I8, false, ), valid_range: 0..=255, }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], @@ -175,22 +179,23 @@ error: layout_of(CommonPayloadField) = Layout { }, VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Initialized { + b: Initialized { value: Int( I8, false, ), valid_range: 0..=255, }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], @@ -216,21 +221,22 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { align: AbiAlign { abi: Align(1 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -263,21 +269,22 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { variants: [ VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], @@ -289,21 +296,22 @@ error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout { }, VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], @@ -329,21 +337,22 @@ error: layout_of(NicheFirst) = Layout { align: AbiAlign { abi: Align(1 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=4, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -380,22 +389,23 @@ error: layout_of(NicheFirst) = Layout { variants: [ VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=2, }, - Initialized { + b: Initialized { value: Int( I8, false, ), valid_range: 0..=255, }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(0 bytes), Size(1 bytes), @@ -452,21 +462,22 @@ error: layout_of(NicheSecond) = Layout { align: AbiAlign { abi: Align(1 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=4, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -503,22 +514,23 @@ error: layout_of(NicheSecond) = Layout { variants: [ VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=2, }, - Initialized { + b: Initialized { value: Int( I8, false, ), valid_range: 0..=255, }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), Size(0 bytes), diff --git a/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr b/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr index 28fafa7800305..3b94d3751f227 100644 --- a/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr +++ b/tests/ui/repr/repr-c-dead-variants.aarch64-unknown-linux-gnu.stderr @@ -78,21 +78,22 @@ error: layout_of(TwoVariants) = Layout { align: AbiAlign { abi: Align(4 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -125,21 +126,22 @@ error: layout_of(TwoVariants) = Layout { variants: [ VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], @@ -151,21 +153,22 @@ error: layout_of(TwoVariants) = Layout { }, VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], diff --git a/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr b/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr index 45193552b507f..757bbba9c381a 100644 --- a/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr +++ b/tests/ui/repr/repr-c-dead-variants.armebv7r-none-eabi.stderr @@ -78,21 +78,22 @@ error: layout_of(TwoVariants) = Layout { align: AbiAlign { abi: Align(1 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -125,21 +126,22 @@ error: layout_of(TwoVariants) = Layout { variants: [ VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], @@ -151,21 +153,22 @@ error: layout_of(TwoVariants) = Layout { }, VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], diff --git a/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr b/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr index 28fafa7800305..3b94d3751f227 100644 --- a/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr +++ b/tests/ui/repr/repr-c-dead-variants.i686-pc-windows-msvc.stderr @@ -78,21 +78,22 @@ error: layout_of(TwoVariants) = Layout { align: AbiAlign { abi: Align(4 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -125,21 +126,22 @@ error: layout_of(TwoVariants) = Layout { variants: [ VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], @@ -151,21 +153,22 @@ error: layout_of(TwoVariants) = Layout { }, VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], diff --git a/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr b/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr index 28fafa7800305..3b94d3751f227 100644 --- a/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr +++ b/tests/ui/repr/repr-c-dead-variants.x86_64-unknown-linux-gnu.stderr @@ -78,21 +78,22 @@ error: layout_of(TwoVariants) = Layout { align: AbiAlign { abi: Align(4 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -125,21 +126,22 @@ error: layout_of(TwoVariants) = Layout { variants: [ VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], @@ -151,21 +153,22 @@ error: layout_of(TwoVariants) = Layout { }, VariantLayout { size: Size(8 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I32, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(4 bytes), + }, field_offsets: [ Size(4 bytes), ], diff --git a/tests/ui/repr/repr-c-int-dead-variants.stderr b/tests/ui/repr/repr-c-int-dead-variants.stderr index c2f7fec38c81f..13051fbdaf493 100644 --- a/tests/ui/repr/repr-c-int-dead-variants.stderr +++ b/tests/ui/repr/repr-c-int-dead-variants.stderr @@ -78,21 +78,22 @@ error: layout_of(TwoVariantsU8) = Layout { align: AbiAlign { abi: Align(1 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes), @@ -125,21 +126,22 @@ error: layout_of(TwoVariantsU8) = Layout { variants: [ VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], @@ -151,21 +153,22 @@ error: layout_of(TwoVariantsU8) = Layout { }, VariantLayout { size: Size(2 bytes), - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Int( I8, false, ), valid_range: 0..=1, }, - Union { + b: Union { value: Int( I8, false, ), }, - ), + b_offset: Size(1 bytes), + }, field_offsets: [ Size(1 bytes), ], diff --git a/tests/ui/splat/arg-count-ice-issue-158482.rs b/tests/ui/splat/arg-count-ice-issue-158482.rs new file mode 100644 index 0000000000000..27f036b8a5906 --- /dev/null +++ b/tests/ui/splat/arg-count-ice-issue-158482.rs @@ -0,0 +1,14 @@ +//! Checks that an incorrect number of arguments to splat doesn't panic. + +#![feature(splat)] +struct Foo {} +trait BarTrait { + fn trait_assoc(w: W, #[splat] _s: (u32, u8)); +} +impl BarTrait for Foo { + fn trait_assoc(_w: W, #[splat] _s: (u32, u8)) {} +} +fn main() { + Foo::trait_assoc() + //~^ ERROR: this splatted function takes 3 arguments, but 0 were provided +} diff --git a/tests/ui/splat/arg-count-ice-issue-158482.stderr b/tests/ui/splat/arg-count-ice-issue-158482.stderr new file mode 100644 index 0000000000000..b7f3a8070f2b2 --- /dev/null +++ b/tests/ui/splat/arg-count-ice-issue-158482.stderr @@ -0,0 +1,9 @@ +error[E0057]: this splatted function takes 3 arguments, but 0 were provided + --> $DIR/arg-count-ice-issue-158482.rs:12:5 + | +LL | Foo::trait_assoc() + | ^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0057`. diff --git a/tests/ui/splat/reject-closure-issue-158605.rs b/tests/ui/splat/reject-closure-issue-158605.rs new file mode 100644 index 0000000000000..7464bab2a77d2 --- /dev/null +++ b/tests/ui/splat/reject-closure-issue-158605.rs @@ -0,0 +1,30 @@ +//! Checks that closures and rust-call functions can't be splatted. +//! This should be rejected until we decide on sensible semantics. + +#![feature(splat, unboxed_closures, tuple_trait)] +#![expect(incomplete_features)] + +use std::marker::Tuple; + +trait Trait: Tuple + Sized { + extern "rust-call" fn method(#[splat] self: Self); + //~^ ERROR: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI +} + +impl Trait for (i32, i64) { + extern "rust-call" fn method(#[splat] self: Self) { + //~^ ERROR: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + println!("{self:?}"); + } +} + +fn main() { + (|#[splat] x: i32| { + //~^ ERROR `#[splat]` is not allowed on closure arguments + println!("{x}"); + })(1); + + (1_i32, 2_i64).method(); + Trait::method(3_i32, 4_i64); + //~^ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument +} diff --git a/tests/ui/splat/reject-closure-issue-158605.stderr b/tests/ui/splat/reject-closure-issue-158605.stderr new file mode 100644 index 0000000000000..c23211459a0db --- /dev/null +++ b/tests/ui/splat/reject-closure-issue-158605.stderr @@ -0,0 +1,36 @@ +error: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + --> $DIR/reject-closure-issue-158605.rs:10:5 + | +LL | extern "rust-call" fn method(#[splat] self: Self); + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^ + | + = help: remove `#[splat]` or change the ABI + +error: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + --> $DIR/reject-closure-issue-158605.rs:15:5 + | +LL | extern "rust-call" fn method(#[splat] self: Self) { + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^ + | + = help: remove `#[splat]` or change the ABI + +error: `#[splat]` is not allowed on closure arguments + --> $DIR/reject-closure-issue-158605.rs:22:7 + | +LL | (|#[splat] x: i32| { + | _______^^^^^^^^_________^ +LL | | +LL | | println!("{x}"); +LL | | })(1); + | |_____^ + | + = help: remove `#[splat]` or turn the closure into a function + +error: functions with the "rust-call" ABI must take a single non-self tuple argument + --> $DIR/reject-closure-issue-158605.rs:28:26 + | +LL | Trait::method(3_i32, 4_i64); + | ^^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/splat/splat-255-limit-fail.rs b/tests/ui/splat/splat-255-limit-fail.rs index 31767f11d999b..f19ce37bc4566 100644 --- a/tests/ui/splat/splat-255-limit-fail.rs +++ b/tests/ui/splat/splat-255-limit-fail.rs @@ -47,7 +47,7 @@ fn s_255_terminal( _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, - #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 255 + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 255 ) {} #[rustfmt::skip] @@ -68,7 +68,7 @@ fn s_256_terminal( _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, - #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 256 + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 256 ) {} #[rustfmt::skip] @@ -88,7 +88,7 @@ fn s_255_non_terminal( _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, - #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 255 + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 255 _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, ) {} @@ -110,7 +110,7 @@ fn s_256_non_terminal( _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, _: A, - #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is not supported on argument index 256 + #[splat] (_a, _b): (u32, i8), //~ ERROR `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 256 _: A, ) {} diff --git a/tests/ui/splat/splat-255-limit-fail.stderr b/tests/ui/splat/splat-255-limit-fail.stderr index da62da8aa605b..a3a0b9cf3d350 100644 --- a/tests/ui/splat/splat-255-limit-fail.stderr +++ b/tests/ui/splat/splat-255-limit-fail.stderr @@ -1,32 +1,32 @@ -error: `#[splat]` is not supported on argument index 255 +error: `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 255 --> $DIR/splat-255-limit-fail.rs:50:5 | LL | #[splat] (_a, _b): (u32, i8), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | ^^^^^^^^ `#[splat]` is not supported here | = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list -error: `#[splat]` is not supported on argument index 256 +error: `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 256 --> $DIR/splat-255-limit-fail.rs:71:5 | LL | #[splat] (_a, _b): (u32, i8), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | ^^^^^^^^ `#[splat]` is not supported here | = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list -error: `#[splat]` is not supported on argument index 255 +error: `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 255 --> $DIR/splat-255-limit-fail.rs:91:5 | LL | #[splat] (_a, _b): (u32, i8), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | ^^^^^^^^ `#[splat]` is not supported here | = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list -error: `#[splat]` is not supported on argument index 256 +error: `#[splat]` is only supported on argument index 254 or less, this `#[splat]` is on index 256 --> $DIR/splat-255-limit-fail.rs:113:5 | LL | #[splat] (_a, _b): (u32, i8), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `#[splat]` is not supported here + | ^^^^^^^^ `#[splat]` is not supported here | = help: remove `#[splat]`, or use it on an argument closer to the start of the argument list diff --git a/tests/ui/splat/splat-async-fn-tuple-fail.rs b/tests/ui/splat/splat-async-fn-tuple-fail.rs index a1f67dfe606d6..308a6c4a2131e 100644 --- a/tests/ui/splat/splat-async-fn-tuple-fail.rs +++ b/tests/ui/splat/splat-async-fn-tuple-fail.rs @@ -8,7 +8,7 @@ async fn async_wrong_type(#[splat] _x: u32) {} //~^ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 async fn async_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} -//~^ ERROR multiple `#[splat]`s are not allowed in the same function +//~^ ERROR multiple `#[splat]`s are not allowed in the same function argument list fn main() { async_wrong_type(1u32); diff --git a/tests/ui/splat/splat-async-fn-tuple-fail.stderr b/tests/ui/splat/splat-async-fn-tuple-fail.stderr index e643b29c88c8a..7c73382d73a5a 100644 --- a/tests/ui/splat/splat-async-fn-tuple-fail.stderr +++ b/tests/ui/splat/splat-async-fn-tuple-fail.stderr @@ -1,8 +1,8 @@ -error: multiple `#[splat]`s are not allowed in the same function +error: multiple `#[splat]`s are not allowed in the same function argument list --> $DIR/splat-async-fn-tuple-fail.rs:10:28 | LL | async fn async_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ ^^^^^^^^ | = help: remove `#[splat]` from all but one argument diff --git a/tests/ui/splat/splat-fn-ptr-cast.rs b/tests/ui/splat/splat-fn-ptr-cast.rs new file mode 100644 index 0000000000000..918e26c4dd741 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-cast.rs @@ -0,0 +1,17 @@ +//@ run-fail +//! Test never type casts to splatted and non-splatted functions. + +#![allow(incomplete_features)] +#![feature(splat)] +#![allow(unused_features)] + +fn main() { + // Bug #158603 regression test variants + #[rustfmt::skip] + let _x: fn(#[splat] (f32,)) = None.unwrap(); + // FIXME(splat): causes an ICE until #158603 is fixed + //x(1.0); + + let x: fn((i32,)) = None.unwrap(); + x((1,)); +} diff --git a/tests/ui/splat/splat-fn-ptr-ptr-tuple.rs b/tests/ui/splat/splat-fn-ptr-ptr-tuple.rs new file mode 100644 index 0000000000000..321511f270915 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-ptr-tuple.rs @@ -0,0 +1,42 @@ +//! Test using `#[splat]` on tuple arguments of pointers to pointers to simple functions. +//! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. + +//@ failure-status: 101 + +//@ normalize-stderr: ".*error:.*compiler/([^:]+):\d{1,}:\d{1,}:(.*)" -> "error: compiler/$1:LL:CC:$2" +//@ normalize-stderr: "thread.*panicked at .*compiler.*" -> "" +//@ normalize-stderr: "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}" +//@ normalize-stderr: "note: compiler flags.*\n\n" -> "" +//@ normalize-stderr: " +\d{1,}: .*\n" -> "" +//@ normalize-stderr: " + at .*\n" -> "" +//@ normalize-stderr: ".*omitted \d{1,} frames?.*\n" -> "" +//@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" +//@ normalize-stderr: ".*--> .*/splat-fn-ptr-tuple.rs:\d{1,}:\d{1,}.*\n" -> "" + +#![allow(incomplete_features)] +#![feature(splat)] + +fn tuple_args(#[splat] (_a, _b): (u32, i8)) {} + +fn splat_non_terminal_arg(#[splat] (_a, _b): (u32, i8), _c: f64) {} + +fn main() { + // FIXME(splat): not currently supported, can be supported when we no longer require a DefId in + // MIR lowering + // FIXME(rustfmt): the attribute gets deleted by rustfmt + #[rustfmt::skip] + let fn_pp: *const fn(#[splat] (u32, i8)) = tuple_args as *const fn(#[splat] (u32, i8)); + unsafe { + (*fn_pp)(1, 2); //~ ERROR splatted FnPtr side-tables are not yet implemented + // The ICE means that code after this line is not fully checked + (*fn_pp)(1u32, 2i8); + } + + #[rustfmt::skip] + let fn_pp: *const fn(#[splat] (u32, i8), f64) = + splat_non_terminal_arg as *const fn(#[splat] (u32, i8), f64); + unsafe { + (*fn_pp)(1, 2, 3.5); + (*fn_pp)(1u32, 2i8, 3.5f64); + } +} diff --git a/tests/ui/splat/splat-fn-ptr-ptr-tuple.stderr b/tests/ui/splat/splat-fn-ptr-ptr-tuple.stderr new file mode 100644 index 0000000000000..726561b366f80 --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-ptr-tuple.stderr @@ -0,0 +1,24 @@ +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented + --> $DIR/splat-fn-ptr-ptr-tuple.rs:30:9 + | +LL | (*fn_pp)(1, 2); + | ^^^^^^^^^^^^^^ + + + +Box +stack backtrace: + +note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md + +note: please make sure that you have updated to the latest nightly + +note: rustc {version} running on {platform} + +query stack during panic: +#0 [thir_body] building THIR for `main` +#1 [check_unsafety] unsafety-checking `main` +#2 [analysis] running analysis passes on crate `splat_fn_ptr_ptr_tuple` +end of query stack +error: aborting due to 1 previous error + diff --git a/tests/ui/splat/splat-fn-ptr-rust-call.rs b/tests/ui/splat/splat-fn-ptr-rust-call.rs new file mode 100644 index 0000000000000..a9fe48a459d8a --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-rust-call.rs @@ -0,0 +1,18 @@ +//! Test using `#[splat]` on tuple arguments of pointers to "rust-call" functions. +//! Currently ICEs at a later stage, but AST validation should catch it earlier. + +#![allow(incomplete_features)] +#![feature(splat)] +#![feature(unboxed_closures)] + +extern "rust-call" fn f(#[splat] _: ()) {} //~ ERROR `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + +fn main() { + // FIXME(rustfmt): the attribute gets deleted by rustfmt + #[rustfmt::skip] + let f2: extern "rust-call" fn(#[splat] ()) = f; //~ ERROR `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + // These errors could be confusing, but they're useful if the user meant to use "rust-call" + // instead of #[splat] + f(); //~ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument + f2(); //~ ERROR functions with the "rust-call" ABI must take a single non-self tuple argument +} diff --git a/tests/ui/splat/splat-fn-ptr-rust-call.stderr b/tests/ui/splat/splat-fn-ptr-rust-call.stderr new file mode 100644 index 0000000000000..6eacdfab5faac --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-rust-call.stderr @@ -0,0 +1,30 @@ +error: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + --> $DIR/splat-fn-ptr-rust-call.rs:8:1 + | +LL | extern "rust-call" fn f(#[splat] _: ()) {} + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^ + | + = help: remove `#[splat]` or change the ABI + +error: `#[splat]` is not allowed in the arguments of functions with the `rust-call` ABI + --> $DIR/splat-fn-ptr-rust-call.rs:13:13 + | +LL | let f2: extern "rust-call" fn(#[splat] ()) = f; + | ^^^^^^^^^^^^^^^^^^ ^^^^^^^^ + | + = help: remove `#[splat]` or change the ABI + +error: functions with the "rust-call" ABI must take a single non-self tuple argument + --> $DIR/splat-fn-ptr-rust-call.rs:16:5 + | +LL | f(); + | ^^^ + +error: functions with the "rust-call" ABI must take a single non-self tuple argument + --> $DIR/splat-fn-ptr-rust-call.rs:17:5 + | +LL | f2(); + | ^^^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/splat/splat-fn-ptr-tuple-const.rs b/tests/ui/splat/splat-fn-ptr-tuple-const.rs new file mode 100644 index 0000000000000..faf8501cc650a --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple-const.rs @@ -0,0 +1,34 @@ +//! Test using `#[splat]` on tuple arguments of generic function constants. +//! Currently ICEs (#158603), but if we fix it, we'll want to know and update this test to pass. + +//@ failure-status: 101 + +//@ normalize-stderr: ".*error:.*compiler/([^:]+):\d{1,}:\d{1,}:(.*)" -> "error: compiler/$1:LL:CC:$2" +//@ normalize-stderr: "thread.*panicked at .*compiler.*" -> "" +//@ normalize-stderr: "note: rustc.*running on.*" -> "note: rustc {version} running on {platform}" +//@ normalize-stderr: "note: compiler flags.*\n\n" -> "" +//@ normalize-stderr: " +\d{1,}: .*\n" -> "" +//@ normalize-stderr: " + at .*\n" -> "" +//@ normalize-stderr: ".*omitted \d{1,} frames?.*\n" -> "" +//@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" +//@ normalize-stderr: ".*--> .*/splat-fn-ptr-tuple.rs:\d{1,}:\d{1,}.*\n" -> "" + +#![allow(incomplete_features)] +#![feature(splat, tuple_trait)] + +use std::marker::Tuple; + +fn f(#[splat] args: Args) {} + +fn main() { + // FIXME(splat): not currently supported, can be supported when we no longer require a DefId in + // MIR lowering + // FIXME(rustfmt): the attribute gets deleted by rustfmt + #[rustfmt::skip] + const F2: fn(#[splat] (u8, u32)) = f::<(u8, u32)>; + const R2: () = F2(1, 2); //~ ERROR splatted FnPtr side-tables are not yet implemented + + #[rustfmt::skip] + const F1: fn(#[splat] ((u8, u32),)) = f::<((u8, u32),)>; + const R1: () = F1((1, 2)); //~ ERROR splatted FnPtr side-tables are not yet implemented +} diff --git a/tests/ui/splat/splat-fn-ptr-tuple-const.stderr b/tests/ui/splat/splat-fn-ptr-tuple-const.stderr new file mode 100644 index 0000000000000..1767782a9535e --- /dev/null +++ b/tests/ui/splat/splat-fn-ptr-tuple-const.stderr @@ -0,0 +1,52 @@ +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented + --> $DIR/splat-fn-ptr-tuple-const.rs:29:20 + | +LL | const R2: () = F2(1, 2); + | ^^^^^^^^ + + + +Box +stack backtrace: + +note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md + +note: please make sure that you have updated to the latest nightly + +note: rustc {version} running on {platform} + +query stack during panic: +#0 [thir_body] building THIR for `main::R2` +#1 [check_match] match-checking `main::R2` +#2 [mir_built] building MIR for `main::R2` +#3 [trivial_const] checking if `main::R2` is a trivial const +#4 [eval_to_const_value_raw] simplifying constant for the type system `main::R2` +#5 [analysis] running analysis passes on crate `splat_fn_ptr_tuple_const` +end of query stack +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented + --> $DIR/splat-fn-ptr-tuple-const.rs:33:20 + | +LL | const R1: () = F1((1, 2)); + | ^^^^^^^^^^ + + + +Box +stack backtrace: + +note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md + +note: please make sure that you have updated to the latest nightly + +note: rustc {version} running on {platform} + +query stack during panic: +#0 [thir_body] building THIR for `main::R1` +#1 [check_match] match-checking `main::R1` +#2 [mir_built] building MIR for `main::R1` +#3 [trivial_const] checking if `main::R1` is a trivial const +#4 [eval_to_const_value_raw] simplifying constant for the type system `main::R1` +#5 [analysis] running analysis passes on crate `splat_fn_ptr_tuple_const` +end of query stack +error: aborting due to 2 previous errors + diff --git a/tests/ui/splat/splat-fn-ptr-tuple.rs b/tests/ui/splat/splat-fn-ptr-tuple.rs index 297dbc0457794..395d30baedaaf 100644 --- a/tests/ui/splat/splat-fn-ptr-tuple.rs +++ b/tests/ui/splat/splat-fn-ptr-tuple.rs @@ -1,3 +1,6 @@ +//! Test using `#[splat]` on tuple arguments of pointers to simple functions. +//! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. + //@ failure-status: 101 //@ normalize-stderr: ".*error:.*compiler/([^:]+):\d{1,}:\d{1,}:(.*)" -> "error: compiler/$1:LL:CC:$2" @@ -10,9 +13,6 @@ //@ normalize-stderr: ".*note: Some details are omitted.*\n" -> "" //@ normalize-stderr: ".*--> .*/splat-fn-ptr-tuple.rs:\d{1,}:\d{1,}.*\n" -> "" -//! Test using `#[splat]` on tuple arguments of simple functions. -//! Currently ICEs, but if we fix it, we'll want to know and update this test to pass. - #![allow(incomplete_features)] #![feature(splat)] @@ -24,10 +24,10 @@ fn main() { // FIXME(splat): not currently supported, can be supported when we no longer require a DefId in // MIR lowering // FIXME(rustfmt): the attribute gets deleted by rustfmt - // Functions #[rustfmt::skip] let fn_ptr: fn(#[splat] (u32, i8)) = tuple_args; - fn_ptr(1, 2); //~ ERROR no splatted def for function or method callee + fn_ptr(1, 2); //~ ERROR splatted FnPtr side-tables are not yet implemented + // The ICE means that code after this line is not fully checked fn_ptr(1u32, 2i8); // FIXME(splat): should splatted functions be callable with tupled and un-tupled arguments? @@ -39,15 +39,8 @@ fn main() { fn_ptr(1, 2, 3.5); fn_ptr(1u32, 2i8, 3.5f64); - // Function pointers - #[rustfmt::skip] - let fn_ptr: *const fn(#[splat] (u32, i8)) = tuple_args as *const fn(#[splat] (u32, i8)); - (*fn_ptr)(1, 2); - (*fn_ptr)(1u32, 2i8); - + // Bug #158603 regression test #[rustfmt::skip] - let fn_ptr: *const fn(#[splat] (u32, i8), f64) = - splat_non_terminal_arg as *const fn(#[splat] (u32, i8), f64); - (*fn_ptr)(1, 2, 3.5); - (*fn_ptr)(1u32, 2i8, 3.5f64); + let x: fn(#[splat] (i32,)) = None.unwrap(); + x(1); } diff --git a/tests/ui/splat/splat-fn-ptr-tuple.stderr b/tests/ui/splat/splat-fn-ptr-tuple.stderr index a69175bd5e886..4cc861cafe968 100644 --- a/tests/ui/splat/splat-fn-ptr-tuple.stderr +++ b/tests/ui/splat/splat-fn-ptr-tuple.stderr @@ -1,4 +1,4 @@ -error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: no splatted def for function or method callee +error: compiler/rustc_mir_build/src/thir/cx/expr.rs:LL:CC: splatted FnPtr side-tables are not yet implemented | LL | fn_ptr(1, 2); | ^^^^^^^^^^^^ diff --git a/tests/ui/splat/splat-invalid.rs b/tests/ui/splat/splat-invalid.rs index 2c4bf57ef972a..1fe4d131fc584 100644 --- a/tests/ui/splat/splat-invalid.rs +++ b/tests/ui/splat/splat-invalid.rs @@ -4,14 +4,32 @@ #![feature(splat)] #![feature(c_variadic)] -fn multisplat_bad(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} -//~^ ERROR multiple `#[splat]`s are not allowed in the same function +fn multisplat_fn_bad(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} +//~^ ERROR multiple `#[splat]`s are not allowed in the same function argument list + +fn multisplat_arg_bad( + #[splat] + #[splat] + //~^ ERROR multiple `splat` attributes + (_a, _b): (u32, i8), +) { +} + +fn multisplat_arg_fn_bad( + #[splat] + //~^ ERROR multiple `#[splat]`s are not allowed in the same function argument list + #[splat] + //~^ ERROR multiple `splat` attributes + (_a, _b): (u32, i8), + #[splat] (_c, _d): (u32, i8), +) { +} unsafe extern "C" fn splat_variadic(#[splat] (_a, _b): (u32, i8), varargs: ...) {} -//~^ ERROR `...` and `#[splat]` are not allowed in the same function +//~^ ERROR `...` and `#[splat]` are not allowed in the same function argument list unsafe extern "C" fn splat_variadic2(varargs: ..., #[splat] (_a, _b): (u32, i8)) {} -//~^ ERROR `...` and `#[splat]` are not allowed in the same function +//~^ ERROR `...` and `#[splat]` are not allowed in the same function argument list //~| ERROR `...` must be the last argument of a C-variadic function extern "C" { diff --git a/tests/ui/splat/splat-invalid.stderr b/tests/ui/splat/splat-invalid.stderr index 74c54c00cd285..86b1dbbb7e44b 100644 --- a/tests/ui/splat/splat-invalid.stderr +++ b/tests/ui/splat/splat-invalid.stderr @@ -1,35 +1,49 @@ -error: multiple `#[splat]`s are not allowed in the same function - --> $DIR/splat-invalid.rs:7:19 +error: multiple `#[splat]`s are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:7:22 | -LL | fn multisplat_bad(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | fn multisplat_fn_bad(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} + | ^^^^^^^^ ^^^^^^^^ | = help: remove `#[splat]` from all but one argument -error: `...` and `#[splat]` are not allowed in the same function - --> $DIR/splat-invalid.rs:10:37 +error: multiple `#[splat]`s are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:19:5 + | +LL | #[splat] + | ^^^^^^^^ +LL | +LL | #[splat] + | ^^^^^^^^ +... +LL | #[splat] (_c, _d): (u32, i8), + | ^^^^^^^^ + | + = help: remove `#[splat]` from all but one argument + +error: `...` and `#[splat]` are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:28:37 | LL | unsafe extern "C" fn splat_variadic(#[splat] (_a, _b): (u32, i8), varargs: ...) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^ + | ^^^^^^^^ ^^^^^^^^^^^^ | = help: remove `#[splat]` or remove `...` error: `...` must be the last argument of a C-variadic function - --> $DIR/splat-invalid.rs:13:38 + --> $DIR/splat-invalid.rs:31:38 | LL | unsafe extern "C" fn splat_variadic2(varargs: ..., #[splat] (_a, _b): (u32, i8)) {} | ^^^^^^^^^^^^ -error: `...` and `#[splat]` are not allowed in the same function - --> $DIR/splat-invalid.rs:13:38 +error: `...` and `#[splat]` are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:31:38 | LL | unsafe extern "C" fn splat_variadic2(varargs: ..., #[splat] (_a, _b): (u32, i8)) {} - | ^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ ^^^^^^^^ | = help: remove `#[splat]` or remove `...` error: incorrect function inside `extern` block - --> $DIR/splat-invalid.rs:18:8 + --> $DIR/splat-invalid.rs:36:8 | LL | extern "C" { | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body @@ -39,16 +53,16 @@ LL | fn splat_variadic3(#[splat] (_a, _b): (u32, i8), ...) {} = help: you might have meant to write a function accessible through FFI, which can be done by writing `extern fn` outside of the `extern` block = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html -error: `...` and `#[splat]` are not allowed in the same function - --> $DIR/splat-invalid.rs:18:24 +error: `...` and `#[splat]` are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:36:24 | LL | fn splat_variadic3(#[splat] (_a, _b): (u32, i8), ...) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^ + | ^^^^^^^^ ^^^ | = help: remove `#[splat]` or remove `...` error: incorrect function inside `extern` block - --> $DIR/splat-invalid.rs:22:8 + --> $DIR/splat-invalid.rs:40:8 | LL | extern "C" { | ---------- `extern` blocks define existing foreign functions and functions inside of them cannot have a body @@ -60,27 +74,51 @@ LL | fn splat_variadic4(..., #[splat] (_a, _b): (u32, i8)) {} = note: for more information, visit https://doc.rust-lang.org/std/keyword.extern.html error: `...` must be the last argument of a C-variadic function - --> $DIR/splat-invalid.rs:22:24 + --> $DIR/splat-invalid.rs:40:24 | LL | fn splat_variadic4(..., #[splat] (_a, _b): (u32, i8)) {} | ^^^ -error: `...` and `#[splat]` are not allowed in the same function - --> $DIR/splat-invalid.rs:22:24 +error: `...` and `#[splat]` are not allowed in the same function argument list + --> $DIR/splat-invalid.rs:40:24 | LL | fn splat_variadic4(..., #[splat] (_a, _b): (u32, i8)) {} - | ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^ ^^^^^^^^ | = help: remove `#[splat]` or remove `...` +error: multiple `splat` attributes + --> $DIR/splat-invalid.rs:12:5 + | +LL | #[splat] + | ^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/splat-invalid.rs:11:5 + | +LL | #[splat] + | ^^^^^^^^ + +error: multiple `splat` attributes + --> $DIR/splat-invalid.rs:21:5 + | +LL | #[splat] + | ^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/splat-invalid.rs:19:5 + | +LL | #[splat] + | ^^^^^^^^ + error[E0053]: method `has_splat` has an incompatible type for trait - --> $DIR/splat-invalid.rs:42:5 + --> $DIR/splat-invalid.rs:60:5 | LL | fn has_splat(_: ()) {} | ^^^^^^^^^^^^^^^^^^^ expected fn with arg 0 splatted, found fn with no splatted arg | note: type in trait - --> $DIR/splat-invalid.rs:34:5 + --> $DIR/splat-invalid.rs:52:5 | LL | fn has_splat(#[splat] _: ()); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -88,19 +126,19 @@ LL | fn has_splat(#[splat] _: ()); found signature `fn(())` error[E0053]: method `no_splat` has an incompatible type for trait - --> $DIR/splat-invalid.rs:44:5 + --> $DIR/splat-invalid.rs:62:5 | LL | fn no_splat(#[splat] _: (u32, f64)) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn with no splatted arg, found fn with arg 0 splatted | note: type in trait - --> $DIR/splat-invalid.rs:36:5 + --> $DIR/splat-invalid.rs:54:5 | LL | fn no_splat(_: (u32, f64)); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ = note: expected signature `fn((_, _))` found signature `fn(#[splat] (_, _))` -error: aborting due to 11 previous errors +error: aborting due to 14 previous errors For more information about this error, try `rustc --explain E0053`. diff --git a/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs b/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs index 3f29b9d292d22..473aff7891636 100644 --- a/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs +++ b/tests/ui/splat/splat-unsafe-fn-tuple-fail.rs @@ -7,7 +7,7 @@ unsafe fn unsafe_wrong_type(#[splat] _x: u32) {} //~^ ERROR cannot use splat attribute; the splatted argument type must be a tuple or unit, not a u32 unsafe fn unsafe_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} -//~^ ERROR multiple `#[splat]`s are not allowed in the same function +//~^ ERROR multiple `#[splat]`s are not allowed in the same function argument list fn main() { unsafe { diff --git a/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr b/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr index 67935e170671a..7ad15f0f960e2 100644 --- a/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr +++ b/tests/ui/splat/splat-unsafe-fn-tuple-fail.stderr @@ -1,8 +1,8 @@ -error: multiple `#[splat]`s are not allowed in the same function +error: multiple `#[splat]`s are not allowed in the same function argument list --> $DIR/splat-unsafe-fn-tuple-fail.rs:9:30 | LL | unsafe fn unsafe_multi_splat(#[splat] (_a, _b): (u32, i8), #[splat] (_c, _d): (u32, i8)) {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | ^^^^^^^^ ^^^^^^^^ | = help: remove `#[splat]` from all but one argument diff --git a/tests/ui/suggestions/issue-105645.stderr b/tests/ui/suggestions/issue-105645.stderr index 2b9a94d8e0ab2..2342c22cfa80a 100644 --- a/tests/ui/suggestions/issue-105645.stderr +++ b/tests/ui/suggestions/issue-105645.stderr @@ -7,7 +7,7 @@ LL | foo(&mut bref); | required by a bound introduced by this call | help: the trait `std::io::Write` is implemented for `&mut [u8]` - --> $SRC_DIR/std/src/io/impls.rs:LL:COL + --> $SRC_DIR/core/src/io/impls.rs:LL:COL note: required by a bound in `foo` --> $DIR/issue-105645.rs:8:21 | diff --git a/tests/ui/type-alias-impl-trait/self-referential-2.current.stderr b/tests/ui/type-alias-impl-trait/self-referential-2.current.stderr index ca26e3dd03f35..f656a274703ee 100644 --- a/tests/ui/type-alias-impl-trait/self-referential-2.current.stderr +++ b/tests/ui/type-alias-impl-trait/self-referential-2.current.stderr @@ -12,7 +12,7 @@ help: the trait `PartialEq` is not implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/self-referential-3.stderr b/tests/ui/type-alias-impl-trait/self-referential-3.stderr index 8592de243adac..7bc4c6def624d 100644 --- a/tests/ui/type-alias-impl-trait/self-referential-3.stderr +++ b/tests/ui/type-alias-impl-trait/self-referential-3.stderr @@ -13,7 +13,7 @@ help: the trait `PartialEq` is implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 1 previous error diff --git a/tests/ui/type-alias-impl-trait/self-referential-4.stderr b/tests/ui/type-alias-impl-trait/self-referential-4.stderr index c6bf1973fcbb1..76c5611906d34 100644 --- a/tests/ui/type-alias-impl-trait/self-referential-4.stderr +++ b/tests/ui/type-alias-impl-trait/self-referential-4.stderr @@ -12,7 +12,7 @@ help: the trait `PartialEq` is implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `&i32` with `Foo<'static, 'b>` --> $DIR/self-referential-4.rs:13:31 @@ -28,7 +28,7 @@ help: the trait `PartialEq` is implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `&i32` with `Moo<'static, 'a>` --> $DIR/self-referential-4.rs:20:31 @@ -44,7 +44,7 @@ help: the trait `PartialEq` is implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/type-alias-impl-trait/self-referential.stderr b/tests/ui/type-alias-impl-trait/self-referential.stderr index 7b4e6e9cac528..62d85466cde1a 100644 --- a/tests/ui/type-alias-impl-trait/self-referential.stderr +++ b/tests/ui/type-alias-impl-trait/self-referential.stderr @@ -13,7 +13,7 @@ help: the trait `PartialEq` is implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `&i32` with `(i32, Foo<'a, 'b>::{opaque#0}<'a, 'b>)` --> $DIR/self-referential.rs:14:31 @@ -30,7 +30,7 @@ help: the trait `PartialEq` is implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: can't compare `&i32` with `(i32, Moo<'b, 'a>::{opaque#0}<'b, 'a>)` --> $DIR/self-referential.rs:22:31 @@ -47,7 +47,7 @@ help: the trait `PartialEq` is implemented for `i32` ::: $SRC_DIR/core/src/cmp.rs:LL:COL | = note: in this macro invocation - = note: this error originates in the macro `partial_eq_impl` (in Nightly builds, run with -Z macro-backtrace for more info) + = note: this error originates in the macro `impl_partial_eq_for_primitive` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/tests/ui/type/pattern_types/non_null.stderr b/tests/ui/type/pattern_types/non_null.stderr index 7d3e61770c2c7..88ff73ca2a4a4 100644 --- a/tests/ui/type/pattern_types/non_null.stderr +++ b/tests/ui/type/pattern_types/non_null.stderr @@ -143,8 +143,8 @@ error: layout_of((*const [u8]) is !null) = Layout { align: AbiAlign { abi: Align(8 bytes), }, - backend_repr: ScalarPair( - Initialized { + backend_repr: ScalarPair { + a: Initialized { value: Pointer( AddressSpace( 0, @@ -152,14 +152,15 @@ error: layout_of((*const [u8]) is !null) = Layout { ), valid_range: 1..=18446744073709551615, }, - Initialized { + b: Initialized { value: Int( I64, false, ), valid_range: 0..=18446744073709551615, }, - ), + b_offset: Size(8 bytes), + }, fields: Arbitrary { offsets: [ Size(0 bytes),