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::