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/bootstrap.example.toml b/bootstrap.example.toml index 1ca4bc9159e7c..2c9a5ab1a152c 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -973,6 +973,24 @@ #dist.vendor = if "is a tarball source" || "is a git repository" { true } else { false } +# ============================================================================= +# Profile-guided optimization options +# +# Configure the PGO profile to be used for compilation, or a path where the +# profile will be written by an instrumented binary, for individual components +# ============================================================================= + +# Use the following profile to PGO optimize the Rust compiler. +#pgo.rustc.use = "/tmp/profiles/foo.profraw" +# Instrument the Rust compiler, so that when executed, it will gather profiles +# to this path. +#pgo.rustc.generate = "/tmp/profiles/foo.profraw" +# Use the following profile to PGO optimize LLVM. +#pgo.llvm.use = "/tmp/profiles/foo.profraw" +# Instrument LLVM, so that when executed, it will gather profiles +# Note: for LLVM specifically, this should be a directory, not a file path. +#pgo.llvm.generate = "/tmp/profiles" + # ============================================================================= # Options for specific targets # 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_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 9edf7103f3990..64ba106b68adc 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -70,7 +70,9 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { // Doesn't have any language semantics | StatementKind::Coverage(..) // Does not actually affect borrowck - | StatementKind::StorageLive(..) => {} + | StatementKind::StorageLive(..) + // Does not affect borrowck + | StatementKind::BackwardIncompatibleDropHint { .. } => {} StatementKind::StorageDead(local) => { self.access_place( location, @@ -81,7 +83,6 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { } StatementKind::ConstEvalCounter | StatementKind::Nop - | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::SetDiscriminant { .. } => { bug!("Statement not allowed in this MIR phase") } 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..f417dfba746f1 100644 --- a/compiler/rustc_codegen_ssa/src/mir/rvalue.rs +++ b/compiler/rustc_codegen_ssa/src/mir/rvalue.rs @@ -36,7 +36,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(..))); } @@ -323,9 +323,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..815f60c55538e 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) @@ -3424,6 +3421,7 @@ impl<'a> Parser<'a> { let (pat, colon) = this.parse_fn_param_pat_colon()?; if !colon { let mut err = this.unexpected().unwrap_err(); + let pat_span = pat.span; return if let Some(ident) = this.parameter_without_type( &mut err, pat, @@ -3432,7 +3430,9 @@ impl<'a> Parser<'a> { fn_parse_mode, ) { let guar = err.emit(); - Ok((dummy_arg(ident, guar), Trailing::No, UsePreAttrPos::No)) + let mut arg = dummy_arg(ident, guar); + arg.span = pat_span; + Ok((arg, Trailing::No, UsePreAttrPos::No)) } else { Err(err) }; 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/alloctests/Cargo.toml b/library/alloctests/Cargo.toml index 3b522bf80a217..791b8ecbafce3 100644 --- a/library/alloctests/Cargo.toml +++ b/library/alloctests/Cargo.toml @@ -22,6 +22,10 @@ rand_xorshift = "0.4.0" name = "alloctests" path = "tests/lib.rs" +[[test]] +name = "c_str_alloc_error" +path = "tests/c_str_alloc_error.rs" + [[test]] name = "vec_deque_alloc_error" path = "tests/vec_deque_alloc_error.rs" diff --git a/library/alloctests/tests/c_str_alloc_error.rs b/library/alloctests/tests/c_str_alloc_error.rs new file mode 100644 index 0000000000000..669a783645baa --- /dev/null +++ b/library/alloctests/tests/c_str_alloc_error.rs @@ -0,0 +1,72 @@ +//! Regression test for the panic-safety fix in rust-lang/rust#155707. +//! +//! rust-lang/rust#70201 gave `::clone_into` a path that moved the +//! target `CString`'s buffer out before growing a `Vec`; if that growth's allocation +//! failed and unwound, the target was left without its nul terminator. This only +//! reproduces under Miri: in a normal build the `#[global_allocator]` below can't +//! intercept the reallocation inside `CString::clone_into` (it lives in libstd, which +//! library tests link with `-C prefer-dynamic`), so as a regular test it just checks +//! the happy path. + +// Disabled under Miri on Windows: a `#[global_allocator]` wrapping `System` trips +// Stacked Borrows there, and it affects libtest's own allocations, not just this test +// (so `#[ignore]` would not be enough). See . +#![cfg(not(all(miri, windows)))] +#![feature(alloc_error_hook)] + +use std::alloc::{GlobalAlloc, Layout, System, set_alloc_error_hook}; +use std::ffi::CString; +use std::panic::{AssertUnwindSafe, catch_unwind}; +use std::sync::atomic::{AtomicBool, Ordering}; + +// Once armed, the first allocation of 8 bytes or more fails and disarms, so the +// reallocation inside `clone_into`'s grow path fails while the runtime's own +// smaller allocations keep succeeding. +struct OneShotFailingAlloc; + +static ARMED: AtomicBool = AtomicBool::new(false); + +unsafe impl GlobalAlloc for OneShotFailingAlloc { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if layout.size() >= 8 && ARMED.swap(false, Ordering::SeqCst) { + return core::ptr::null_mut(); + } + unsafe { System.alloc(layout) } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + unsafe { System.dealloc(ptr, layout) } + } + + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + if new_size >= 8 && ARMED.swap(false, Ordering::SeqCst) { + return core::ptr::null_mut(); + } + unsafe { System.realloc(ptr, layout, new_size) } + } +} + +#[global_allocator] +static ALLOC: OneShotFailingAlloc = OneShotFailingAlloc; + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn clone_into_alloc_failure_leaves_target_valid() { + set_alloc_error_hook(|_| panic!("alloc error")); + + let src = CString::new("a fairly long value").unwrap(); + let mut target = CString::new("x").unwrap(); + + ARMED.store(true, Ordering::SeqCst); + // Under Miri the failing allocator is honored, so this reallocation unwinds; in a + // normal build the allocator can't intercept it and `clone_into` just succeeds. + let res = catch_unwind(AssertUnwindSafe(|| src.as_c_str().clone_into(&mut target))); + ARMED.store(false, Ordering::SeqCst); + + if cfg!(miri) { + assert!(res.is_err(), "clone_into should have unwound on the alloc failure"); + } + // Either way `target` must still end in its nul terminator. Before the fix the Miri + // unwind left it empty (also caught as a bad write in `CString`'s destructor). + assert_eq!(target.as_bytes_with_nul().last(), Some(&0)); +} 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/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/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/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/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/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 3216fd671c3f8..bc6d1f94d698b 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -1293,12 +1293,10 @@ pub fn rustc_cargo( cargo.rustflag("-Clink-args=-Wl,--icf=all"); } - if builder.config.rust_profile_use.is_some() && builder.config.rust_profile_generate.is_some() { - panic!("Cannot use and generate PGO profiles at the same time"); - } - let is_collecting = if let Some(path) = &builder.config.rust_profile_generate { + let is_collecting = if let Some(path) = &builder.config.rust_pgo.generate_profile { if build_compiler.stage == 1 { - cargo.rustflag(&format!("-Cprofile-generate={path}")); + cargo + .rustflag(&format!("-Cprofile-generate={}", path.to_str().expect("non-UTF8 path"))); // Apparently necessary to avoid overflowing the counters during // a Cargo build profile cargo.rustflag("-Cllvm-args=-vp-counters-per-site=4"); @@ -1306,9 +1304,9 @@ pub fn rustc_cargo( } else { false } - } else if let Some(path) = &builder.config.rust_profile_use { + } else if let Some(path) = &builder.config.rust_pgo.use_profile { if build_compiler.stage == 1 { - cargo.rustflag(&format!("-Cprofile-use={path}")); + cargo.rustflag(&format!("-Cprofile-use={}", path.to_str().expect("non-UTF8 path"))); if builder.is_verbose() { cargo.rustflag("-Cllvm-args=-pgo-warn-missing-function"); } @@ -1464,7 +1462,7 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect // found. This is to avoid the linker errors about undefined references to // `__llvm_profile_instrument_memop` when linking `rustc_driver`. let mut llvm_linker_flags = String::new(); - if builder.config.llvm_profile_generate + if builder.config.llvm_pgo.generate_profile.is_some() && target.is_msvc() && let Some(ref clang_cl_path) = builder.config.llvm_clang_cl { diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 114387d963a6d..fdb13d3ca6a34 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -3035,11 +3035,11 @@ impl Step for ReproducibleArtifacts { fn run(self, builder: &Builder<'_>) -> Self::Output { let mut added_anything = false; let tarball = Tarball::new(builder, "reproducible-artifacts", &self.target.triple); - if let Some(path) = builder.config.rust_profile_use.as_ref() { + if let Some(path) = builder.config.rust_pgo.use_profile.as_ref() { tarball.add_file(path, ".", FileType::Regular); added_anything = true; } - if let Some(path) = builder.config.llvm_profile_use.as_ref() { + if let Some(path) = builder.config.llvm_pgo.use_profile.as_ref() { tarball.add_file(path, ".", FileType::Regular); added_anything = true; } diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index 6f1b8532f031b..f40b384a589ba 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -19,7 +19,7 @@ use build_helper::git::PathFreshness; use crate::core::build_steps::llvm; use crate::core::builder::{Builder, RunConfig, ShouldRun, Step, StepMetadata}; -use crate::core::config::{Config, TargetSelection}; +use crate::core::config::{Config, LlvmPgoGenerationMode, TargetSelection}; use crate::utils::build_stamp::{BuildStamp, generate_smart_stamp_hash}; use crate::utils::exec::command; use crate::utils::helpers::{ @@ -369,14 +369,17 @@ impl Step for Llvm { // This flag makes sure `FileCheck` is copied in the final binaries directory. cfg.define("LLVM_INSTALL_UTILS", "ON"); - if builder.config.llvm_profile_generate { + if let Some(mode) = builder.config.llvm_pgo.generate_profile.as_ref() { cfg.define("LLVM_BUILD_INSTRUMENTED", "IR"); - if let Ok(llvm_profile_dir) = std::env::var("LLVM_PROFILE_DIR") { - cfg.define("LLVM_PROFILE_DATA_DIR", llvm_profile_dir); + match mode { + LlvmPgoGenerationMode::Implicit => {} + LlvmPgoGenerationMode::Directory(llvm_profile_dir) => { + cfg.define("LLVM_PROFILE_DATA_DIR", llvm_profile_dir); + } } cfg.define("LLVM_BUILD_RUNTIME", "No"); } - if let Some(path) = builder.config.llvm_profile_use.as_ref() { + if let Some(path) = builder.config.llvm_pgo.use_profile.as_ref() { cfg.define("LLVM_PROFDATA_FILE", path); } @@ -1326,7 +1329,7 @@ impl Step for Lld { // when doing PGO on CI, cmake or clang-cl don't automatically link clang's // profiler runtime in. In that case, we need to manually ask cmake to do it, to avoid // linking errors, much like LLVM's cmake setup does in that situation. - if builder.config.llvm_profile_generate + if builder.config.llvm_pgo.generate_profile.is_some() && target.is_msvc() && let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref() { 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/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs index 267e21c599728..a76792a58252b 100644 --- a/src/bootstrap/src/core/config/config.rs +++ b/src/bootstrap/src/core/config/config.rs @@ -41,6 +41,7 @@ use crate::core::config::toml::dist::Dist; use crate::core::config::toml::gcc::Gcc; use crate::core::config::toml::install::Install; use crate::core::config::toml::llvm::Llvm; +use crate::core::config::toml::pgo::{Pgo, PgoConfig}; use crate::core::config::toml::rust::{ BootstrapOverrideLld, Rust, RustOptimize, check_incompatible_options_for_ci_rustc, parse_codegen_backends, @@ -190,6 +191,7 @@ pub struct Config { pub llvm_cxxflags: Option, pub llvm_ldflags: Option, pub llvm_use_libcxx: bool, + pub llvm_pgo: LlvmPgoConfig, // gcc codegen options pub gcc_ci_mode: GccCiMode, @@ -225,17 +227,14 @@ pub struct Config { pub rust_remap_debuginfo: bool, pub rust_new_symbol_mangling: Option, pub rust_annotate_moves_size_limit: Option, - pub rust_profile_use: Option, - pub rust_profile_generate: Option, pub rust_lto: RustcLto, pub rust_validate_mir_opts: Option, pub rust_std_features: BTreeSet, pub rust_break_on_ice: bool, pub rust_parallel_frontend_threads: Option, pub rust_rustflags: Vec, + pub rust_pgo: PgoConfig, - pub llvm_profile_use: Option, - pub llvm_profile_generate: bool, pub llvm_libunwind_default: Option, pub enable_bolt_settings: bool, @@ -456,8 +455,20 @@ impl Config { // Now load the TOML config, as soon as possible let (mut toml, toml_path) = load_toml_config(&src, flags_config, &get_toml); - postprocess_toml(&mut toml, &src, toml_path.clone(), &exec_ctx, &flags_set, &get_toml); + let TomlConfig { + change_id: toml_change_id, + build: toml_build, + install: toml_install, + llvm: toml_llvm, + gcc: toml_gcc, + rust: toml_rust, + target: toml_target, + dist: toml_dist, + pgo: toml_pgo, + profile: _, + include: _, + } = toml; // Now override TOML values with flags, to make sure that we won't later override flags with // TOML values by accident instead, because flags have higher priority. @@ -523,7 +534,7 @@ impl Config { ccache: build_ccache, exclude: build_exclude, compiletest_allow_stage0: build_compiletest_allow_stage0, - } = toml.build.unwrap_or_default(); + } = toml_build.unwrap_or_default(); let Install { prefix: install_prefix, @@ -533,7 +544,7 @@ impl Config { libdir: install_libdir, mandir: install_mandir, datadir: install_datadir, - } = toml.install.unwrap_or_default(); + } = toml_install.unwrap_or_default(); let Rust { optimize: rust_optimize, @@ -595,7 +606,7 @@ impl Config { std_features: rust_std_features, break_on_ice: rust_break_on_ice, rustflags: rust_rustflags, - } = toml.rust.unwrap_or_default(); + } = toml_rust.unwrap_or_default(); let Llvm { optimize: llvm_optimize, @@ -627,7 +638,7 @@ impl Config { enable_warnings: llvm_enable_warnings, download_ci_llvm: llvm_download_ci_llvm, build_config: llvm_build_config, - } = toml.llvm.unwrap_or_default(); + } = toml_llvm.unwrap_or_default(); let Dist { sign_folder: dist_sign_folder, @@ -637,12 +648,57 @@ impl Config { compression_profile: dist_compression_profile, include_mingw_linker: dist_include_mingw_linker, vendor: dist_vendor, - } = toml.dist.unwrap_or_default(); + } = toml_dist.unwrap_or_default(); let Gcc { download_ci_gcc: gcc_download_ci_gcc, libgccjit_libs_dir: gcc_libgccjit_libs_dir, - } = toml.gcc.unwrap_or_default(); + } = toml_gcc.unwrap_or_default(); + + let Pgo { rustc: pgo_rustc, llvm: pgo_llvm } = toml_pgo.unwrap_or_default(); + + // Backcompat: flags have priority over config + if flags_rust_profile_use.is_some() || flags_rust_profile_generate.is_some() { + eprintln!( + "WARNING: the `--rust-profile-generate` and `--rust-profile-use` flags have been deprecated. Configure PGO through the config file instead, in the [pgo.rustc] section." + ); + } + if rust_profile_use.is_some() || rust_profile_generate.is_some() { + eprintln!( + "WARNING: the `rust.profile-generate` and `rust.profile-use` config options have been deprecated. Configure PGO through the config file instead, in the [pgo.rustc] section." + ); + } + if flags_llvm_profile_use.is_some() || flags_llvm_profile_generate { + eprintln!( + "WARNING: the `--llvm-profile-generate` and `--llvm-profile-use` flags have been deprecated. Configure PGO through the config file instead, in the [pgo.llvm] section." + ); + } + + let mut pgo_rustc = pgo_rustc.unwrap_or_default(); + pgo_rustc.use_profile = + flags_rust_profile_use.or(pgo_rustc.use_profile).or(rust_profile_use); + pgo_rustc.generate_profile = + flags_rust_profile_generate.or(pgo_rustc.generate_profile).or(rust_profile_generate); + if pgo_rustc.use_profile.is_some() && pgo_rustc.generate_profile.is_some() { + panic!("Cannot use and generate rust PGO profiles at the same time"); + } + + let pgo_llvm = pgo_llvm.unwrap_or_default(); + let pgo_llvm = LlvmPgoConfig { + use_profile: flags_llvm_profile_use.or(pgo_llvm.use_profile), + generate_profile: if flags_llvm_profile_generate { + Some(if let Ok(llvm_profile_dir) = std::env::var("LLVM_PROFILE_DIR") { + LlvmPgoGenerationMode::Directory(PathBuf::from(llvm_profile_dir)) + } else { + LlvmPgoGenerationMode::Implicit + }) + } else { + pgo_llvm.generate_profile.map(LlvmPgoGenerationMode::Directory) + }, + }; + if pgo_llvm.use_profile.is_some() && pgo_llvm.generate_profile.is_some() { + panic!("Cannot use and generate LLVM PGO profiles at the same time"); + } if rust_bootstrap_override_lld.is_some() && rust_bootstrap_override_lld_legacy.is_some() { panic!( @@ -877,7 +933,7 @@ impl Config { // Linux targets for which the user explicitly overrode the used linker let mut targets_with_user_linker_override = HashSet::new(); - if let Some(t) = toml.target { + if let Some(t) = toml_target { for (triple, cfg) in t { let TomlTarget { cc: target_cc, @@ -1337,7 +1393,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to cargo_info, cargo_native_static: build_cargo_native_static.unwrap_or(false), ccache, - change_id: toml.change_id.inner, + change_id: toml_change_id.inner, channel, ci_env, clippy_info, @@ -1428,10 +1484,9 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to ), llvm_offload: llvm_offload.unwrap_or(false), llvm_optimize: llvm_optimize.unwrap_or(true), + llvm_pgo: pgo_llvm, llvm_plugins: llvm_plugin.unwrap_or(false), llvm_polly: llvm_polly.unwrap_or(false), - llvm_profile_generate: flags_llvm_profile_generate, - llvm_profile_use: flags_llvm_profile_use, llvm_release_debuginfo: llvm_release_debuginfo.unwrap_or(false), llvm_static_stdcpp: llvm_static_libstdcpp.unwrap_or(false), llvm_targets, @@ -1496,8 +1551,7 @@ NOTE: Please add `--stage 2` to your command line, or if you're sure you want to .or(rust_overflow_checks) .unwrap_or(rust_debug == Some(true)), rust_parallel_frontend_threads: rust_parallel_frontend_threads.map(threads_from_config), - rust_profile_generate: flags_rust_profile_generate.or(rust_profile_generate), - rust_profile_use: flags_rust_profile_use.or(rust_profile_use), + rust_pgo: pgo_rustc, rust_randomize_layout: rust_randomize_layout.unwrap_or(false), rust_remap_debuginfo: rust_remap_debuginfo.unwrap_or(false), rust_rpath: rust_rpath.unwrap_or(true), @@ -2030,6 +2084,20 @@ fn compute_src_directory(src_dir: Option, exec_ctx: &ExecutionContext) None } +#[derive(Clone)] +pub enum LlvmPgoGenerationMode { + /// Enable PGO instrumentation that will write profiles into a default path. + Implicit, + /// Enable PGO instrumentation that will write profiles into the specified directory. + Directory(PathBuf), +} + +#[derive(Clone)] +pub struct LlvmPgoConfig { + pub use_profile: Option, + pub generate_profile: Option, +} + /// Loads bootstrap TOML config and returns the config together with a path from where /// it was loaded. /// `src` is the source root directory, and `config_path` is an optionally provided path to the diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs index bf171f1de34e0..775de587b3e54 100644 --- a/src/bootstrap/src/core/config/flags.rs +++ b/src/bootstrap/src/core/config/flags.rs @@ -153,18 +153,22 @@ pub struct Flags { /// generate PGO profile with rustc build #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")] - pub rust_profile_generate: Option, + // FIXME: Remove this option at the end of 2026 + pub rust_profile_generate: Option, /// use PGO profile for rustc build + // FIXME: Remove this option at the end of 2026 #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")] - pub rust_profile_use: Option, + pub rust_profile_use: Option, /// use PGO profile for LLVM build + // FIXME: Remove this option at the end of 2026 #[arg(global = true, value_hint = clap::ValueHint::FilePath, long, value_name = "PROFILE")] - pub llvm_profile_use: Option, + pub llvm_profile_use: Option, // LLVM doesn't support a custom location for generating profile // information. // // llvm_out/build/profiles/ is the location this writes to. /// generate PGO profile with llvm built for rustc + // FIXME: Remove this option at the end of 2026 #[arg(global = true, long)] pub llvm_profile_generate: bool, /// Enable BOLT link flags diff --git a/src/bootstrap/src/core/config/toml/mod.rs b/src/bootstrap/src/core/config/toml/mod.rs index f6dc5b67e1010..74c21617746bf 100644 --- a/src/bootstrap/src/core/config/toml/mod.rs +++ b/src/bootstrap/src/core/config/toml/mod.rs @@ -15,6 +15,7 @@ pub mod dist; pub mod gcc; pub mod install; pub mod llvm; +pub mod pgo; pub mod rust; pub mod target; @@ -27,6 +28,7 @@ use llvm::Llvm; use rust::Rust; use target::TomlTarget; +use crate::core::config::toml::pgo::Pgo; use crate::core::config::{Merge, ReplaceOpt}; use crate::{Config, HashMap, HashSet, Path, PathBuf, exit, fs, t}; @@ -47,6 +49,7 @@ pub(crate) struct TomlConfig { pub(super) rust: Option, pub(super) target: Option>, pub(super) dist: Option, + pub(super) pgo: Option, pub(super) profile: Option, pub(super) include: Option>, } @@ -56,7 +59,19 @@ impl Merge for TomlConfig { &mut self, parent_config_path: Option, included_extensions: &mut HashSet, - TomlConfig { build, install, llvm, gcc, rust, dist, target, profile, change_id, include }: Self, + TomlConfig { + build, + install, + llvm, + gcc, + rust, + dist, + target, + pgo, + profile, + change_id, + include, + }: Self, replace: ReplaceOpt, ) { fn do_merge(x: &mut Option, y: Option, replace: ReplaceOpt) { @@ -78,6 +93,7 @@ impl Merge for TomlConfig { do_merge(&mut self.gcc, gcc, replace); do_merge(&mut self.rust, rust, replace); do_merge(&mut self.dist, dist, replace); + do_merge(&mut self.pgo, pgo, replace); match (self.target.as_mut(), target) { (_, None) => {} diff --git a/src/bootstrap/src/core/config/toml/pgo.rs b/src/bootstrap/src/core/config/toml/pgo.rs new file mode 100644 index 0000000000000..b845f3c182b53 --- /dev/null +++ b/src/bootstrap/src/core/config/toml/pgo.rs @@ -0,0 +1,31 @@ +//! This module defines the `Pgo` struct, which represents the `[pgo]` table +//! in the `bootstrap.toml` configuration file. +//! +//! The `[pgo]` table contains options related PGO (Profile-Guided Optimization) of various +//! components built by bootstrap. + +use serde::{Deserialize, Deserializer}; + +use crate::core::config::Merge; +use crate::core::config::toml::ReplaceOpt; +use crate::{HashSet, PathBuf, define_config, exit}; + +#[derive(Clone, Default, Debug, serde_derive::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct PgoConfig { + /// Use the given PGO profile to optimize a component. + #[serde(default, rename = "use")] + pub use_profile: Option, + /// Build a component with PGO instrumentation. Once executed, the profiles will be stored + /// into this path. + #[serde(default, rename = "generate")] + pub generate_profile: Option, +} + +define_config! { + #[derive(Default)] + struct Pgo { + rustc: Option = "rustc", + llvm: Option = "llvm", + } +} diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs index 2facf839521d3..5c0fd37e46c2e 100644 --- a/src/bootstrap/src/core/config/toml/rust.rs +++ b/src/bootstrap/src/core/config/toml/rust.rs @@ -64,8 +64,10 @@ define_config! { ehcont_guard: Option = "ehcont-guard", new_symbol_mangling: Option = "new-symbol-mangling", annotate_moves_size_limit: Option = "annotate-moves-size-limit", - profile_generate: Option = "profile-generate", - profile_use: Option = "profile-use", + // FIXME: Remove this option at the end of 2026 + profile_generate: Option = "profile-generate", + // FIXME: Remove this option at the end of 2026 + profile_use: Option = "profile-use", // ignored; this is set from an env var set by bootstrap.py download_rustc: Option = "download-rustc", lto: Option = "lto", diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs index b34c1b55700f8..94a7acf30d4e9 100644 --- a/src/bootstrap/src/utils/change_tracker.rs +++ b/src/bootstrap/src/utils/change_tracker.rs @@ -636,4 +636,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[ severity: ChangeSeverity::Info, summary: "New option `rust.compress-debuginfo` allows configuring whether Rust and C/C++ debuginfo should be compressed.", }, + ChangeInfo { + change_id: 999999, + severity: ChangeSeverity::Info, + summary: "New config section `pgo` was introduced, to configure PGO profiling options. The `--rust-profile-use`/`--rust-profile-generate`/`--llvm-profile-use`/`--llvm-profile-generate` flags and the `rust.profile-use`/`rust.profile-generate` config options have been deprecated.", + }, ]; 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/opt-dist/src/exec.rs b/src/tools/opt-dist/src/exec.rs index a3935f9835956..7c6b07b8c8bb0 100644 --- a/src/tools/opt-dist/src/exec.rs +++ b/src/tools/opt-dist/src/exec.rs @@ -134,20 +134,21 @@ impl Bootstrap { pub fn llvm_pgo_instrument(mut self, profile_dir: &Utf8Path) -> Self { self.cmd = self .cmd - .arg("--llvm-profile-generate") - .env("LLVM_PROFILE_DIR", profile_dir.join("prof-%p").as_str()); + .arg("--set") + .arg(format!(r#"pgo.llvm.generate="{}""#, profile_dir.join("prof-%p").as_str())); self } pub fn llvm_pgo_optimize(mut self, profile: Option<&LlvmPGOProfile>) -> Self { if let Some(prof) = profile { - self.cmd = self.cmd.arg("--llvm-profile-use").arg(prof.0.as_str()); + self.cmd = self.cmd.arg("--set").arg(format!(r#"pgo.llvm.use="{}""#, prof.0.as_str())); } self } pub fn rustc_pgo_instrument(mut self, profile_dir: &Utf8Path) -> Self { - self.cmd = self.cmd.arg("--rust-profile-generate").arg(profile_dir.as_str()); + self.cmd = + self.cmd.arg("--set").arg(format!(r#"pgo.rustc.generate="{}""#, profile_dir.as_str())); self } @@ -162,7 +163,7 @@ impl Bootstrap { } pub fn rustc_pgo_optimize(mut self, profile: &RustcPGOProfile) -> Self { - self.cmd = self.cmd.arg("--rust-profile-use").arg(profile.0.as_str()); + self.cmd = self.cmd.arg("--set").arg(format!(r#"pgo.rustc.use="{}""#, profile.0.as_str())); self } 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/array-cmp.rs b/tests/codegen-llvm/array-cmp.rs index 0106b9c15c11b..5b0a802f4097e 100644 --- a/tests/codegen-llvm/array-cmp.rs +++ b/tests/codegen-llvm/array-cmp.rs @@ -2,6 +2,9 @@ //@ compile-flags: -C opt-level=2 //@ needs-deterministic-layouts (checks depend on tuple layout) +//@ revisions: LLVM22 LLVM23 +//@ [LLVM22] max-llvm-major-version: 22 +//@ [LLVM23] min-llvm-version: 23 #![crate_type = "lib"] @@ -66,7 +69,8 @@ pub fn array_of_tuple_le(a: &[(i16, u16); 2], b: &[(i16, u16); 2]) -> bool { // CHECK-NEXT: br i1 %[[EQ01]], label %{{.+}}, label %[[EXIT_U]] // CHECK: [[DONE]]: - // CHECK: %[[RET:.+]] = phi i1 [ %{{.+}}, %[[EXIT_S]] ], [ %{{.+}}, %[[EXIT_U]] ], [ true, %[[L11]] ] + // LLVM22: %[[RET:.+]] = phi i1 [ %{{.+}}, %[[EXIT_S]] ], [ %{{.+}}, %[[EXIT_U]] ], [ true, %[[L11]] ] + // LLVM23: %[[RET:.+]] = phi i1 [ %{{.+}}, %[[EXIT_U]] ], [ %{{.+}}, %[[EXIT_S]] ], [ true, %[[L11]] ] // CHECK: ret i1 %[[RET]] a <= b diff --git a/tests/codegen-llvm/enum/enum-match.rs b/tests/codegen-llvm/enum/enum-match.rs index 6d8b97328f8e0..a0cba452e123c 100644 --- a/tests/codegen-llvm/enum/enum-match.rs +++ b/tests/codegen-llvm/enum/enum-match.rs @@ -1,5 +1,8 @@ //@ compile-flags: -Copt-level=1 //@ only-64bit +//@ revisions: LLVM22 LLVM23 +//@ [LLVM22] max-llvm-major-version: 22 +//@ [LLVM23] min-llvm-version: 23 #![crate_type = "lib"] #![feature(core_intrinsics)] @@ -18,8 +21,9 @@ pub enum Enum0 { // CHECK-LABEL: define{{( dso_local)?}} noundef{{( range\(i8 [0-9]+, [0-9]+\))?}} i8 @match0(i8{{.+}}%0) // CHECK-NEXT: start: // CHECK-NEXT: %[[IS_B:.+]] = icmp eq i8 %0, 2 -// CHECK-NEXT: %[[TRUNC:.+]] = and i8 %0, 1 -// CHECK-NEXT: %[[R:.+]] = select i1 %[[IS_B]], i8 13, i8 %[[TRUNC]] +// LLVM22-NEXT: %[[TRUNC:.+]] = and i8 %0, 1 +// LLVM22-NEXT: %[[R:.+]] = select i1 %[[IS_B]], i8 13, i8 %[[TRUNC]] +// LLVM23-NEXT: %[[R:.+]] = select i1 %[[IS_B]], i8 13, i8 %0 // CHECK-NEXT: ret i8 %[[R]] #[no_mangle] pub fn match0(e: Enum0) -> u8 { 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/nll/polonius/allow-backward-incompatible-drop-hint.rs b/tests/ui/nll/polonius/allow-backward-incompatible-drop-hint.rs new file mode 100644 index 0000000000000..b2a9f648809ff --- /dev/null +++ b/tests/ui/nll/polonius/allow-backward-incompatible-drop-hint.rs @@ -0,0 +1,13 @@ +// Test for issue #157568 +//@ compile-flags: -Zpolonius +//@ check-pass + +#![warn(rust_2024_compatibility)] +pub struct F; +impl std::fmt::Debug for F { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("F").finish_non_exhaustive() + } +} + +fn main() {} 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/suggest-add-self-issue-131084.rs b/tests/ui/suggestions/suggest-add-self-issue-131084.rs new file mode 100644 index 0000000000000..adad0f56857d8 --- /dev/null +++ b/tests/ui/suggestions/suggest-add-self-issue-131084.rs @@ -0,0 +1,28 @@ +//@ check-fail + +// A recovered `¶m` should not make the missing-`self` suggestion insert +// the receiver after the leading `&`. + +struct SomeStruct; + +impl SomeStruct { + fn some_fn(&some_name) { + //~^ ERROR expected one of `:`, `@`, or `|`, found `)` + self + //~^ ERROR expected value, found module `self` + } + + fn mut_param(mut some_name) { + //~^ ERROR expected one of `:`, `@`, or `|`, found `)` + self + //~^ ERROR expected value, found module `self` + } + + fn type_before_name(String s) { + //~^ ERROR expected one of `:`, `@`, or `|`, found `s` + self + //~^ ERROR expected value, found module `self` + } +} + +fn main() {} diff --git a/tests/ui/suggestions/suggest-add-self-issue-131084.stderr b/tests/ui/suggestions/suggest-add-self-issue-131084.stderr new file mode 100644 index 0000000000000..1af7eec726273 --- /dev/null +++ b/tests/ui/suggestions/suggest-add-self-issue-131084.stderr @@ -0,0 +1,93 @@ +error: expected one of `:`, `@`, or `|`, found `)` + --> $DIR/suggest-add-self-issue-131084.rs:9:26 + | +LL | fn some_fn(&some_name) { + | ^ expected one of `:`, `@`, or `|` + | +help: if this is a `self` type, give it a parameter name + | +LL | fn some_fn(self: &some_name) { + | +++++ +help: if this is a parameter name, give it a type + | +LL - fn some_fn(&some_name) { +LL + fn some_fn(some_name: &TypeName) { + | +help: if this is a type, explicitly ignore the parameter name + | +LL | fn some_fn(_: &some_name) { + | ++ + +error: expected one of `:`, `@`, or `|`, found `)` + --> $DIR/suggest-add-self-issue-131084.rs:15:31 + | +LL | fn mut_param(mut some_name) { + | ^ expected one of `:`, `@`, or `|` + | +help: if this is a `self` type, give it a parameter name + | +LL | fn mut_param(self: mut some_name) { + | +++++ +help: if this is a parameter name, give it a type + | +LL | fn mut_param(mut some_name: TypeName) { + | ++++++++++ +help: if this is a type, explicitly ignore the parameter name + | +LL | fn mut_param(_: mut some_name) { + | ++ + +error: expected one of `:`, `@`, or `|`, found `s` + --> $DIR/suggest-add-self-issue-131084.rs:21:32 + | +LL | fn type_before_name(String s) { + | -------^ + | | | + | | expected one of `:`, `@`, or `|` + | help: declare the type after the parameter binding: `: ` + +error[E0424]: expected value, found module `self` + --> $DIR/suggest-add-self-issue-131084.rs:11:9 + | +LL | fn some_fn(&some_name) { + | ------- this function doesn't have a `self` parameter +LL | +LL | self + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + | +help: add a `self` receiver parameter to make the associated `fn` a method + | +LL | fn some_fn(&self, &some_name) { + | ++++++ + +error[E0424]: expected value, found module `self` + --> $DIR/suggest-add-self-issue-131084.rs:17:9 + | +LL | fn mut_param(mut some_name) { + | --------- this function doesn't have a `self` parameter +LL | +LL | self + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + | +help: add a `self` receiver parameter to make the associated `fn` a method + | +LL | fn mut_param(&self, mut some_name) { + | ++++++ + +error[E0424]: expected value, found module `self` + --> $DIR/suggest-add-self-issue-131084.rs:23:9 + | +LL | fn type_before_name(String s) { + | ---------------- this function doesn't have a `self` parameter +LL | +LL | self + | ^^^^ `self` value is a keyword only available in methods with a `self` parameter + | +help: add a `self` receiver parameter to make the associated `fn` a method + | +LL | fn type_before_name(&self, String s) { + | ++++++ + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0424`. 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),