From 0c8e36095dac2a419ddb3403d6302bd7ed7541d1 Mon Sep 17 00:00:00 2001 From: TechnoPorg Date: Wed, 9 Sep 2026 13:59:41 -0400 Subject: [PATCH 01/25] RISC-V: Add zmmul target feature --- compiler/rustc_target/src/target_features.rs | 3 ++- library/std_detect/src/detect/arch/riscv.rs | 3 +++ library/std_detect/src/detect/os/riscv.rs | 2 ++ library/std_detect/tests/cpu-detection.rs | 1 + src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs | 1 + tests/ui/check-cfg/target_feature.stderr | 1 + 6 files changed, 10 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 68ece92fbc9c0..d3f799efbdd4b 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -692,7 +692,7 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ }, &[], ), - ("m", Stable, &[]), + ("m", Stable, &["zmmul"]), ("relax", Unstable(sym::riscv_target_feature), &[]), ( "rva23u64", @@ -793,6 +793,7 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("zksed", Stable, &[]), ("zksh", Stable, &[]), ("zkt", Stable, &[]), + ("zmmul", Unstable(sym::riscv_target_feature), &[]), ("ztso", Stable, &[]), ("zvbb", Unstable(sym::riscv_target_feature), &["zvkb"]), // Zvbb ⊃ Zvkb ("zvbc", Unstable(sym::riscv_target_feature), &["zve64x"]), diff --git a/library/std_detect/src/detect/arch/riscv.rs b/library/std_detect/src/detect/arch/riscv.rs index 0e6bab512ac15..0fe37873b0bb9 100644 --- a/library/std_detect/src/detect/arch/riscv.rs +++ b/library/std_detect/src/detect/arch/riscv.rs @@ -104,6 +104,7 @@ features! { /// | `"zksed"` | Zksed | 6.8 | /// | `"zksh"` | Zksh | 6.8 | /// | `"zkt"` | Zkt | 6.8 | + /// | `"zmmul"` | Zmmul | Yes [^ima] [^dep] | /// | `"ztso"` | Ztso | 6.8 | /// | `"zvbb"` | Zvbb | 6.8 | /// | `"zvbc"` | Zvbc | 6.8 | @@ -220,6 +221,8 @@ features! { @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] m: "m"; /// "M" Extension for Integer Multiplication and Division + @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zmmul: "zmmul"; + //// "Zmmul" Extension for Integer Multiplication @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] a: "a"; /// "A" Extension for Atomic Instructions diff --git a/library/std_detect/src/detect/os/riscv.rs b/library/std_detect/src/detect/os/riscv.rs index 9b9e0cba09d1c..d4315f5cc1d87 100644 --- a/library/std_detect/src/detect/os/riscv.rs +++ b/library/std_detect/src/detect/os/riscv.rs @@ -147,6 +147,8 @@ pub(crate) fn imply_features(mut value: cache::Initializer) -> cache::Initialize imply!(zicntr | zihpm | f | zfinx | zve32x => zicsr); + imply!(m => zmmul); + // Loop until the feature flags converge. if prev == value { return value; diff --git a/library/std_detect/tests/cpu-detection.rs b/library/std_detect/tests/cpu-detection.rs index f0b276072108d..a381d84cfbbc3 100644 --- a/library/std_detect/tests/cpu-detection.rs +++ b/library/std_detect/tests/cpu-detection.rs @@ -243,6 +243,7 @@ fn riscv_linux() { println!("zicboz: {}", is_riscv_feature_detected!("zicboz")); println!("zicond: {}", is_riscv_feature_detected!("zicond")); println!("m: {}", is_riscv_feature_detected!("m")); + println!("zmmul: {}", is_riscv_feature_detected!("zmmul")); println!("a: {}", is_riscv_feature_detected!("a")); println!("zalrsc: {}", is_riscv_feature_detected!("zalrsc")); println!("zaamo: {}", is_riscv_feature_detected!("zaamo")); diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs index 29a933f922630..7664d87cdee3e 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/target_feature.rs @@ -204,6 +204,7 @@ const TARGET_FEATURE_IMPLICATIONS_RAW: &[(&str, &[&str])] = &[ // RISC-V ("a", &["zaamo", "zalrsc"]), ("d", &["f"]), + ("m", &["zmmul"]), ("zabha", &["zaamo"]), ("zdinx", &["zfinx"]), ("zfh", &["zfhmin"]), diff --git a/tests/ui/check-cfg/target_feature.stderr b/tests/ui/check-cfg/target_feature.stderr index 0d9aad0e4a193..13c49fe00ef1f 100644 --- a/tests/ui/check-cfg/target_feature.stderr +++ b/tests/ui/check-cfg/target_feature.stderr @@ -479,6 +479,7 @@ LL | cfg!(target_feature = "_UNEXPECTED_VALUE"); `zksed` `zksh` `zkt` +`zmmul` `zreg` `ztso` `zvbb` From 7a6e24abe1a27ce5e1465dacc57df9937461ef30 Mon Sep 17 00:00:00 2001 From: TechnoPorg Date: Thu, 10 Sep 2026 08:24:54 -0400 Subject: [PATCH 02/25] Document interaction between "m" and "zmmul" --- compiler/rustc_target/src/target_features.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index d3f799efbdd4b..97ec07f1a36e5 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -692,6 +692,14 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ }, &[], ), + // According to the RISC-V spec, the M ISA extension (integer multiplication/division) is supported only when the M bit + // of the misa register is 1, while Zmmul means integer multiplication is always supported, even on systems with a writable misa. + // + // The Rust/LLVM "m" target feature means something slightly different: with "m" enabled, it is expected that multiplication + // and division will be unconditionally available (M bit will always be 1 in misa); therefore, "m" implies "zmmul" in the context + // of target features. + // + // See discussion in the PR adding Zmmul: https://github.com/rust-lang/rust/pull/162552 ("m", Stable, &["zmmul"]), ("relax", Unstable(sym::riscv_target_feature), &[]), ( From d609aff297ea369fcab7142f3363bb1b197f5de0 Mon Sep 17 00:00:00 2001 From: TechnoPorg Date: Fri, 11 Sep 2026 08:40:48 -0400 Subject: [PATCH 03/25] Tweak wording of comment in target_features.rs --- compiler/rustc_target/src/target_features.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index 97ec07f1a36e5..e8970ea57e822 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -693,11 +693,10 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ &[], ), // According to the RISC-V spec, the M ISA extension (integer multiplication/division) is supported only when the M bit - // of the misa register is 1, while Zmmul means integer multiplication is always supported, even on systems with a writable misa. + // of the misa register is 1, while Zmmul means integer multiplication is always supported. // - // The Rust/LLVM "m" target feature means something slightly different: with "m" enabled, it is expected that multiplication - // and division will be unconditionally available (M bit will always be 1 in misa); therefore, "m" implies "zmmul" in the context - // of target features. + // The Rust/LLVM "m" target feature means something slightly different: with "m" enabled, it is assumed that integer + // multiplication and division will always be available (M bit will be 1 in misa), so "m" implies "zmmul". // // See discussion in the PR adding Zmmul: https://github.com/rust-lang/rust/pull/162552 ("m", Stable, &["zmmul"]), From a33e6eb2ca3b4bcb6479b7c94e85586216c26ff8 Mon Sep 17 00:00:00 2001 From: TechnoPorg Date: Fri, 11 Sep 2026 10:45:30 -0400 Subject: [PATCH 04/25] Address review comments --- library/std_detect/src/detect/arch/riscv.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/std_detect/src/detect/arch/riscv.rs b/library/std_detect/src/detect/arch/riscv.rs index 0fe37873b0bb9..9e8648823e7a3 100644 --- a/library/std_detect/src/detect/arch/riscv.rs +++ b/library/std_detect/src/detect/arch/riscv.rs @@ -104,7 +104,7 @@ features! { /// | `"zksed"` | Zksed | 6.8 | /// | `"zksh"` | Zksh | 6.8 | /// | `"zkt"` | Zkt | 6.8 | - /// | `"zmmul"` | Zmmul | Yes [^ima] [^dep] | + /// | `"zmmul"` | Zmmul | No [^ima] [^dep] | /// | `"ztso"` | Ztso | 6.8 | /// | `"zvbb"` | Zvbb | 6.8 | /// | `"zvbc"` | Zvbc | 6.8 | @@ -222,7 +222,7 @@ features! { @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] m: "m"; /// "M" Extension for Integer Multiplication and Division @FEATURE: #[unstable(feature = "stdarch_riscv_feature_detection", issue = "111192")] zmmul: "zmmul"; - //// "Zmmul" Extension for Integer Multiplication + /// "Zmmul" Extension for Integer Multiplication @FEATURE: #[stable(feature = "riscv_ratified", since = "1.78.0")] a: "a"; /// "A" Extension for Atomic Instructions From aadf13c861a431da65d58c3f5cc98c89ea92b7df Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 13 Sep 2026 16:39:59 +0200 Subject: [PATCH 05/25] add `non_zst_field` helper --- compiler/rustc_abi/src/layout/ty.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index 27ec34f519870..b53f5cacb6256 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -301,6 +301,29 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { found } + /// Finds the one field that is not a ZST. + /// Returns `None` if there are multiple non-ZST fields or only ZST-fields. + /// + /// Note that this function checks for ZSTs, not just 1-ZSTs. + pub fn non_zst_field_ignore_alignment(&self, cx: &C) -> Option<(FieldIdx, Self)> + where + Ty: TyAbiInterface<'a, C> + Copy, + { + let mut found = None; + for field_idx in 0..self.fields.count() { + let field = self.field(cx, field_idx); + if field.is_zst() { + continue; + } + if found.is_some() { + // More than one non-ZST field. + return None; + } + found = Some((FieldIdx::from_usize(field_idx), field)); + } + found + } + /// If this type should match the ABI of the C `_Complex` type, returns the primitive that is /// used for its components. /// From ee524a2ab79c49ca592d0d0c687204b8d53cc87f Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sat, 29 Aug 2026 21:39:09 +0200 Subject: [PATCH 06/25] fix `is_single_fp_element` for `s390x` and `x86` --- compiler/rustc_abi/src/callconv/reg.rs | 1 + compiler/rustc_abi/src/layout/ty.rs | 20 --- compiler/rustc_codegen_llvm/src/va_arg.rs | 14 +- compiler/rustc_target/src/callconv/s390x.rs | 48 +++++- compiler/rustc_target/src/callconv/x86.rs | 35 ++++- .../s390x-abi/single-fp-element.rs | 129 ++++++++++++++++ .../codegen-llvm/x86-abi/single-fp-element.rs | 146 ++++++++++++++++++ 7 files changed, 369 insertions(+), 24 deletions(-) create mode 100644 tests/codegen-llvm/s390x-abi/single-fp-element.rs create mode 100644 tests/codegen-llvm/x86-abi/single-fp-element.rs diff --git a/compiler/rustc_abi/src/callconv/reg.rs b/compiler/rustc_abi/src/callconv/reg.rs index 745a2ecfc6159..51d6b8a87f2d8 100644 --- a/compiler/rustc_abi/src/callconv/reg.rs +++ b/compiler/rustc_abi/src/callconv/reg.rs @@ -38,6 +38,7 @@ impl Reg { reg_ctor!(i64, Integer, 64); reg_ctor!(i128, Integer, 128); + reg_ctor!(f16, Float, 16); reg_ctor!(f32, Float, 32); reg_ctor!(f64, Float, 64); reg_ctor!(f128, Float, 128); diff --git a/compiler/rustc_abi/src/layout/ty.rs b/compiler/rustc_abi/src/layout/ty.rs index b53f5cacb6256..b27c112249a15 100644 --- a/compiler/rustc_abi/src/layout/ty.rs +++ b/compiler/rustc_abi/src/layout/ty.rs @@ -155,26 +155,6 @@ impl<'a, Ty> TyAndLayout<'a, Ty> { Ty::ty_and_layout_pointee_info_at(self, cx, offset) } - pub fn is_single_fp_element(self, cx: &C) -> bool - where - Ty: TyAbiInterface<'a, C>, - C: HasDataLayout, - { - match self.backend_repr { - BackendRepr::Scalar(scalar) => { - matches!(scalar.primitive(), Primitive::Float(Float::F32 | Float::F64)) - } - BackendRepr::Memory { .. } => { - if self.fields.count() == 1 && self.fields.offset(0).bytes() == 0 { - self.field(cx, 0).is_single_fp_element(cx) - } else { - false - } - } - _ => false, - } - } - pub fn is_single_vector_element(self, cx: &C, expected_size: Size) -> bool where Ty: TyAbiInterface<'a, C>, diff --git a/compiler/rustc_codegen_llvm/src/va_arg.rs b/compiler/rustc_codegen_llvm/src/va_arg.rs index 8d2a504d4877b..4afa1b5f23761 100644 --- a/compiler/rustc_codegen_llvm/src/va_arg.rs +++ b/compiler/rustc_codegen_llvm/src/va_arg.rs @@ -474,7 +474,19 @@ fn emit_s390x_va_arg<'ll, 'tcx>( let padded_size = 8; let padding = padded_size - unpadded_size; - let gpr_type = indirect || !layout.is_single_fp_element(bx.cx); + // NOTE: if we ever allow aggregate types, this should handle structs with a single fp element. + let is_single_fp_element = |layout: TyAndLayout<'_>| -> bool { + match layout.layout.backend_repr() { + BackendRepr::Scalar(scalar) => match scalar.primitive() { + Primitive::Float(Float::F16 | Float::F32 | Float::F64) => true, + Primitive::Float(Float::F128) => false, + Primitive::Int(_, _) | Primitive::Pointer(_) => false, + }, + _ => false, + } + }; + + let gpr_type = indirect || !is_single_fp_element(layout); let (max_regs, reg_count, reg_save_index, reg_padding) = if gpr_type { (5, gpr, 2, padding) } else { (4, fpr, 16, 0) }; diff --git a/compiler/rustc_target/src/callconv/s390x.rs b/compiler/rustc_target/src/callconv/s390x.rs index f0d9675de34f4..0d29bc658b56d 100644 --- a/compiler/rustc_target/src/callconv/s390x.rs +++ b/compiler/rustc_target/src/callconv/s390x.rs @@ -1,11 +1,44 @@ // Reference: ELF Application Binary Interface s390x Supplement // https://github.com/IBM/s390x-abi -use rustc_abi::{BackendRepr, HasDataLayout, TyAbiInterface}; +use rustc_abi::{BackendRepr, FieldsShape, HasDataLayout, Primitive, TyAbiInterface, TyAndLayout}; use crate::callconv::{ArgAbi, FnAbi, Reg}; use crate::spec::{Env, HasTargetSpec, Os}; +/// Is this a struct with a single float field? +fn is_single_fp_element<'a, Ty, C>(mut layout: TyAndLayout<'a, Ty>, cx: &C) -> bool +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout, +{ + // Contrary to X86, trailing padding is allowed on s390x. + + loop { + layout = layout.peel_transparent_wrappers(cx); + + return match layout.backend_repr { + BackendRepr::Scalar(scalar) => match scalar.primitive() { + Primitive::Float(_) => true, + Primitive::Int(_, _) | Primitive::Pointer(_) => false, + }, + BackendRepr::Memory { .. } => { + // A single-element array or union does not qualify. + if let FieldsShape::Arbitrary { .. } = layout.fields + && layout.fields.count() == 1 + && layout.fields.offset(0).bytes() == 0 + { + layout = layout.field(cx, 0); + continue; + } else { + false + } + } + _ => false, + }; + } +} + fn classify_ret(ret: &mut ArgAbi<'_, Ty>) { let size = ret.layout.size; if size.bits() <= 128 && matches!(ret.layout.backend_repr, BackendRepr::SimdVector { .. }) { @@ -65,8 +98,19 @@ where return; } - if arg.layout.is_single_fp_element(cx) { + if is_single_fp_element(arg.layout, cx) { + // Match GCC and Clang by explicitly passing padding, even though their behavior violates + // (our reading of) the specification, which says that: + // + // > Structures equivalent to a floating point type are passed in floating point registers. + // > A structure is equivalent to a floating point type if and only if it has exactly one + // > member, which is either of floating point type of itself a structure equivalent to a + // > floating point type. + // + // When the alignment is higher than 8, we pass the argument indirectly, which violates + // the specification but is consistent with GCC and Clang. match size.bytes() { + 2 => arg.cast_to(Reg::f16()), 4 => arg.cast_to(Reg::f32()), 8 => arg.cast_to(Reg::f64()), _ => arg.make_indirect(), diff --git a/compiler/rustc_target/src/callconv/x86.rs b/compiler/rustc_target/src/callconv/x86.rs index fd608fcf62919..a1c59d885b7fc 100644 --- a/compiler/rustc_target/src/callconv/x86.rs +++ b/compiler/rustc_target/src/callconv/x86.rs @@ -5,6 +5,38 @@ use rustc_abi::{ use crate::callconv::{ArgAttribute, FnAbi, PassMode, TyAbiInterface}; use crate::spec::{HasTargetSpec, RustcAbi}; +/// Is this a struct with a single float field? +fn is_single_fp_element<'a, Ty, C>(mut layout: TyAndLayout<'a, Ty>, cx: &C) -> bool +where + Ty: TyAbiInterface<'a, C> + Copy, + C: HasDataLayout, +{ + // On X86 over-aligned structs are disqualified. + let outer_size = layout.layout.size(); + + loop { + layout = layout.peel_transparent_wrappers(cx); + + return match layout.backend_repr { + BackendRepr::Scalar(scalar) => match scalar.primitive() { + Primitive::Float(float) => float.size() == outer_size, + Primitive::Int(_, _) | Primitive::Pointer(_) => false, + }, + BackendRepr::Memory { .. } => { + // Structs, unions and arrays all qualify. + if let Some((_idx, field)) = layout.non_zst_field_ignore_alignment(cx) { + // NOTE: alignment is not relevant here, checking for 1-ZST is incorrect. + layout = field; + continue; + } else { + false + } + } + _ => false, + }; + } +} + #[derive(PartialEq)] pub(crate) enum Flavor { General, @@ -42,8 +74,9 @@ where { // According to Clang, everyone but MSVC returns single-element // float aggregates directly in a floating-point register. - if fn_abi.ret.layout.is_single_fp_element(cx) { + if is_single_fp_element(fn_abi.ret.layout, cx) { match fn_abi.ret.layout.size.bytes() { + 2 => fn_abi.ret.cast_to(Reg::f16()), 4 => fn_abi.ret.cast_to(Reg::f32()), 8 => fn_abi.ret.cast_to(Reg::f64()), _ => fn_abi.ret.make_indirect(), diff --git a/tests/codegen-llvm/s390x-abi/single-fp-element.rs b/tests/codegen-llvm/s390x-abi/single-fp-element.rs new file mode 100644 index 0000000000000..857fa7e3b2737 --- /dev/null +++ b/tests/codegen-llvm/s390x-abi/single-fp-element.rs @@ -0,0 +1,129 @@ +//@ add-minicore +//@ needs-llvm-components: systemz +//@ compile-flags: --target=s390x-unknown-linux-gnu -Copt-level=3 -Zmerge-functions=disabled +#![crate_type = "lib"] +#![feature(no_core, f16, f128)] +#![no_core] + +extern crate minicore; +use minicore::hint::black_box; +use minicore::*; + +#[repr(C)] +struct Wrapper(T); + +// CHECK-LABEL: define void @plain_f16(half noundef %x) +#[unsafe(no_mangle)] +extern "C" fn plain_f16(x: f16) { + black_box(x); +} + +// CHECK-LABEL: define void @wrapped_f16(half %0) +#[unsafe(no_mangle)] +extern "C" fn wrapped_f16(x: Wrapper) { + black_box(x); +} + +// CHECK-LABEL: define void @plain_f32(float noundef %x) +#[unsafe(no_mangle)] +extern "C" fn plain_f32(x: f32) { + black_box(x); +} + +// CHECK-LABEL: define void @wrapped_f32(float %0) +#[unsafe(no_mangle)] +extern "C" fn wrapped_f32(x: Wrapper) { + black_box(x); +} + +// CHECK-LABEL: define void @plain_f64(double noundef %x) +#[unsafe(no_mangle)] +extern "C" fn plain_f64(x: f64) { + black_box(x); +} + +// CHECK-LABEL: define void @wrapped_f64(double %0) +#[unsafe(no_mangle)] +extern "C" fn wrapped_f64(x: Wrapper) { + black_box(x); +} + +// CHECK-LABEL: define void @plain_f128(ptr {{.*}}dereferenceable(16) %x) +#[unsafe(no_mangle)] +extern "C" fn plain_f128(x: f128) { + black_box(x); +} + +// CHECK-LABEL: define void @wrapped_f128(ptr {{.*}}dereferenceable(16) %x) +#[unsafe(no_mangle)] +extern "C" fn wrapped_f128(x: Wrapper) { + black_box(x); +} + +#[repr(transparent)] +struct Transparent(T); + +// CHECK-LABEL: define void @transparent_wrapped_f32(float %0) +#[unsafe(no_mangle)] +extern "C" fn transparent_wrapped_f32(x: Transparent>) { + black_box(x); +} + +// CHECK-LABEL: define void @transparent_transparent_wrapped_f32(float %0) +#[unsafe(no_mangle)] +extern "C" fn transparent_transparent_wrapped_f32(x: Transparent>>) { + black_box(x); +} + +#[repr(C, align(8))] +struct Aligned8Wrapper(T); + +// CHECK-LABEL: define void @aligned_8_wrapped_f16(double %0) +#[unsafe(no_mangle)] +extern "C" fn aligned_8_wrapped_f16(x: Aligned8Wrapper) { + black_box(x); +} + +// CHECK-LABEL: define void @aligned_8_wrapped_f32(double %0) +#[unsafe(no_mangle)] +extern "C" fn aligned_8_wrapped_f32(x: Aligned8Wrapper) { + black_box(x); +} + +#[repr(C, align(16))] +struct Aligned16Wrapper(T); + +// CHECK-LABEL: define void @aligned_16_wrapped_f32(ptr {{.*}}dereferenceable(16) +#[unsafe(no_mangle)] +extern "C" fn aligned_16_wrapped_f32(x: Aligned16Wrapper) { + black_box(x); +} + +#[repr(C)] +union UnionWrapper { + a: T, +} + +// A repr(C) union does not count. +// +// CHECK-LABEL: define void @union_wrapped_f32(i32 %0) +#[unsafe(no_mangle)] +extern "C" fn union_wrapped_f32(x: UnionWrapper) { + black_box(x); +} + +// But a repr(transparent) union does. +// +// CHECK-LABEL: define void @maybe_uninit_f32(float %x) +#[unsafe(no_mangle)] +extern "C" fn maybe_uninit_f32(x: MaybeUninit) { + black_box(x); +} + +// A single-element array also does not count. +// +// CHECK-LABEL: define void @array_f32(i32 %0) +#[unsafe(no_mangle)] +extern "C" fn array_f32(x: [f32; 1]) { + black_box(x); +} diff --git a/tests/codegen-llvm/x86-abi/single-fp-element.rs b/tests/codegen-llvm/x86-abi/single-fp-element.rs new file mode 100644 index 0000000000000..1aa2531570bb8 --- /dev/null +++ b/tests/codegen-llvm/x86-abi/single-fp-element.rs @@ -0,0 +1,146 @@ +//@ add-minicore +//@ needs-llvm-components: x86 +//@ revisions: win linux +//@[win] compile-flags: --target i686-pc-windows-gnu +//@[linux] compile-flags: --target i686-unknown-linux-gnu -Zreg-struct-return=true +//@ compile-flags: -Copt-level=3 -Zmerge-functions=disabled +#![crate_type = "lib"] +#![feature(no_core, f16, f128)] +#![no_core] + +extern crate minicore; +use minicore::hint::black_box; +use minicore::*; + +#[repr(C)] +struct Wrapper(T); + +// CHECK-LABEL: define noundef half @plain_f16( +#[unsafe(no_mangle)] +extern "C" fn plain_f16(x: f16) -> f16 { + x +} + +// CHECK-LABEL: define half @wrapped_f16( +#[unsafe(no_mangle)] +extern "C" fn wrapped_f16(x: Wrapper) -> Wrapper { + x +} + +// CHECK-LABEL: define noundef float @plain_f32( +#[unsafe(no_mangle)] +extern "C" fn plain_f32(x: f32) -> f32 { + x +} + +// CHECK-LABEL: define float @wrapped_f32( +#[unsafe(no_mangle)] +extern "C" fn wrapped_f32(x: Wrapper) -> Wrapper { + x +} + +// CHECK-LABEL: define noundef double @plain_f64( +#[unsafe(no_mangle)] +extern "C" fn plain_f64(x: f64) -> f64 { + x +} + +// CHECK-LABEL: define double @wrapped_f64( +#[unsafe(no_mangle)] +extern "C" fn wrapped_f64(x: Wrapper) -> Wrapper { + x +} + +// CHECK-LABEL: define noundef fp128 @plain_f128( +#[unsafe(no_mangle)] +extern "C" fn plain_f128(x: f128) -> f128 { + x +} + +// CHECK-LABEL: define void @wrapped_f128(ptr {{.*}}sret([16 x i8]) +#[unsafe(no_mangle)] +extern "C" fn wrapped_f128(x: Wrapper) -> Wrapper { + x +} + +#[repr(transparent)] +struct Transparent(T); + +// CHECK-LABEL: define float @transparent_wrapped_f32( +#[unsafe(no_mangle)] +extern "C" fn transparent_wrapped_f32(x: Transparent>) -> Transparent> { + x +} + +// CHECK-LABEL: define float @transparent_transparent_wrapped_f32( +#[unsafe(no_mangle)] +extern "C" fn transparent_transparent_wrapped_f32( + x: Transparent>>, +) -> Transparent>> { + x +} + +#[repr(align(4))] +struct Empty {} + +#[repr(C)] +struct Struct { + f: f32, + a: [i32; 0], + b: Empty, +} + +// One or more aligned ZSTs are fine and do not disqualify the type. +// CHECK-LABEL: define float @aligned_zst_f32( +#[unsafe(no_mangle)] +extern "C" fn aligned_zst_f32(x: Struct) -> Struct { + x +} + +#[repr(C, align(8))] +struct AlignedWrapper(T); + +// Over-aligning disqualifies the type. +// +// CHECK-LABEL: define i64 @aligned_wrapped_f16( +#[unsafe(no_mangle)] +extern "C" fn aligned_wrapped_f16(x: AlignedWrapper) -> AlignedWrapper { + x +} + +// Over-aligning disqualifies the type. +// +// CHECK-LABEL: define i64 @aligned_wrapped_f32( +#[unsafe(no_mangle)] +extern "C" fn aligned_wrapped_f32(x: AlignedWrapper) -> AlignedWrapper { + x +} + +#[repr(C)] +union UnionWrapper { + a: T, +} + +// A repr(C) union does count. +// +// CHECK-LABEL: define float @union_wrapped_f32( +#[unsafe(no_mangle)] +extern "C" fn union_wrapped_f32(x: UnionWrapper) -> UnionWrapper { + x +} + +// A repr(transparent) union does too. +// +// CHECK-LABEL: define float @maybe_uninit_f32( +#[unsafe(no_mangle)] +extern "C" fn maybe_uninit_f32(x: MaybeUninit) -> MaybeUninit { + x +} + +// A single-element array also does count. +// +// CHECK-LABEL: define float @array_f32( +#[unsafe(no_mangle)] +extern "C" fn array_f32(x: [f32; 1]) -> [f32; 1] { + x +} From 33871b954215f4ea8e11a1043360dc5187671df7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 29 Aug 2026 23:13:13 +0200 Subject: [PATCH 07/25] abi/compatibility test: add simple float type tests --- tests/ui/abi/compatibility.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/tests/ui/abi/compatibility.rs b/tests/ui/abi/compatibility.rs index 6071ad9bb435b..e2496726f4b3f 100644 --- a/tests/ui/abi/compatibility.rs +++ b/tests/ui/abi/compatibility.rs @@ -4,12 +4,24 @@ //@ revisions: i686 //@[i686] compile-flags: --target i686-unknown-linux-gnu //@[i686] needs-llvm-components: x86 +//@ revisions: i686-reg-struct-return +//@[i686-reg-struct-return] compile-flags: --target i686-unknown-linux-gnu -Zreg-struct-return=true +//@[i686-reg-struct-return] needs-llvm-components: x86 +//@ revisions: i686-win +//@[i686-win] compile-flags: --target i686-pc-windows-msvc +//@[i686-win] needs-llvm-components: x86 +//@ revisions: i686-win-gnu +//@[i686-win-gnu] compile-flags: --target i686-pc-windows-gnu +//@[i686-win-gnu] needs-llvm-components: x86 //@ revisions: x86-64 //@[x86-64] compile-flags: --target x86_64-unknown-linux-gnu //@[x86-64] needs-llvm-components: x86 //@ revisions: x86-64-win //@[x86-64-win] compile-flags: --target x86_64-pc-windows-msvc //@[x86-64-win] needs-llvm-components: x86 +//@ revisions: x86-64-win-gnu +//@[x86-64-win-gnu] compile-flags: --target x86_64-pc-windows-gnu +//@[x86-64-win-gnu] needs-llvm-components: x86 //@ revisions: arm //@[arm] compile-flags: --target arm-unknown-linux-gnueabi //@[arm] needs-llvm-components: arm @@ -19,6 +31,9 @@ //@ revisions: aarch64 //@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu //@[aarch64] needs-llvm-components: aarch64 +//@ revisions: aarch64-win +//@[aarch64-win] compile-flags: --target aarch64-pc-windows-msvc +//@[aarch64-win] needs-llvm-components: aarch64 //@ revisions: s390x //@[s390x] compile-flags: --target s390x-unknown-linux-gnu //@[s390x] needs-llvm-components: systemz @@ -171,6 +186,11 @@ enum Either2 { Right(U, ()), } +#[repr(C)] +struct ReprC(T); +#[repr(C)] +struct ReprC2(T, U); + #[repr(C)] enum ReprCEnum { Variant1, @@ -240,16 +260,20 @@ macro_rules! test_transparent { } test_transparent!(simple, i32); +test_transparent!(float, f32); test_transparent!(reference, &'static i32); test_transparent!(zst, Zst); test_transparent!(unit, ()); test_transparent!(enum_, Option); test_transparent!(enum_niched, Option<&'static i32>); #[cfg(not(any(target_arch = "mips64")))] -mod tuples { +mod structs_and_tuples { use super::*; + test_transparent!(float_struct, ReprC); // mixing in some floats since they often get special treatment test_transparent!(pair, (i32, f32)); + // a homogeneous repr(C) struct + test_transparent!(c_pair, ReprC2); // chosen to fit into 64bit test_transparent!(triple, (i8, i16, f32)); // Pure-float types that are not ScalarPair seem to be tricky. From 5dd46310416da1074f652d3935956d6b1f03df4e Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Tue, 15 Sep 2026 12:21:54 -0400 Subject: [PATCH 08/25] rustc_{codegen_,}llvm: adapt to new ThreadModel API LLVM 24 has moved from TargetOptions::ThreadModel to specifying thread-model as a module flag. This catches us up. --- compiler/rustc_codegen_llvm/src/context.rs | 9 +++++++++ compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp | 2 ++ 2 files changed, 11 insertions(+) diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index d4d1c950ec3f8..ed256e6d26fdf 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -585,6 +585,15 @@ pub(crate) unsafe fn create_module<'ll>( ); } + if llvm_version >= (24, 0, 0) && sess.target.singlethread(&sess.internal_target_features) { + llvm::add_module_flag_str( + llmod, + llvm::ModuleFlagMergeBehavior::Error, + "thread-model", + "single", + ); + } + // Add module flags specified via -Z llvm_module_flag for (key, value, merge_behavior) in &sess.opts.unstable_opts.llvm_module_flag { let merge_behavior = match merge_behavior.as_str() { diff --git a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp index d181891cdfa54..ec712deafaba0 100644 --- a/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp +++ b/compiler/rustc_llvm/llvm-wrapper/PassWrapper.cpp @@ -425,9 +425,11 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( } } +#if LLVM_VERSION_LT(24, 0) if (Singlethread) { Options.ThreadModel = ThreadModel::Single; } +#endif if (UseWasmEH) Options.ExceptionModel = ExceptionHandling::Wasm; From 3c283d4f47a54a65b2e6766d951c2bf49ac0341c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joosep=20Orasm=C3=A4e?= Date: Sun, 6 Sep 2026 12:26:59 +0300 Subject: [PATCH 09/25] Copy metadata workproduct when session dir rename fails --- compiler/rustc_incremental/src/persist/fs.rs | 35 ++++++++++++++++++- .../rustc_incremental/src/persist/fs/tests.rs | 15 ++++++++ 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 7493653987a91..5decbcbc09af0 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -129,6 +129,7 @@ const DEP_GRAPH_FILENAME: &str = "dep-graph.bin"; const STAGING_DEP_GRAPH_FILENAME: &str = "dep-graph.part.bin"; const WORK_PRODUCTS_FILENAME: &str = "work-products.bin"; const QUERY_CACHE_FILENAME: &str = "query-cache.bin"; +const METADATA_WORK_PRODUCT_FILENAME: &str = "metadata.rmeta"; // We encode integers using the following base, so they are shorter than decimal // or hexadecimal numbers (we want short file and directory names). Since these @@ -332,7 +333,27 @@ pub fn finalize_session_directory( let new_path = incr_comp_session_dir.parent().unwrap().join(&*sub_dir_name); debug!("finalize_session_directory() - new path: {}", new_path.display()); - match rename_path_with_retry(&*incr_comp_session_dir, &new_path, 3) { + let result = std_fs::rename(&*incr_comp_session_dir, &new_path).or_else(|e| { + if e.kind() != ErrorKind::PermissionDenied { + return Err(e); + } + + // On ReFS, renaming a directory that contains a hard link to the metadata workproduct file + // can fail if it is being used by another process (such as another rustc instance). + // As a fallback, we try to replace the hard link with a copy, which should allow the + // rename to succeed. + // See https://github.com/rust-lang/rust/issues/151181 + if let Err(err) = replace_hard_link_with_copy(&in_incr_comp_dir_sess( + &incr_comp_session, + METADATA_WORK_PRODUCT_FILENAME, + )) { + debug!("finalize_session_directory() - error replacing hard link with copy: {}", err); + } + + rename_path_with_retry(&*incr_comp_session_dir, &new_path, 3) + }); + + match result { Ok(_) => { debug!("finalize_session_directory() - directory renamed successfully"); } @@ -893,3 +914,15 @@ fn rename_path_with_retry(from: &Path, to: &Path, mut retries_left: usize) -> st } } } + +/// Turns a hard link of the file at `path` into a copy. +fn replace_hard_link_with_copy(path: &Path) -> std::io::Result<()> { + let tmp_name = path.with_added_extension("tmp"); + + // In case a stale temporary file was linked from a previous failed attempt. + safe_remove_file(&tmp_name)?; + + std_fs::copy(path, &tmp_name).and_then(|_| std_fs::rename(&tmp_name, path)).inspect_err(|_| { + let _ = safe_remove_file(&tmp_name); + }) +} diff --git a/compiler/rustc_incremental/src/persist/fs/tests.rs b/compiler/rustc_incremental/src/persist/fs/tests.rs index 644b8187621c9..3652656b7c48f 100644 --- a/compiler/rustc_incremental/src/persist/fs/tests.rs +++ b/compiler/rustc_incremental/src/persist/fs/tests.rs @@ -75,3 +75,18 @@ fn test_find_source_directory_in_iter() { None ); } + +#[test] +fn test_replace_hard_link_with_copy_unshares_hard_link() { + let dir = rustc_fs_util::TempDirBuilder::new().tempdir_in(std::env::temp_dir()).unwrap(); + let file = dir.path().join("file"); + let link = dir.path().join("link"); + std_fs::write(&file, b"original").unwrap(); + std_fs::hard_link(&file, &link).unwrap(); + + replace_hard_link_with_copy(&link).unwrap(); + + std_fs::write(&file, b"changed").unwrap(); + assert_eq!(std_fs::read(&link).unwrap(), b"original"); + assert!(!link.with_added_extension("tmp").exists()); +} From 26e7e774b191c43e96beb27840d613400fd5b8db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joosep=20Orasm=C3=A4e?= Date: Wed, 9 Sep 2026 21:24:31 +0300 Subject: [PATCH 10/25] Gate finalize_session_directory rename fallbacks to windows --- compiler/rustc_incremental/src/persist/fs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 5decbcbc09af0..70045f6a0fcff 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -334,7 +334,7 @@ pub fn finalize_session_directory( debug!("finalize_session_directory() - new path: {}", new_path.display()); let result = std_fs::rename(&*incr_comp_session_dir, &new_path).or_else(|e| { - if e.kind() != ErrorKind::PermissionDenied { + if !cfg!(windows) || e.kind() != ErrorKind::PermissionDenied { return Err(e); } From b4beb1eeeabc82c0e4a73dc3472c64e7871e1613 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 16 Sep 2026 11:32:17 +0200 Subject: [PATCH 11/25] Pass File to Fallback::lock --- compiler/rustc_data_structures/src/flock.rs | 2 +- compiler/rustc_data_structures/src/flock/unix.rs | 12 ++---------- .../rustc_data_structures/src/flock/unsupported.rs | 4 ++-- 3 files changed, 5 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_data_structures/src/flock.rs b/compiler/rustc_data_structures/src/flock.rs index 3e3aa32f52eaa..123e7cff3ac75 100644 --- a/compiler/rustc_data_structures/src/flock.rs +++ b/compiler/rustc_data_structures/src/flock.rs @@ -42,7 +42,7 @@ impl Lock { match res { Ok(()) => Ok(Lock::FdLocked { _file: file }), Err(err) if matches!(err.kind(), io::ErrorKind::Unsupported) => { - Ok(Lock::Fallback(fallback::Lock::new(p, wait, create, exclusive)?)) + Ok(Lock::Fallback(fallback::Lock::new(file, wait, exclusive)?)) } Err(err) => Err(err), } diff --git a/compiler/rustc_data_structures/src/flock/unix.rs b/compiler/rustc_data_structures/src/flock/unix.rs index af1a6b1479531..b41070855654b 100644 --- a/compiler/rustc_data_structures/src/flock/unix.rs +++ b/compiler/rustc_data_structures/src/flock/unix.rs @@ -1,6 +1,5 @@ -use std::fs::{File, OpenOptions}; +use std::fs::File; use std::os::unix::prelude::*; -use std::path::Path; use std::{io, mem}; #[derive(Debug)] @@ -9,14 +8,7 @@ pub struct Lock { } impl Lock { - pub fn new(p: &Path, wait: bool, create: bool, exclusive: bool) -> io::Result { - let file = OpenOptions::new() - .read(true) - .write(true) - .create(create) - .mode(libc::S_IRWXU as u32) - .open(p)?; - + pub fn new(file: File, wait: bool, exclusive: bool) -> io::Result { let lock_type = if exclusive { libc::F_WRLCK } else { libc::F_RDLCK }; let mut flock: libc::flock = unsafe { mem::zeroed() }; diff --git a/compiler/rustc_data_structures/src/flock/unsupported.rs b/compiler/rustc_data_structures/src/flock/unsupported.rs index 6775784749763..e2f1d8ce22f95 100644 --- a/compiler/rustc_data_structures/src/flock/unsupported.rs +++ b/compiler/rustc_data_structures/src/flock/unsupported.rs @@ -1,11 +1,11 @@ +use std::fs::File; use std::io; -use std::path::Path; #[derive(Debug)] pub struct Lock(()); impl Lock { - pub fn new(_p: &Path, _wait: bool, _create: bool, _exclusive: bool) -> io::Result { + pub fn new(_f: File, _wait: bool, _exclusive: bool) -> io::Result { let msg = "file locks not supported on this platform"; Err(io::Error::new(io::ErrorKind::Unsupported, msg)) } From 0143ce1a521e90002ef00d2d189a01a21367b627 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 16 Sep 2026 11:35:09 +0200 Subject: [PATCH 12/25] Have a separate copy of the flock code for rustdoc This copy will retain and is specialized for blocking operations. --- Cargo.lock | 1 + src/librustdoc/Cargo.toml | 1 + src/librustdoc/html/render/write_shared.rs | 5 +- .../html/render/write_shared/flock.rs | 92 +++++++++++++++++++ 4 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/librustdoc/html/render/write_shared/flock.rs diff --git a/Cargo.lock b/Cargo.lock index 36213a1e4e481..2b56dc6ab1641 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5055,6 +5055,7 @@ dependencies = [ "expect-test", "indexmap", "itertools", + "libc", "minifier", "proc-macro2", "pulldown-cmark-escape", diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 19600ff2bb63e..4d7895264c158 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -14,6 +14,7 @@ askama = { version = "0.16.1", default-features = false, features = ["alloc", "c base64 = "0.21.7" indexmap = { version = "2", features = ["serde"] } itertools = "0.15" +libc = "0.2" minifier = { version = "0.4.0", default-features = false } proc-macro2 = "1.0.103" pulldown-cmark-escape = { version = "0.11.0", features = ["simd"] } diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index ab72edacae296..5c963afd3a221 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -26,7 +26,6 @@ use std::{fmt, fs}; use indexmap::IndexMap; use rustc_ast::join_path_syms; -use rustc_data_structures::flock; use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet}; use rustc_middle::ty::TyCtxt; use rustc_middle::ty::fast_reject::DeepRejectCtxt; @@ -57,6 +56,8 @@ use crate::html::static_files::{self, suffix_path}; use crate::visit::DocVisitor; use crate::{DOC_RUST_LANG_ORG_VERSION, try_err, try_none}; +mod flock; + pub(crate) fn write_shared( cx: &mut Context<'_>, krate: &Crate, @@ -67,7 +68,7 @@ pub(crate) fn write_shared( cx.shared.fs.set_sync_only(true); let lock_file = cx.dst.join(".lock"); // Write shared runs within a flock; disable thread dispatching of IO temporarily. - let _lock = try_err!(flock::Lock::new(&lock_file, true, true, true), &lock_file); + let _lock = try_err!(flock::Lock::new(&lock_file), &lock_file); let search_index = build_index( krate, diff --git a/src/librustdoc/html/render/write_shared/flock.rs b/src/librustdoc/html/render/write_shared/flock.rs new file mode 100644 index 0000000000000..385c0bcc84cd3 --- /dev/null +++ b/src/librustdoc/html/render/write_shared/flock.rs @@ -0,0 +1,92 @@ +use std::fs::{File, OpenOptions}; +use std::io; +#[cfg(unix)] +use std::os::unix::prelude::*; +use std::path::Path; + +#[derive(Debug)] +pub(super) enum Lock { + /// A well behaved lock scoped to a single fd/handle and unlocked when closing it. + #[doc(hidden)] + FdLocked { _file: File }, + /// A fallback implementation using the legacy `fcntl(F_SETLK)` which is scoped to + /// an entire process. This should only be used when `flock()` or equivalent sane + /// locking mechanism is unsupported by the OS. + #[doc(hidden)] + #[cfg(unix)] + FcntlFallback { _lock: FcntlLock }, +} + +impl Lock { + pub(super) fn new(p: &Path) -> io::Result { + let mut open_options = OpenOptions::new(); + open_options.read(true).write(true).create(true); + #[cfg(unix)] + { + use std::os::unix::fs::OpenOptionsExt; + open_options.mode(0o600); + } + + let file = open_options.open(p)?; + + match file.lock() { + Ok(()) => Ok(Lock::FdLocked { _file: file }), + #[cfg(unix)] + Err(err) if matches!(err.kind(), io::ErrorKind::Unsupported) => { + Ok(Lock::FcntlFallback { _lock: FcntlLock::new(file)? }) + } + Err(err) => Err(err), + } + } +} + +#[derive(Debug)] +#[cfg(unix)] +pub(super) struct FcntlLock { + file: File, +} + +#[cfg(unix)] +impl FcntlLock { + fn new(file: File) -> io::Result { + let mut flock: libc::flock = unsafe { std::mem::zeroed() }; + #[cfg(not(all(target_os = "hurd", target_arch = "x86")))] + { + flock.l_type = libc::F_WRLCK as libc::c_short; + flock.l_whence = libc::SEEK_SET as libc::c_short; + } + #[cfg(all(target_os = "hurd", target_arch = "x86"))] + { + flock.l_type = libc::F_WRLCK as libc::c_int; + flock.l_whence = libc::SEEK_SET as libc::c_int; + } + flock.l_start = 0; + flock.l_len = 0; + + let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETLKW, &flock) }; + if ret == -1 { Err(io::Error::last_os_error()) } else { Ok(FcntlLock { file }) } + } +} + +#[cfg(unix)] +impl Drop for FcntlLock { + fn drop(&mut self) { + let mut flock: libc::flock = unsafe { std::mem::zeroed() }; + #[cfg(not(all(target_os = "hurd", target_arch = "x86")))] + { + flock.l_type = libc::F_UNLCK as libc::c_short; + flock.l_whence = libc::SEEK_SET as libc::c_short; + } + #[cfg(all(target_os = "hurd", target_arch = "x86"))] + { + flock.l_type = libc::F_UNLCK as libc::c_int; + flock.l_whence = libc::SEEK_SET as libc::c_int; + } + flock.l_start = 0; + flock.l_len = 0; + + unsafe { + libc::fcntl(self.file.as_raw_fd(), libc::F_SETLK, &flock); + } + } +} From 435b07d29f2ff479bc26d635d9ae54947f773f0c Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 10 Sep 2026 16:43:10 +0200 Subject: [PATCH 13/25] Remove wait argument from Lock::new Incr comp only uses the non-blocking variant. --- compiler/rustc_data_structures/src/flock.rs | 13 ++++++------- compiler/rustc_data_structures/src/flock/unix.rs | 5 ++--- .../rustc_data_structures/src/flock/unsupported.rs | 2 +- compiler/rustc_incremental/src/persist/fs.rs | 14 +++++--------- 4 files changed, 14 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_data_structures/src/flock.rs b/compiler/rustc_data_structures/src/flock.rs index 123e7cff3ac75..0eb05e5229d54 100644 --- a/compiler/rustc_data_structures/src/flock.rs +++ b/compiler/rustc_data_structures/src/flock.rs @@ -21,7 +21,7 @@ pub enum Lock { } impl Lock { - pub fn new(p: &Path, wait: bool, create: bool, exclusive: bool) -> io::Result { + pub fn try_lock(p: &Path, create: bool, exclusive: bool) -> io::Result { let mut open_options = OpenOptions::new(); open_options.read(true).write(true).create(create); #[cfg(unix)] @@ -32,17 +32,16 @@ impl Lock { let file = open_options.open(p)?; - let res = match (wait, exclusive) { - (true, true) => file.lock(), - (true, false) => file.lock_shared(), - (false, true) => file.try_lock().map_err(io::Error::from), - (false, false) => file.try_lock_shared().map_err(io::Error::from), + let res = if exclusive { + file.try_lock().map_err(io::Error::from) + } else { + file.try_lock_shared().map_err(io::Error::from) }; match res { Ok(()) => Ok(Lock::FdLocked { _file: file }), Err(err) if matches!(err.kind(), io::ErrorKind::Unsupported) => { - Ok(Lock::Fallback(fallback::Lock::new(file, wait, exclusive)?)) + Ok(Lock::Fallback(fallback::Lock::try_lock(file, exclusive)?)) } Err(err) => Err(err), } diff --git a/compiler/rustc_data_structures/src/flock/unix.rs b/compiler/rustc_data_structures/src/flock/unix.rs index b41070855654b..e9ff4a60b5793 100644 --- a/compiler/rustc_data_structures/src/flock/unix.rs +++ b/compiler/rustc_data_structures/src/flock/unix.rs @@ -8,7 +8,7 @@ pub struct Lock { } impl Lock { - pub fn new(file: File, wait: bool, exclusive: bool) -> io::Result { + pub fn try_lock(file: File, exclusive: bool) -> io::Result { let lock_type = if exclusive { libc::F_WRLCK } else { libc::F_RDLCK }; let mut flock: libc::flock = unsafe { mem::zeroed() }; @@ -25,8 +25,7 @@ impl Lock { flock.l_start = 0; flock.l_len = 0; - let cmd = if wait { libc::F_SETLKW } else { libc::F_SETLK }; - let ret = unsafe { libc::fcntl(file.as_raw_fd(), cmd, &flock) }; + let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETLK, &flock) }; if ret == -1 { Err(io::Error::last_os_error()) } else { Ok(Lock { file }) } } } diff --git a/compiler/rustc_data_structures/src/flock/unsupported.rs b/compiler/rustc_data_structures/src/flock/unsupported.rs index e2f1d8ce22f95..12d2a993858d8 100644 --- a/compiler/rustc_data_structures/src/flock/unsupported.rs +++ b/compiler/rustc_data_structures/src/flock/unsupported.rs @@ -5,7 +5,7 @@ use std::io; pub struct Lock(()); impl Lock { - pub fn new(_f: File, _wait: bool, _exclusive: bool) -> io::Result { + pub fn try_lock(_f: File, _exclusive: bool) -> io::Result { let msg = "file locks not supported on this platform"; Err(io::Error::new(io::ErrorKind::Unsupported, msg)) } diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 7493653987a91..ba80975c3317a 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -366,9 +366,8 @@ fn copy_files(sess: &Session, target_dir: &Path, source_dir: &Path) -> Result (flock::Lock, PathBuf) let lock_file_path = lock_file_path(session_dir); debug!("lock_directory() - lock_file: {}", lock_file_path.display()); - match flock::Lock::new( + match flock::Lock::try_lock( &lock_file_path, - false, // don't wait - true, // create the lock file + true, // create the lock file true, ) { // the lock should be exclusive @@ -712,9 +710,8 @@ pub(crate) fn garbage_collect_session_directories( if is_finalized(directory_name) { let lock_file_path = crate_directory.join(lock_file_name); - match flock::Lock::new( + match flock::Lock::try_lock( &lock_file_path, - false, // don't wait false, // don't create the lock-file true, ) { @@ -781,9 +778,8 @@ pub(crate) fn garbage_collect_session_directories( // means that the owning process is still alive and we // leave this directory alone. let lock_file_path = crate_directory.join(lock_file_name); - match flock::Lock::new( + match flock::Lock::try_lock( &lock_file_path, - false, // don't wait false, // don't create the lock-file true, ) { From de50895c4c8c723f8bc9ed633330cb968f84ea57 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 10 Sep 2026 17:58:30 +0200 Subject: [PATCH 14/25] Introduce a global lock registry for the fcntl lock fallback This lock registry allows emulating fd based locks (like flock()) on top of the fcntl(F_SETLK) process based locks, thereby ensuring that concurrent rustc instances inside the same process don't break the incr comp locking scheme. --- compiler/rustc_data_structures/src/flock.rs | 2 +- .../rustc_data_structures/src/flock/unix.rs | 90 ++++++++++++++++++- .../src/flock/unsupported.rs | 3 +- 3 files changed, 89 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_data_structures/src/flock.rs b/compiler/rustc_data_structures/src/flock.rs index 0eb05e5229d54..9bd7d06c27f5e 100644 --- a/compiler/rustc_data_structures/src/flock.rs +++ b/compiler/rustc_data_structures/src/flock.rs @@ -41,7 +41,7 @@ impl Lock { match res { Ok(()) => Ok(Lock::FdLocked { _file: file }), Err(err) if matches!(err.kind(), io::ErrorKind::Unsupported) => { - Ok(Lock::Fallback(fallback::Lock::try_lock(file, exclusive)?)) + Ok(Lock::Fallback(fallback::Lock::try_lock(p, file, exclusive)?)) } Err(err) => Err(err), } diff --git a/compiler/rustc_data_structures/src/flock/unix.rs b/compiler/rustc_data_structures/src/flock/unix.rs index e9ff4a60b5793..bceeb84ff52fa 100644 --- a/compiler/rustc_data_structures/src/flock/unix.rs +++ b/compiler/rustc_data_structures/src/flock/unix.rs @@ -1,14 +1,96 @@ use std::fs::File; use std::os::unix::prelude::*; +use std::path::{Path, PathBuf}; +use std::sync::{Arc, LazyLock, Mutex}; use std::{io, mem}; +use rustc_hash::FxHashMap; + +static LOCK_REGISTRY: LazyLock>> = + LazyLock::new(|| Mutex::new(FxHashMap::default())); + +enum LockState { + /// Lock exclusively held. `Lock.file` contains an `Arc` with a single reference. + /// + /// The `extra_files` fields contains files we opened while the lock was held. We have to + /// persist them until we actually want to unlock the file to prevent unlocking on close. + Exclusive { extra_files: Vec }, + /// Lock can be shared. When there are N lock holders, the `Arc` has N+1 references + /// with the last one being held by `LockState` and getting removed in the drop impl of `Lock` + /// if it is the remaining reference. + Shared(Arc), +} + #[derive(Debug)] pub struct Lock { - file: File, + path: PathBuf, + file: Option>, } impl Lock { - pub fn try_lock(file: File, exclusive: bool) -> io::Result { + pub fn try_lock(p: &Path, file: File, exclusive: bool) -> io::Result { + let mut locks = LOCK_REGISTRY.lock().unwrap(); + + let file = if let Some(state) = locks.get_mut(p) { + // We must not open the file again if there is an existing lock to prevent the close + // from unlocking the file even when another `Lock` already had the lock held before + // this `Lock::try_lock` call. + match state { + LockState::Exclusive { extra_files } => { + // Retain file to prevent unlock on close + extra_files.push(file); + + return Err(io::ErrorKind::WouldBlock.into()); + } + LockState::Shared(file) => { + if exclusive { + return Err(io::ErrorKind::WouldBlock.into()); + } else { + Arc::clone(file) + } + } + } + } else { + let file = Arc::new(UnlockGuard::try_lock(file, exclusive)?); + + if exclusive { + locks.insert(p.to_owned(), LockState::Exclusive { extra_files: vec![] }); + } else { + locks.insert(p.to_owned(), LockState::Shared(Arc::clone(&file))); + } + + file + }; + + Ok(Lock { path: p.to_owned(), file: Some(file) }) + } +} + +impl Drop for Lock { + fn drop(&mut self) { + let mut locks = LOCK_REGISTRY.lock().unwrap(); + self.file.take().unwrap(); + match locks.get_mut(&self.path).unwrap() { + LockState::Exclusive { extra_files: _ } => { + locks.remove(&self.path); + } + LockState::Shared(file) => { + if Arc::strong_count(file) == 1 { + locks.remove(&self.path); + } + } + } + } +} + +/// A file guard which will unlock the file when dropped. +#[derive(Debug)] +struct UnlockGuard { + file: File, +} + +impl UnlockGuard { + fn try_lock(file: File, exclusive: bool) -> io::Result { let lock_type = if exclusive { libc::F_WRLCK } else { libc::F_RDLCK }; let mut flock: libc::flock = unsafe { mem::zeroed() }; @@ -26,11 +108,11 @@ impl Lock { flock.l_len = 0; let ret = unsafe { libc::fcntl(file.as_raw_fd(), libc::F_SETLK, &flock) }; - if ret == -1 { Err(io::Error::last_os_error()) } else { Ok(Lock { file }) } + if ret == -1 { Err(io::Error::last_os_error()) } else { Ok(Self { file }) } } } -impl Drop for Lock { +impl Drop for UnlockGuard { fn drop(&mut self) { let mut flock: libc::flock = unsafe { mem::zeroed() }; #[cfg(not(all(target_os = "hurd", target_arch = "x86")))] diff --git a/compiler/rustc_data_structures/src/flock/unsupported.rs b/compiler/rustc_data_structures/src/flock/unsupported.rs index 12d2a993858d8..17b83e252d243 100644 --- a/compiler/rustc_data_structures/src/flock/unsupported.rs +++ b/compiler/rustc_data_structures/src/flock/unsupported.rs @@ -1,11 +1,12 @@ use std::fs::File; use std::io; +use std::path::Path; #[derive(Debug)] pub struct Lock(()); impl Lock { - pub fn try_lock(_f: File, _exclusive: bool) -> io::Result { + pub fn try_lock(_p: &Path, _f: File, _exclusive: bool) -> io::Result { let msg = "file locks not supported on this platform"; Err(io::Error::new(io::ErrorKind::Unsupported, msg)) } From 0bae8cff69147bd419d03251ec8bc3a63823c676 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joosep=20Orasm=C3=A4e?= Date: Tue, 15 Sep 2026 21:05:27 +0300 Subject: [PATCH 15/25] Move metadata work product name into a constant --- compiler/rustc_incremental/src/persist/fs.rs | 9 +++++++-- compiler/rustc_interface/src/queries.rs | 6 +++--- compiler/rustc_metadata/src/rmeta/encoder.rs | 9 +++++---- compiler/rustc_middle/src/dep_graph/graph.rs | 7 +++++++ 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_incremental/src/persist/fs.rs b/compiler/rustc_incremental/src/persist/fs.rs index 70045f6a0fcff..a6394e905865d 100644 --- a/compiler/rustc_incremental/src/persist/fs.rs +++ b/compiler/rustc_incremental/src/persist/fs.rs @@ -115,6 +115,8 @@ use rustc_data_structures::svh::Svh; use rustc_data_structures::unord::{UnordMap, UnordSet}; use rustc_data_structures::{base_n, flock}; use rustc_fs_util::{LinkOrCopy, link_or_copy, try_canonicalize}; +use rustc_middle::dep_graph::WorkProduct; +use rustc_session::config::OutputType; use rustc_session::{IncrCompSession, Session, StableCrateId}; use rustc_span::{Symbol, bug}; use tracing::debug; @@ -129,7 +131,6 @@ const DEP_GRAPH_FILENAME: &str = "dep-graph.bin"; const STAGING_DEP_GRAPH_FILENAME: &str = "dep-graph.part.bin"; const WORK_PRODUCTS_FILENAME: &str = "work-products.bin"; const QUERY_CACHE_FILENAME: &str = "query-cache.bin"; -const METADATA_WORK_PRODUCT_FILENAME: &str = "metadata.rmeta"; // We encode integers using the following base, so they are shorter than decimal // or hexadecimal numbers (we want short file and directory names). Since these @@ -345,7 +346,11 @@ pub fn finalize_session_directory( // See https://github.com/rust-lang/rust/issues/151181 if let Err(err) = replace_hard_link_with_copy(&in_incr_comp_dir_sess( &incr_comp_session, - METADATA_WORK_PRODUCT_FILENAME, + &format!( + "{}.{}", + WorkProduct::METADATA_WORKPRODUCT_CGU_NAME, + OutputType::Metadata.extension() + ), )) { debug!("finalize_session_directory() - error replacing hard link with copy: {}", err); } diff --git a/compiler/rustc_interface/src/queries.rs b/compiler/rustc_interface/src/queries.rs index 51e26d4ba5044..2f196c5e5d609 100644 --- a/compiler/rustc_interface/src/queries.rs +++ b/compiler/rustc_interface/src/queries.rs @@ -7,7 +7,7 @@ use rustc_data_structures::svh::Svh; use rustc_errors::timings::TimingSection; use rustc_hir::def_id::LOCAL_CRATE; use rustc_metadata::EncodedMetadata; -use rustc_middle::dep_graph::{DepGraph, WorkProductMap}; +use rustc_middle::dep_graph::{DepGraph, WorkProduct, WorkProductMap}; use rustc_middle::ty::TyCtxt; use rustc_session::config::{self, OutputFilenames, OutputType}; use rustc_session::{IncrCompSession, Session}; @@ -99,8 +99,8 @@ impl Linker { let (id, product) = rustc_incremental::copy_cgu_workproduct_to_incr_comp_cache_dir( sess, incr_comp_session.as_ref().unwrap(), - "metadata", - &[("rmeta", path)], + WorkProduct::METADATA_WORKPRODUCT_CGU_NAME, + &[(OutputType::Metadata.extension(), path)], &[], ); work_products.insert(id, product); diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index 12c8fac8cc2eb..8481db9d3c523 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -18,7 +18,7 @@ use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId, LocalDefIdSet}; use rustc_hir::definitions::DefPathData; use rustc_hir::find_attr; use rustc_hir_pretty::id_to_string; -use rustc_middle::dep_graph::WorkProductId; +use rustc_middle::dep_graph::{WorkProduct, WorkProductId}; use rustc_middle::middle::dependency_format::Linkage; use rustc_middle::mir::interpret; use rustc_middle::query::Providers; @@ -28,7 +28,7 @@ use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::fast_reject::{self, TreatParams}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder, opaque}; use rustc_session::config::mitigation_coverage::DeniedPartialMitigation; -use rustc_session::config::{OptLevel, TargetModifier}; +use rustc_session::config::{OptLevel, OutputType, TargetModifier}; use rustc_span::def_id::CRATE_MOD_ID; use rustc_span::hygiene::HygieneEncodeContext; use rustc_span::{ @@ -2502,11 +2502,12 @@ pub fn encode_metadata(tcx: TyCtxt<'_>, path: &Path, ref_path: Option<&Path>) { // If the metadata dep-node is green, try to reuse the saved work product. if tcx.dep_graph.is_fully_enabled() - && let work_product_id = WorkProductId::from_cgu_name("metadata") + && let work_product_id = + WorkProductId::from_cgu_name(WorkProduct::METADATA_WORKPRODUCT_CGU_NAME) && let Some(work_product) = tcx.dep_graph.previous_work_product(&work_product_id) && tcx.dep_graph.try_mark_green(tcx, &dep_node).is_some() { - let saved_path = &work_product.saved_files["rmeta"]; + let saved_path = &work_product.saved_files[OutputType::Metadata.extension()]; let incr_comp_session_dir = &tcx.incr_comp_session.unwrap().session_directory; let source_file_in_incr_dir = &incr_comp_session_dir.join(saved_path); debug!("copying preexisting metadata from {source_file_in_incr_dir:?} to {path:?}"); diff --git a/compiler/rustc_middle/src/dep_graph/graph.rs b/compiler/rustc_middle/src/dep_graph/graph.rs index a4165d793069d..dcb5775f20595 100644 --- a/compiler/rustc_middle/src/dep_graph/graph.rs +++ b/compiler/rustc_middle/src/dep_graph/graph.rs @@ -1143,6 +1143,13 @@ pub struct WorkProduct { pub saved_files: UnordMap, } +impl WorkProduct { + /// The metadata work product is not produced by any CGU and thus its + /// name cannot be derived from `CodegenUnit`. Both the writers and readers + /// of the metadata work product use this constant to agree on the name. + pub const METADATA_WORKPRODUCT_CGU_NAME: &str = "metadata"; +} + pub type WorkProductMap = UnordMap; // Index type for `DepNodeData`'s edges. From 4138d0279786a23635ff11fbe6b7abdcff4e37f1 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 17 Sep 2026 12:28:36 +1000 Subject: [PATCH 16/25] Remove redundant "add bound" help messages Borrow checking produces some weird, spanless help messages about fixing lifetime errors. If there are multiple, they are grouped under a header like this: ``` help: the following changes may resolve your lifetime errors ``` There are three forms of these. One of the forms is this: ``` = help: add bound `'a: 'b` ``` This form is redundant, because there is always an earlier help note (on a proper error message with a span) like this: ``` = help: consider adding the following bound: `'b: 'a` ``` This commit removes these redundant messages. --- .../src/diagnostics/outlives_suggestion.rs | 28 ++++--------------- tests/ui/fn/fn_def_coercion.stderr | 5 ---- tests/ui/lub-glb/empty-binders-err.stderr | 5 ---- ...gate-approximated-both-lower-bounds.stderr | 5 ---- tests/ui/nll/outlives-suggestion-more.stderr | 10 +------ 5 files changed, 6 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index b1e16717466b5..64344d8fe7226 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -14,9 +14,6 @@ use crate::MirBorrowckCtxt; /// The different things we could suggest. enum SuggestedConstraint { - /// Outlives(a, [b, c, d, ...]) => 'a: 'b + 'c + 'd + ... - Outlives(RegionName, SmallVec<[RegionName; 2]>), - /// 'a = 'b Equal(RegionName, RegionName), @@ -106,11 +103,10 @@ impl OutlivesSuggestionBuilder { continue; } - // There are three types of suggestions we can make: - // 1) Suggest a bound: 'a: 'b - // 2) Suggest replacing 'a with 'static. If any of `outlived` is `'static`, then we + // There are two types of suggestions we can make: + // 1) Suggest replacing 'a with 'static. If any of `outlived` is `'static`, then we // should just replace 'a with 'static. - // 3) Suggest unifying 'a with 'b if we have both 'a: 'b and 'b: 'a + // 2) Suggest unifying 'a with 'b if we have both 'a: 'b and 'b: 'a if outlived .iter() @@ -121,7 +117,7 @@ impl OutlivesSuggestionBuilder { // We want to isolate out all lifetimes that should be unified and print out // separate messages for them. - let (unified, other): (Vec<_>, Vec<_>) = outlived.into_iter().partition( + let unified = outlived.into_iter().filter( // Do we have both 'fr: 'r and 'r: 'fr? |(r, _)| { self.constraints_to_add @@ -130,17 +126,12 @@ impl OutlivesSuggestionBuilder { }, ); - for (r, bound) in unified.into_iter() { + for (r, bound) in unified { if !unified_already.contains(fr) { suggested.push(SuggestedConstraint::Equal(fr_name, bound)); unified_already.insert(r); } } - - if !other.is_empty() { - let other = other.iter().map(|(_, rname)| *rname).collect::>(); - suggested.push(SuggestedConstraint::Outlives(fr_name, other)) - } } } @@ -206,11 +197,6 @@ impl OutlivesSuggestionBuilder { // list of diagnostics. let mut diag = if let [constraint] = suggested.as_slice() { mbcx.dcx().struct_help(match constraint { - SuggestedConstraint::Outlives(a, bs) => { - let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect(); - format!("add bound `{a}: {}`", bs.join(" + ")) - } - SuggestedConstraint::Equal(a, b) => { format!("`{a}` and `{b}` must be the same: replace one with the other") } @@ -227,10 +213,6 @@ impl OutlivesSuggestionBuilder { // Add suggestions. for constraint in suggested { match constraint { - SuggestedConstraint::Outlives(a, bs) => { - let bs: SmallVec<[String; 2]> = bs.iter().map(|r| r.to_string()).collect(); - diag.help(format!("add bound `{a}: {}`", bs.join(" + "))); - } SuggestedConstraint::Equal(a, b) => { diag.help(format!( "`{a}` and `{b}` must be the same: replace one with the other", diff --git a/tests/ui/fn/fn_def_coercion.stderr b/tests/ui/fn/fn_def_coercion.stderr index 85e234c839341..2db2721b7dec1 100644 --- a/tests/ui/fn/fn_def_coercion.stderr +++ b/tests/ui/fn/fn_def_coercion.stderr @@ -148,10 +148,5 @@ LL | false => foo::<&'a ()>, = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance -help: the following changes may resolve your lifetime errors - | - = help: add bound `'a: 'c` - = help: add bound `'b: 'a` - error: aborting due to 9 previous errors diff --git a/tests/ui/lub-glb/empty-binders-err.stderr b/tests/ui/lub-glb/empty-binders-err.stderr index f86f22d5e40bf..68b62c87e85b9 100644 --- a/tests/ui/lub-glb/empty-binders-err.stderr +++ b/tests/ui/lub-glb/empty-binders-err.stderr @@ -24,11 +24,6 @@ LL | let _: &'upper () = match v { | = help: consider adding the following bound: `'b: 'upper` -help: the following changes may resolve your lifetime errors - | - = help: add bound `'a: 'upper` - = help: add bound `'b: 'upper` - error: lifetime may not live long enough --> $DIR/empty-binders-err.rs:35:12 | diff --git a/tests/ui/nll/closure-requirements/propagate-approximated-both-lower-bounds.stderr b/tests/ui/nll/closure-requirements/propagate-approximated-both-lower-bounds.stderr index af7ea253cc52a..38c1d9c5efc9a 100644 --- a/tests/ui/nll/closure-requirements/propagate-approximated-both-lower-bounds.stderr +++ b/tests/ui/nll/closure-requirements/propagate-approximated-both-lower-bounds.stderr @@ -70,10 +70,5 @@ LL | | ); = note: the struct `Cell` is invariant over the parameter `T` = help: see for more information about variance -help: the following changes may resolve your lifetime errors - | - = help: add bound `'a: 'c` - = help: add bound `'b: 'c` - error: aborting due to 2 previous errors diff --git a/tests/ui/nll/outlives-suggestion-more.stderr b/tests/ui/nll/outlives-suggestion-more.stderr index c8c604b5b4c78..e8fbc2ff7b437 100644 --- a/tests/ui/nll/outlives-suggestion-more.stderr +++ b/tests/ui/nll/outlives-suggestion-more.stderr @@ -22,11 +22,6 @@ LL | (x, y) | = help: consider adding the following bound: `'b: 'd` -help: the following changes may resolve your lifetime errors - | - = help: add bound `'a: 'c` - = help: add bound `'b: 'd` - error: lifetime may not live long enough --> $DIR/outlives-suggestion-more.rs:11:5 | @@ -47,10 +42,7 @@ LL | fn foo2<'a, 'b, 'c>(x: &'a usize, y: &'b usize) -> (&'c usize, &'static usi LL | (x, y) | ^^^^^^ returning this value requires that `'b` must outlive `'static` -help: the following changes may resolve your lifetime errors - | - = help: add bound `'a: 'c` - = help: replace `'b` with `'static` +help: replace `'b` with `'static` error: lifetime may not live long enough --> $DIR/outlives-suggestion-more.rs:21:5 From 82ddf4c8d209a7a6cfd627cb721a07ec305063b6 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 17 Sep 2026 15:33:13 +1000 Subject: [PATCH 17/25] Change "the following changes may resolve your lifetime errors" diagnostic It's currently a spanless top-level help diagnostic. This commit changes it to an error (with one or more help subdiagnostics), gives it a span, and changes the wording. Note that giving it a span means that deduplication will no longer erroneously hide diagnostics with identical wording from different functions! Because of this, several "duplicate diagnostic" mentions are removed from the expected outputs. It's still a weird diagnostic (and now counts towards the error count) but it's better than before. This also removes the one use of buffered non-error diagnostics. So `BufferedDiag` is also removed. --- .../rustc_borrowck/src/diagnostics/mod.rs | 38 +++----------- .../src/diagnostics/outlives_suggestion.rs | 51 ++++++++----------- ...ated-types-project-from-hrtb-in-fn-body.rs | 2 +- ...-types-project-from-hrtb-in-fn-body.stderr | 10 +++- ...ect-fn-ret-contravariant.krisskross.stderr | 14 +++-- .../cache/project-fn-ret-contravariant.rs | 1 + ...project-fn-ret-invariant.krisskross.stderr | 14 +++-- .../project-fn-ret-invariant.oneuse.stderr | 16 ++++-- .../cache/project-fn-ret-invariant.rs | 2 + .../project-fn-ret-invariant.transmute.stderr | 2 +- ...ptr-to-trait-obj-different-regions-misc.rs | 6 +-- ...to-trait-obj-different-regions-misc.stderr | 26 +++++++--- .../invariant.next.stderr | 16 ++++-- .../invariant.old.stderr | 16 ++++-- .../ui/field_representing_types/invariant.rs | 1 + tests/ui/fn/fn_def_coercion.rs | 6 +-- tests/ui/fn/fn_def_coercion.stderr | 26 +++++++--- .../ui/impl-trait/precise-capturing/rpitit.rs | 2 +- .../precise-capturing/rpitit.stderr | 10 +++- ...-preserve-equality.borrowck_current.stderr | 10 +++- tests/ui/nll/outlives-suggestion-more.rs | 4 +- tests/ui/nll/outlives-suggestion-more.stderr | 16 ++++-- tests/ui/nll/outlives-suggestion-simple.rs | 2 +- .../ui/nll/outlives-suggestion-simple.stderr | 10 +++- .../flow-sensitive-invariance.nll.stderr | 18 ++++--- .../flow-sensitive-invariance.polonius.stderr | 18 ++++--- .../nll/polonius/flow-sensitive-invariance.rs | 2 + tests/ui/nll/type-check-pointer-coercions.rs | 2 +- .../nll/type-check-pointer-coercions.stderr | 10 +++- .../ui/nll/type-check-pointer-comparisons.rs | 6 +-- .../nll/type-check-pointer-comparisons.stderr | 26 +++++++--- .../nll/user-annotations/normalization-2.rs | 14 ++--- .../user-annotations/normalization-2.stderr | 46 +++++++++++++---- tests/ui/regions/regions-infer-not-param.rs | 2 +- .../ui/regions/regions-infer-not-param.stderr | 10 +++- 35 files changed, 293 insertions(+), 162 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/mod.rs b/compiler/rustc_borrowck/src/diagnostics/mod.rs index 4229a0723ce49..5ebf950358c4f 100644 --- a/compiler/rustc_borrowck/src/diagnostics/mod.rs +++ b/compiler/rustc_borrowck/src/diagnostics/mod.rs @@ -74,20 +74,6 @@ pub(super) struct DescribePlaceOpt { pub(super) struct IncludingTupleField(pub(super) bool); -pub(crate) enum BufferedDiag<'diag> { - Error(Diag<'diag>), - NonError(Diag<'diag, ()>), -} - -impl<'diag> BufferedDiag<'diag> { - fn sort_span(&self) -> Span { - match self { - BufferedDiag::Error(diag) => diag.sort_span, - BufferedDiag::NonError(diag) => diag.sort_span, - } - } -} - #[derive(Default)] pub(crate) struct BorrowckDiagnosticsBuffer<'diag, 'tcx> { /// This field keeps track of move errors that are to be reported for given move indices. @@ -108,16 +94,13 @@ pub(crate) struct BorrowckDiagnosticsBuffer<'diag, 'tcx> { buffered_mut_errors: FxIndexMap, usize)>, - /// Buffer of diagnostics to be reported. A mixture of error and non-error diagnostics. - buffered_diags: Vec>, + /// Buffer of diagnostics to be reported. + buffered_diags: Vec>, } impl<'diag, 'tcx> BorrowckDiagnosticsBuffer<'diag, 'tcx> { - pub(crate) fn buffer_non_error(&mut self, diag: Diag<'diag, ()>) { - self.buffered_diags.push(BufferedDiag::NonError(diag)); - } pub(crate) fn buffer_error(&mut self, diag: Diag<'diag>) { - self.buffered_diags.push(BufferedDiag::Error(diag)); + self.buffered_diags.push(diag); } pub(crate) fn emit_errors(&mut self) { @@ -134,14 +117,9 @@ impl<'diag, 'tcx> BorrowckDiagnosticsBuffer<'diag, 'tcx> { } if !self.buffered_diags.is_empty() { - self.buffered_diags.sort_by_key(|buffered_diag| buffered_diag.sort_span()); - for buffered_diag in self.buffered_diags.drain(..) { - match buffered_diag { - BufferedDiag::Error(diag) => { - diag.emit(); - } - BufferedDiag::NonError(diag) => diag.emit(), - } + self.buffered_diags.sort_by_key(|diag| diag.sort_span); + for diag in self.buffered_diags.drain(..) { + diag.emit(); } } } @@ -152,10 +130,6 @@ impl<'diag, 'tcx> MirBorrowckCtxt<'_, 'diag, 'tcx> { self.diags_buffer.buffer_error(diag.with_dcx(self.dcx())); } - pub(crate) fn buffer_non_error(&mut self, diag: Diag<'_, ()>) { - self.diags_buffer.buffer_non_error(diag.with_dcx(self.dcx())); - } - pub(crate) fn buffer_move_error( &mut self, move_out_indices: Vec, diff --git a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs index 64344d8fe7226..75c666f49a2f7 100644 --- a/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs +++ b/compiler/rustc_borrowck/src/diagnostics/outlives_suggestion.rs @@ -193,45 +193,36 @@ impl OutlivesSuggestionBuilder { return; } - // If there is exactly one suggestable constraints, then just suggest it. Otherwise, emit a - // list of diagnostics. - let mut diag = if let [constraint] = suggested.as_slice() { - mbcx.dcx().struct_help(match constraint { + // Emit an error with a list of one or more help suggestions. This is a weird error because + // it's just there to provide somewhere to put the help suggestions that describe how to + // fix the one or more borrow errors already reported within the item. + let tcx = mbcx.infcx.tcx; + let def_id = mbcx.mir_def_id(); + let span = tcx.def_ident_span(def_id).unwrap_or_else(|| tcx.def_span(def_id)); + let mut diag = tcx + .dcx() + .struct_err("one or more lifetime errors were found in this item") + .with_span(span); + + // Add suggestions. + for constraint in suggested { + match constraint { SuggestedConstraint::Equal(a, b) => { - format!("`{a}` and `{b}` must be the same: replace one with the other") + diag.help(format!( + "`{a}` and `{b}` must be the same: replace one with the other", + )); } - SuggestedConstraint::Static(a) => format!("replace `{a}` with `'static`"), - }) - } else { - // Create a new diagnostic. - let mut diag = mbcx - .infcx - .tcx - .dcx() - .struct_help("the following changes may resolve your lifetime errors"); - - // Add suggestions. - for constraint in suggested { - match constraint { - SuggestedConstraint::Equal(a, b) => { - diag.help(format!( - "`{a}` and `{b}` must be the same: replace one with the other", - )); - } - SuggestedConstraint::Static(a) => { - diag.help(format!("replace `{a}` with `'static`")); - } + SuggestedConstraint::Static(a) => { + diag.help(format!("replace `{a}` with `'static`")); } } - - diag - }; + } // We want this message to appear after other messages on the mir def. let mir_span = mbcx.body.span; diag.sort_span = mir_span.shrink_to_hi(); // Buffer the diagnostic - mbcx.buffer_non_error(diag); + mbcx.buffer_error(diag); } } diff --git a/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.rs b/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.rs index 069bf56004461..9646e45c55cdb 100644 --- a/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.rs +++ b/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.rs @@ -13,7 +13,7 @@ fn foo<'a, I : for<'x> Foo<&'x isize>>( let y: I::A = x; } -fn bar<'a, 'b, I : for<'x> Foo<&'x isize>>( +fn bar<'a, 'b, I : for<'x> Foo<&'x isize>>( //~ ERROR one or more lifetime errors x: >::A, y: >::A, cond: bool) diff --git a/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.stderr b/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.stderr index 42d83fca6ca83..15567a9143a18 100644 --- a/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.stderr +++ b/tests/ui/associated-types/associated-types-project-from-hrtb-in-fn-body.stderr @@ -24,7 +24,13 @@ LL | let z: I::A = if cond { x } else { y }; | = help: consider adding the following bound: `'a: 'b` -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/associated-types-project-from-hrtb-in-fn-body.rs:16:4 + | +LL | fn bar<'a, 'b, I : for<'x> Foo<&'x isize>>( + | ^^^ + | + = help: `'a` and `'b` must be the same: replace one with the other -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/associated-types/cache/project-fn-ret-contravariant.krisskross.stderr b/tests/ui/associated-types/cache/project-fn-ret-contravariant.krisskross.stderr index 2ecee1341abd3..1cfdde9ff1057 100644 --- a/tests/ui/associated-types/cache/project-fn-ret-contravariant.krisskross.stderr +++ b/tests/ui/associated-types/cache/project-fn-ret-contravariant.krisskross.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/project-fn-ret-contravariant.rs:46:4 + --> $DIR/project-fn-ret-contravariant.rs:47:4 | LL | fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) { | -- -- lifetime `'b` defined here @@ -12,7 +12,7 @@ LL | (a, b) = help: consider adding the following bound: `'a: 'b` error: lifetime may not live long enough - --> $DIR/project-fn-ret-contravariant.rs:46:4 + --> $DIR/project-fn-ret-contravariant.rs:47:4 | LL | fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) { | -- -- lifetime `'b` defined here @@ -24,7 +24,13 @@ LL | (a, b) | = help: consider adding the following bound: `'b: 'a` -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/project-fn-ret-contravariant.rs:43:4 + | +LL | fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) { + | ^^^^^^^^^ + | + = help: `'a` and `'b` must be the same: replace one with the other -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/associated-types/cache/project-fn-ret-contravariant.rs b/tests/ui/associated-types/cache/project-fn-ret-contravariant.rs index 6763155790086..44a03a9df639e 100644 --- a/tests/ui/associated-types/cache/project-fn-ret-contravariant.rs +++ b/tests/ui/associated-types/cache/project-fn-ret-contravariant.rs @@ -41,6 +41,7 @@ fn baz<'a,'b>(x: &'a u32) -> &'static u32 { #[cfg(krisskross)] // two instantiations, mixing and matching: BAD fn transmute<'a,'b>(x: &'a u32, y: &'b u32) -> (&'a u32, &'b u32) { + //[krisskross]~^ ERROR one or more lifetime errors let a = bar(foo, y); let b = bar(foo, x); (a, b) //[krisskross]~ ERROR lifetime may not live long enough diff --git a/tests/ui/associated-types/cache/project-fn-ret-invariant.krisskross.stderr b/tests/ui/associated-types/cache/project-fn-ret-invariant.krisskross.stderr index ada12c7ee91d0..86189e9493247 100644 --- a/tests/ui/associated-types/cache/project-fn-ret-invariant.krisskross.stderr +++ b/tests/ui/associated-types/cache/project-fn-ret-invariant.krisskross.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/project-fn-ret-invariant.rs:59:5 + --> $DIR/project-fn-ret-invariant.rs:61:5 | LL | fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { | -- -- lifetime `'b` defined here @@ -15,7 +15,7 @@ LL | (a, b) = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/project-fn-ret-invariant.rs:59:5 + --> $DIR/project-fn-ret-invariant.rs:61:5 | LL | fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { | -- -- lifetime `'b` defined here @@ -30,7 +30,13 @@ LL | (a, b) = note: the struct `Type<'a>` is invariant over the parameter `'a` = help: see for more information about variance -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/project-fn-ret-invariant.rs:57:4 + | +LL | fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { + | ^^^^^^^^^ + | + = help: `'a` and `'b` must be the same: replace one with the other -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr b/tests/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr index 3ef6b85c407af..ad17002821d71 100644 --- a/tests/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr +++ b/tests/ui/associated-types/cache/project-fn-ret-invariant.oneuse.stderr @@ -1,11 +1,11 @@ error: lifetime may not live long enough - --> $DIR/project-fn-ret-invariant.rs:40:13 + --> $DIR/project-fn-ret-invariant.rs:41:13 | LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here -LL | let f = foo; // <-- No consistent type can be inferred for `f` here. +... LL | let a = bar(f, x); | ^^^^^^^^^ argument requires that `'b` must outlive `'a` | @@ -15,7 +15,7 @@ LL | let a = bar(f, x); = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/project-fn-ret-invariant.rs:42:13 + --> $DIR/project-fn-ret-invariant.rs:43:13 | LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { | -- -- lifetime `'b` defined here @@ -30,7 +30,13 @@ LL | let b = bar(f, y); = note: the struct `Type<'a>` is invariant over the parameter `'a` = help: see for more information about variance -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/project-fn-ret-invariant.rs:38:4 + | +LL | fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { + | ^^^ + | + = help: `'a` and `'b` must be the same: replace one with the other -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/associated-types/cache/project-fn-ret-invariant.rs b/tests/ui/associated-types/cache/project-fn-ret-invariant.rs index 4ac642c0e04b5..b1883f45b16f5 100644 --- a/tests/ui/associated-types/cache/project-fn-ret-invariant.rs +++ b/tests/ui/associated-types/cache/project-fn-ret-invariant.rs @@ -36,6 +36,7 @@ fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { #[cfg(oneuse)] // one instantiation: BAD fn baz<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { + //[oneuse]~^ ERROR one or more lifetime errors let f = foo; // <-- No consistent type can be inferred for `f` here. let a = bar(f, x); //[oneuse]~^ ERROR lifetime may not live long enough @@ -54,6 +55,7 @@ fn baz<'a, 'b>(x: Type<'a>) -> Type<'static> { #[cfg(krisskross)] // two instantiations, mixing and matching: BAD fn transmute<'a, 'b>(x: Type<'a>, y: Type<'b>) -> (Type<'a>, Type<'b>) { + //[krisskross]~^ ERROR one or more lifetime errors let a = bar(foo, y); let b = bar(foo, x); (a, b) diff --git a/tests/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr b/tests/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr index b8100f6dfaec0..547bde2e5a0a9 100644 --- a/tests/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr +++ b/tests/ui/associated-types/cache/project-fn-ret-invariant.transmute.stderr @@ -1,5 +1,5 @@ error: lifetime may not live long enough - --> $DIR/project-fn-ret-invariant.rs:52:5 + --> $DIR/project-fn-ret-invariant.rs:53:5 | LL | fn baz<'a, 'b>(x: Type<'a>) -> Type<'static> { | -- lifetime `'a` defined here diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs index ce94ba657b86f..71fca8858c78b 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.rs @@ -3,7 +3,7 @@ trait Trait<'a> {} -fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { +fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { //~ ERROR one or more x as _ //~ error: lifetime may not live long enough //~| error: lifetime may not live long enough } @@ -26,14 +26,14 @@ trait Assocked { type Assoc: ?Sized; } -fn change_assoc_0<'a, 'b>( +fn change_assoc_0<'a, 'b>( //~ ERROR one or more lifetime errors x: *mut dyn Assocked, ) -> *mut dyn Assocked { x as _ //~ error: lifetime may not live long enough //~| error: lifetime may not live long enough } -fn change_assoc_1<'a, 'b>( +fn change_assoc_1<'a, 'b>( //~ ERROR one or more lifetime errors x: *mut dyn Assocked>, ) -> *mut dyn Assocked> { x as _ //~ error: lifetime may not live long enough diff --git a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr index f2c060e4b279b..74a92f459aa62 100644 --- a/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr +++ b/tests/ui/cast/ptr-to-trait-obj-different-regions-misc.stderr @@ -42,7 +42,13 @@ LL | x as _ = note: this was previously accepted by the compiler but was changed recently = help: see for more information -help: `'b` and `'a` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:6:4 + | +LL | fn change_lt<'a, 'b>(x: *mut dyn Trait<'a>) -> *mut dyn Trait<'b> { + | ^^^^^^^^^ + | + = help: `'b` and `'a` must be the same: replace one with the other error: lifetime may not live long enough --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:12:5 @@ -156,9 +162,13 @@ LL | x as _ = note: this was previously accepted by the compiler but was changed recently = help: see for more information -help: `'b` and `'a` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:29:4 | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +LL | fn change_assoc_0<'a, 'b>( + | ^^^^^^^^^^^^^^ + | + = help: `'b` and `'a` must be the same: replace one with the other error: lifetime may not live long enough --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:39:5 @@ -206,9 +216,13 @@ LL | x as _ = note: this was previously accepted by the compiler but was changed recently = help: see for more information -help: `'b` and `'a` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:36:4 + | +LL | fn change_assoc_1<'a, 'b>( + | ^^^^^^^^^^^^^^ | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = help: `'b` and `'a` must be the same: replace one with the other error[E0521]: borrowed data escapes outside of function --> $DIR/ptr-to-trait-obj-different-regions-misc.rs:46:5 @@ -233,7 +247,7 @@ LL | require_static(ptr as _) = note: this was previously accepted by the compiler but was changed recently = help: see for more information -error: aborting due to 11 previous errors +error: aborting due to 14 previous errors Some errors have detailed explanations: E0308, E0521. For more information about an error, try `rustc --explain E0308`. diff --git a/tests/ui/field_representing_types/invariant.next.stderr b/tests/ui/field_representing_types/invariant.next.stderr index 6a622a8e5dd11..c9e0b03040d85 100644 --- a/tests/ui/field_representing_types/invariant.next.stderr +++ b/tests/ui/field_representing_types/invariant.next.stderr @@ -1,10 +1,11 @@ error: lifetime may not live long enough - --> $DIR/invariant.rs:15:5 + --> $DIR/invariant.rs:16:5 | LL | fn assert_invariant<'a, 'b>(x: field_of!(Struct<'a>, field), y: field_of!(Struct<'b>, field)) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here +LL | LL | consume(x, y); | ^^^^^^^^^^^^^ argument requires that `'a` must outlive `'b` | @@ -14,12 +15,13 @@ LL | consume(x, y); = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/invariant.rs:15:5 + --> $DIR/invariant.rs:16:5 | LL | fn assert_invariant<'a, 'b>(x: field_of!(Struct<'a>, field), y: field_of!(Struct<'b>, field)) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here +LL | LL | consume(x, y); | ^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` | @@ -28,7 +30,13 @@ LL | consume(x, y); = note: the struct `FieldRepresentingType` is invariant over the parameter `T` = help: see for more information about variance -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/invariant.rs:14:4 + | +LL | fn assert_invariant<'a, 'b>(x: field_of!(Struct<'a>, field), y: field_of!(Struct<'b>, field)) { + | ^^^^^^^^^^^^^^^^ + | + = help: `'a` and `'b` must be the same: replace one with the other -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/field_representing_types/invariant.old.stderr b/tests/ui/field_representing_types/invariant.old.stderr index 6a622a8e5dd11..c9e0b03040d85 100644 --- a/tests/ui/field_representing_types/invariant.old.stderr +++ b/tests/ui/field_representing_types/invariant.old.stderr @@ -1,10 +1,11 @@ error: lifetime may not live long enough - --> $DIR/invariant.rs:15:5 + --> $DIR/invariant.rs:16:5 | LL | fn assert_invariant<'a, 'b>(x: field_of!(Struct<'a>, field), y: field_of!(Struct<'b>, field)) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here +LL | LL | consume(x, y); | ^^^^^^^^^^^^^ argument requires that `'a` must outlive `'b` | @@ -14,12 +15,13 @@ LL | consume(x, y); = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/invariant.rs:15:5 + --> $DIR/invariant.rs:16:5 | LL | fn assert_invariant<'a, 'b>(x: field_of!(Struct<'a>, field), y: field_of!(Struct<'b>, field)) { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here +LL | LL | consume(x, y); | ^^^^^^^^^^^^^ argument requires that `'b` must outlive `'a` | @@ -28,7 +30,13 @@ LL | consume(x, y); = note: the struct `FieldRepresentingType` is invariant over the parameter `T` = help: see for more information about variance -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/invariant.rs:14:4 + | +LL | fn assert_invariant<'a, 'b>(x: field_of!(Struct<'a>, field), y: field_of!(Struct<'b>, field)) { + | ^^^^^^^^^^^^^^^^ + | + = help: `'a` and `'b` must be the same: replace one with the other -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/field_representing_types/invariant.rs b/tests/ui/field_representing_types/invariant.rs index 16a45e63c79c5..b9b42b2f29105 100644 --- a/tests/ui/field_representing_types/invariant.rs +++ b/tests/ui/field_representing_types/invariant.rs @@ -12,6 +12,7 @@ pub struct Struct<'a> { fn consume<'a>(_: field_of!(Struct<'a>, field), _: field_of!(Struct<'a>, field)) {} fn assert_invariant<'a, 'b>(x: field_of!(Struct<'a>, field), y: field_of!(Struct<'b>, field)) { + //~^ ERROR one or more lifetime errors consume(x, y); //~^ ERROR: lifetime may not live long enough //~^^ ERROR: lifetime may not live long enough diff --git a/tests/ui/fn/fn_def_coercion.rs b/tests/ui/fn/fn_def_coercion.rs index 31c8fa41de17c..40a3d6aa9722b 100644 --- a/tests/ui/fn/fn_def_coercion.rs +++ b/tests/ui/fn/fn_def_coercion.rs @@ -6,7 +6,7 @@ fn foo(t: T) -> T { t } -fn f<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { +fn f<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { //~ ERROR one or more let mut x = foo::<&'a ()>; //~ ERROR: lifetime may not live long enough x = foo::<&'b ()>; //~ ERROR: lifetime may not live long enough x = foo::<&'c ()>; @@ -25,7 +25,7 @@ fn h<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { let _: &'a () = x(c); } -fn i<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { +fn i<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { //~ ERROR one or more let mut x = foo::<&'c ()>; x = foo::<&'b ()>; //~ ERROR lifetime may not live long enough x = foo::<&'a ()>; //~ ERROR lifetime may not live long enough @@ -34,7 +34,7 @@ fn i<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { x(c); } -fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { +fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { //~ ERROR one or more let x = match true { true => foo::<&'b ()>, //~ ERROR lifetime may not live long enough false => foo::<&'a ()>, //~ ERROR lifetime may not live long enough diff --git a/tests/ui/fn/fn_def_coercion.stderr b/tests/ui/fn/fn_def_coercion.stderr index 2db2721b7dec1..3428a118b51a2 100644 --- a/tests/ui/fn/fn_def_coercion.stderr +++ b/tests/ui/fn/fn_def_coercion.stderr @@ -29,7 +29,13 @@ LL | x = foo::<&'b ()>; = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/fn_def_coercion.rs:9:4 + | +LL | fn f<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { + | ^ + | + = help: `'a` and `'b` must be the same: replace one with the other error: lifetime may not live long enough --> $DIR/fn_def_coercion.rs:20:12 @@ -76,9 +82,13 @@ LL | x = foo::<&'a ()>; = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/fn_def_coercion.rs:28:4 | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +LL | fn i<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { + | ^ + | + = help: `'a` and `'b` must be the same: replace one with the other error: lifetime may not live long enough --> $DIR/fn_def_coercion.rs:39:17 @@ -112,9 +122,13 @@ LL | false => foo::<&'a ()>, = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/fn_def_coercion.rs:37:4 + | +LL | fn j<'a, 'b, 'c: 'a + 'b>(a: &'a (), b: &'b (), c: &'c ()) { + | ^ | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = help: `'a` and `'b` must be the same: replace one with the other error: lifetime may not live long enough --> $DIR/fn_def_coercion.rs:49:17 @@ -148,5 +162,5 @@ LL | false => foo::<&'a ()>, = note: the function `foo` is invariant over the parameter `T` = help: see for more information about variance -error: aborting due to 9 previous errors +error: aborting due to 12 previous errors diff --git a/tests/ui/impl-trait/precise-capturing/rpitit.rs b/tests/ui/impl-trait/precise-capturing/rpitit.rs index 91c52817d8573..7d17a3a936ae5 100644 --- a/tests/ui/impl-trait/precise-capturing/rpitit.rs +++ b/tests/ui/impl-trait/precise-capturing/rpitit.rs @@ -9,7 +9,7 @@ trait TraitLt<'a: 'a> { fn hello() -> impl Sized + use; //~^ ERROR `impl Trait` captures lifetime parameter, but it is not mentioned in `use<...>` precise captures list } -fn trait_lt<'a, 'b, T: for<'r> TraitLt<'r>> () { +fn trait_lt<'a, 'b, T: for<'r> TraitLt<'r>> () { //~ ERROR one or more lifetime errors eq_types( //~^ ERROR lifetime may not live long enough //~| ERROR lifetime may not live long enough diff --git a/tests/ui/impl-trait/precise-capturing/rpitit.stderr b/tests/ui/impl-trait/precise-capturing/rpitit.stderr index ff461e81079b8..f294a2d34e355 100644 --- a/tests/ui/impl-trait/precise-capturing/rpitit.stderr +++ b/tests/ui/impl-trait/precise-capturing/rpitit.stderr @@ -40,7 +40,13 @@ LL | | ); | = help: consider adding the following bound: `'b: 'a` -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/rpitit.rs:12:4 + | +LL | fn trait_lt<'a, 'b, T: for<'r> TraitLt<'r>> () { + | ^^^^^^^^ + | + = help: `'a` and `'b` must be the same: replace one with the other -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors diff --git a/tests/ui/implied-bounds/normalization-preserve-equality.borrowck_current.stderr b/tests/ui/implied-bounds/normalization-preserve-equality.borrowck_current.stderr index 008de7a7afc63..1401cd89f28d8 100644 --- a/tests/ui/implied-bounds/normalization-preserve-equality.borrowck_current.stderr +++ b/tests/ui/implied-bounds/normalization-preserve-equality.borrowck_current.stderr @@ -22,7 +22,13 @@ LL | fn test_borrowck<'a, 'b>(_: ( as Trait>::Ty, Equal<'a, 'b>)) | = help: consider adding the following bound: `'b: 'a` -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/normalization-preserve-equality.rs:34:4 + | +LL | fn test_borrowck<'a, 'b>(_: ( as Trait>::Ty, Equal<'a, 'b>)) { + | ^^^^^^^^^^^^^ + | + = help: `'a` and `'b` must be the same: replace one with the other -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/nll/outlives-suggestion-more.rs b/tests/ui/nll/outlives-suggestion-more.rs index 2e1359fe5d496..2f49ad6b85d35 100644 --- a/tests/ui/nll/outlives-suggestion-more.rs +++ b/tests/ui/nll/outlives-suggestion-more.rs @@ -7,13 +7,13 @@ fn foo1<'a, 'b, 'c, 'd>(x: &'a usize, y: &'b usize) -> (&'c usize, &'d usize) { } // Should suggest: 'a: 'c and use 'static instead of 'b -fn foo2<'a, 'b, 'c>(x: &'a usize, y: &'b usize) -> (&'c usize, &'static usize) { +fn foo2<'a, 'b, 'c>(x: &'a usize, y: &'b usize) -> (&'c usize, &'static usize) { //~ ERROR one or (x, y) //~ERROR lifetime may not live long enough //~^ERROR lifetime may not live long enough } // Should suggest: 'a and 'b are the same and use 'static instead of 'c -fn foo3<'a, 'b, 'c, 'd, 'e>( +fn foo3<'a, 'b, 'c, 'd, 'e>( //~ ERROR one or more lifetime errors x: &'a usize, y: &'b usize, z: &'c usize, diff --git a/tests/ui/nll/outlives-suggestion-more.stderr b/tests/ui/nll/outlives-suggestion-more.stderr index e8fbc2ff7b437..0b79395113bf1 100644 --- a/tests/ui/nll/outlives-suggestion-more.stderr +++ b/tests/ui/nll/outlives-suggestion-more.stderr @@ -42,7 +42,13 @@ LL | fn foo2<'a, 'b, 'c>(x: &'a usize, y: &'b usize) -> (&'c usize, &'static usi LL | (x, y) | ^^^^^^ returning this value requires that `'b` must outlive `'static` -help: replace `'b` with `'static` +error: one or more lifetime errors were found in this item + --> $DIR/outlives-suggestion-more.rs:10:4 + | +LL | fn foo2<'a, 'b, 'c>(x: &'a usize, y: &'b usize) -> (&'c usize, &'static usize) { + | ^^^^ + | + = help: replace `'b` with `'static` error: lifetime may not live long enough --> $DIR/outlives-suggestion-more.rs:21:5 @@ -79,10 +85,14 @@ LL | fn foo3<'a, 'b, 'c, 'd, 'e>( LL | (x, y, z) | ^^^^^^^^^ returning this value requires that `'c` must outlive `'static` -help: the following changes may resolve your lifetime errors +error: one or more lifetime errors were found in this item + --> $DIR/outlives-suggestion-more.rs:16:4 + | +LL | fn foo3<'a, 'b, 'c, 'd, 'e>( + | ^^^^ | = help: `'a` and `'b` must be the same: replace one with the other = help: replace `'c` with `'static` -error: aborting due to 7 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/nll/outlives-suggestion-simple.rs b/tests/ui/nll/outlives-suggestion-simple.rs index 2a5c31e3a6468..81d8d3dba9933 100644 --- a/tests/ui/nll/outlives-suggestion-simple.rs +++ b/tests/ui/nll/outlives-suggestion-simple.rs @@ -8,7 +8,7 @@ fn foo2<'a>(x: &'a usize) -> &'static usize { x //~ERROR lifetime may not live long enough } -fn foo3<'a, 'b>(x: &'a usize, y: &'b usize) -> (&'b usize, &'a usize) { +fn foo3<'a, 'b>(x: &'a usize, y: &'b usize) -> (&'b usize, &'a usize) { //~ ERROR one or more (x, y) //~ERROR lifetime may not live long enough //~^ERROR lifetime may not live long enough } diff --git a/tests/ui/nll/outlives-suggestion-simple.stderr b/tests/ui/nll/outlives-suggestion-simple.stderr index 669532005b292..36e4d8b43c6d6 100644 --- a/tests/ui/nll/outlives-suggestion-simple.stderr +++ b/tests/ui/nll/outlives-suggestion-simple.stderr @@ -42,7 +42,13 @@ LL | (x, y) | = help: consider adding the following bound: `'b: 'a` -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/outlives-suggestion-simple.rs:11:4 + | +LL | fn foo3<'a, 'b>(x: &'a usize, y: &'b usize) -> (&'b usize, &'a usize) { + | ^^^^ + | + = help: `'a` and `'b` must be the same: replace one with the other error: lifetime may not live long enough --> $DIR/outlives-suggestion-simple.rs:20:5 @@ -104,5 +110,5 @@ LL | Bar2::new(&self) = note: the struct `Foo2<'a>` is invariant over the parameter `'a` = help: see for more information about variance -error: aborting due to 9 previous errors +error: aborting due to 10 previous errors diff --git a/tests/ui/nll/polonius/flow-sensitive-invariance.nll.stderr b/tests/ui/nll/polonius/flow-sensitive-invariance.nll.stderr index 5756148f4eb2a..6283042e16f33 100644 --- a/tests/ui/nll/polonius/flow-sensitive-invariance.nll.stderr +++ b/tests/ui/nll/polonius/flow-sensitive-invariance.nll.stderr @@ -1,11 +1,11 @@ error: lifetime may not live long enough - --> $DIR/flow-sensitive-invariance.rs:20:17 + --> $DIR/flow-sensitive-invariance.rs:22:17 | LL | fn use_it<'a, 'b>(choice: bool) -> Result, Invariant<'b>> { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here -LL | let returned_value = create_invariant(); +... LL | if choice { Ok(returned_value) } else { Err(returned_value) } | ^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` | @@ -15,13 +15,13 @@ LL | if choice { Ok(returned_value) } else { Err(returned_value) } = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/flow-sensitive-invariance.rs:20:45 + --> $DIR/flow-sensitive-invariance.rs:22:45 | LL | fn use_it<'a, 'b>(choice: bool) -> Result, Invariant<'b>> { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here -LL | let returned_value = create_invariant(); +... LL | if choice { Ok(returned_value) } else { Err(returned_value) } | ^^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` | @@ -30,7 +30,13 @@ LL | if choice { Ok(returned_value) } else { Err(returned_value) } = note: the struct `Invariant<'l>` is invariant over the parameter `'l` = help: see for more information about variance -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/flow-sensitive-invariance.rs:18:4 + | +LL | fn use_it<'a, 'b>(choice: bool) -> Result, Invariant<'b>> { + | ^^^^^^ + | + = help: `'a` and `'b` must be the same: replace one with the other -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/nll/polonius/flow-sensitive-invariance.polonius.stderr b/tests/ui/nll/polonius/flow-sensitive-invariance.polonius.stderr index 5756148f4eb2a..6283042e16f33 100644 --- a/tests/ui/nll/polonius/flow-sensitive-invariance.polonius.stderr +++ b/tests/ui/nll/polonius/flow-sensitive-invariance.polonius.stderr @@ -1,11 +1,11 @@ error: lifetime may not live long enough - --> $DIR/flow-sensitive-invariance.rs:20:17 + --> $DIR/flow-sensitive-invariance.rs:22:17 | LL | fn use_it<'a, 'b>(choice: bool) -> Result, Invariant<'b>> { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here -LL | let returned_value = create_invariant(); +... LL | if choice { Ok(returned_value) } else { Err(returned_value) } | ^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b` | @@ -15,13 +15,13 @@ LL | if choice { Ok(returned_value) } else { Err(returned_value) } = help: see for more information about variance error: lifetime may not live long enough - --> $DIR/flow-sensitive-invariance.rs:20:45 + --> $DIR/flow-sensitive-invariance.rs:22:45 | LL | fn use_it<'a, 'b>(choice: bool) -> Result, Invariant<'b>> { | -- -- lifetime `'b` defined here | | | lifetime `'a` defined here -LL | let returned_value = create_invariant(); +... LL | if choice { Ok(returned_value) } else { Err(returned_value) } | ^^^^^^^^^^^^^^^^^^^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a` | @@ -30,7 +30,13 @@ LL | if choice { Ok(returned_value) } else { Err(returned_value) } = note: the struct `Invariant<'l>` is invariant over the parameter `'l` = help: see for more information about variance -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/flow-sensitive-invariance.rs:18:4 + | +LL | fn use_it<'a, 'b>(choice: bool) -> Result, Invariant<'b>> { + | ^^^^^^ + | + = help: `'a` and `'b` must be the same: replace one with the other -error: aborting due to 2 previous errors +error: aborting due to 3 previous errors diff --git a/tests/ui/nll/polonius/flow-sensitive-invariance.rs b/tests/ui/nll/polonius/flow-sensitive-invariance.rs index c5571f131da3b..cb7cb51189d18 100644 --- a/tests/ui/nll/polonius/flow-sensitive-invariance.rs +++ b/tests/ui/nll/polonius/flow-sensitive-invariance.rs @@ -16,6 +16,8 @@ fn create_invariant<'l>() -> Invariant<'l> { } fn use_it<'a, 'b>(choice: bool) -> Result, Invariant<'b>> { + //[nll]~^ ERROR one or more lifetime errors + //[polonius]~^^ ERROR one or more lifetime errors let returned_value = create_invariant(); if choice { Ok(returned_value) } else { Err(returned_value) } //[nll]~^ ERROR lifetime may not live long enough diff --git a/tests/ui/nll/type-check-pointer-coercions.rs b/tests/ui/nll/type-check-pointer-coercions.rs index 66da57248f98f..6eceebeda39ac 100644 --- a/tests/ui/nll/type-check-pointer-coercions.rs +++ b/tests/ui/nll/type-check-pointer-coercions.rs @@ -6,7 +6,7 @@ fn unique_to_const<'a, 'b>(x: &mut &'a i32) -> *const &'b i32 { x //~ ERROR } -fn unique_to_mut<'a, 'b>(x: &mut &'a i32) -> *mut &'b i32 { +fn unique_to_mut<'a, 'b>(x: &mut &'a i32) -> *mut &'b i32 { //~ ERROR one or more lifetime errors // Two errors because *mut is invariant x //~ ERROR //~| ERROR diff --git a/tests/ui/nll/type-check-pointer-coercions.stderr b/tests/ui/nll/type-check-pointer-coercions.stderr index ef2d928786fca..042bbb456f2ef 100644 --- a/tests/ui/nll/type-check-pointer-coercions.stderr +++ b/tests/ui/nll/type-check-pointer-coercions.stderr @@ -54,7 +54,13 @@ LL | x = note: mutable pointers are invariant over their type parameter = help: see for more information about variance -help: `'b` and `'a` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/type-check-pointer-coercions.rs:9:4 + | +LL | fn unique_to_mut<'a, 'b>(x: &mut &'a i32) -> *mut &'b i32 { + | ^^^^^^^^^^^^^ + | + = help: `'b` and `'a` must be the same: replace one with the other error: lifetime may not live long enough --> $DIR/type-check-pointer-coercions.rs:16:5 @@ -107,5 +113,5 @@ LL | y | = help: consider adding the following bound: `'a: 'b` -error: aborting due to 8 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/nll/type-check-pointer-comparisons.rs b/tests/ui/nll/type-check-pointer-comparisons.rs index 7b0ffeaef0e21..da417688baa80 100644 --- a/tests/ui/nll/type-check-pointer-comparisons.rs +++ b/tests/ui/nll/type-check-pointer-comparisons.rs @@ -1,18 +1,18 @@ // Check that we assert that pointers have a common subtype for comparisons -fn compare_const<'a, 'b>(x: *const &mut &'a i32, y: *const &mut &'b i32) { +fn compare_const<'a, 'b>(x: *const &mut &'a i32, y: *const &mut &'b i32) { //~ ERROR one or more x == y; //~^ ERROR lifetime may not live long enough //~| ERROR lifetime may not live long enough } -fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) { +fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) { //~ ERROR one or more x == y; //~^ ERROR lifetime may not live long enough //~| ERROR lifetime may not live long enough } -fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32)) { +fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32)) { //~ ERROR one or f == g; //~^ ERROR lifetime may not live long enough //~| ERROR lifetime may not live long enough diff --git a/tests/ui/nll/type-check-pointer-comparisons.stderr b/tests/ui/nll/type-check-pointer-comparisons.stderr index e362dfb3c6e7a..50ed9bc3cd3fa 100644 --- a/tests/ui/nll/type-check-pointer-comparisons.stderr +++ b/tests/ui/nll/type-check-pointer-comparisons.stderr @@ -28,7 +28,13 @@ LL | x == y; = note: mutable references are invariant over their type parameter = help: see for more information about variance -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/type-check-pointer-comparisons.rs:3:4 + | +LL | fn compare_const<'a, 'b>(x: *const &mut &'a i32, y: *const &mut &'b i32) { + | ^^^^^^^^^^^^^ + | + = help: `'a` and `'b` must be the same: replace one with the other error: lifetime may not live long enough --> $DIR/type-check-pointer-comparisons.rs:10:5 @@ -60,9 +66,13 @@ LL | x == y; = note: mutable pointers are invariant over their type parameter = help: see for more information about variance -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/type-check-pointer-comparisons.rs:9:4 | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +LL | fn compare_mut<'a, 'b>(x: *mut &'a i32, y: *mut &'b i32) { + | ^^^^^^^^^^^ + | + = help: `'a` and `'b` must be the same: replace one with the other error: lifetime may not live long enough --> $DIR/type-check-pointer-comparisons.rs:16:5 @@ -94,9 +104,13 @@ LL | f == g; = note: mutable references are invariant over their type parameter = help: see for more information about variance -help: `'a` and `'b` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/type-check-pointer-comparisons.rs:15:4 + | +LL | fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32)) { + | ^^^^^^^^^^^^^^ | - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = help: `'a` and `'b` must be the same: replace one with the other -error: aborting due to 6 previous errors +error: aborting due to 9 previous errors diff --git a/tests/ui/nll/user-annotations/normalization-2.rs b/tests/ui/nll/user-annotations/normalization-2.rs index dddba2265c613..63ac7a7a2c02c 100644 --- a/tests/ui/nll/user-annotations/normalization-2.rs +++ b/tests/ui/nll/user-annotations/normalization-2.rs @@ -44,14 +44,14 @@ fn test_local<'a>() { //~^ ERROR lifetime may not live long enough } -fn test_closure_sig<'a, 'b>() { +fn test_closure_sig<'a, 'b>() { //~ ERROR one or more lifetime errors were found |_: Ty<'a>| {}; //~^ ERROR lifetime may not live long enough || -> Option> { None }; //~^ ERROR lifetime may not live long enough } -fn test_path<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h>() { +fn test_path<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h>() { //~ ERROR one or more lifetime errors were found >::method::>; //~^ ERROR lifetime may not live long enough >::method::>; @@ -78,14 +78,14 @@ fn test_path<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h>() { //~^ ERROR lifetime may not live long enough } -fn test_call<'a, 'b, 'c>() { +fn test_call<'a, 'b, 'c>() { //~ ERROR one or more lifetime errors were found >::method::>(); //~^ ERROR lifetime may not live long enough >::method::>(); //~^ ERROR lifetime may not live long enough } -fn test_variants<'a, 'b, 'c>() { +fn test_variants<'a, 'b, 'c>() { //~ ERROR one or more lifetime errors were found >::Struct {}; //~^ ERROR lifetime may not live long enough >::Tuple(); @@ -94,14 +94,14 @@ fn test_variants<'a, 'b, 'c>() { //~^ ERROR lifetime may not live long enough } -fn test_method_call<'a, 'b>(x: MyTy<()>) { +fn test_method_call<'a, 'b>(x: MyTy<()>) { //~ ERROR one or more lifetime errors were found x.method2::>(); //~^ ERROR lifetime may not live long enough x.trait_method::>(); //~^ ERROR lifetime may not live long enough } -fn test_struct_path<'a, 'b, 'c, 'd>() { +fn test_struct_path<'a, 'b, 'c, 'd>() { //~ ERROR one or more lifetime errors were found struct Struct { x: Option, } trait Project { @@ -126,7 +126,7 @@ fn test_struct_path<'a, 'b, 'c, 'd>() { //~^ ERROR lifetime may not live long enough } -fn test_pattern<'a, 'b, 'c, 'd, 'e, 'f>() { +fn test_pattern<'a, 'b, 'c, 'd, 'e, 'f>() { //~ ERROR one or more lifetime errors were found use MyTy::*; match MyTy::Unit { Struct::> {..} => {}, diff --git a/tests/ui/nll/user-annotations/normalization-2.stderr b/tests/ui/nll/user-annotations/normalization-2.stderr index dcf049a7a61a0..d8e679cc29137 100644 --- a/tests/ui/nll/user-annotations/normalization-2.stderr +++ b/tests/ui/nll/user-annotations/normalization-2.stderr @@ -23,7 +23,11 @@ LL | fn test_closure_sig<'a, 'b>() { LL | || -> Option> { None }; | ^^^^^^^^^^^^^^ requires that `'b` must outlive `'static` -help: the following changes may resolve your lifetime errors +error: one or more lifetime errors were found in this item + --> $DIR/normalization-2.rs:47:4 + | +LL | fn test_closure_sig<'a, 'b>() { + | ^^^^^^^^^^^^^^^^ | = help: replace `'a` with `'static` = help: replace `'b` with `'static` @@ -99,7 +103,11 @@ LL | fn test_path<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h>() { LL | MyTy::>::Unit; | ^^^^^^^^^^^^^^^^^^^^ requires that `'h` must outlive `'static` -help: the following changes may resolve your lifetime errors +error: one or more lifetime errors were found in this item + --> $DIR/normalization-2.rs:54:4 + | +LL | fn test_path<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h>() { + | ^^^^^^^^^ | = help: replace `'a` with `'static` = help: replace `'b` with `'static` @@ -127,11 +135,14 @@ LL | fn test_call<'a, 'b, 'c>() { LL | >::method::>(); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'b` must outlive `'static` -help: the following changes may resolve your lifetime errors +error: one or more lifetime errors were found in this item + --> $DIR/normalization-2.rs:81:4 + | +LL | fn test_call<'a, 'b, 'c>() { + | ^^^^^^^^^ | = help: replace `'a` with `'static` = help: replace `'b` with `'static` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: lifetime may not live long enough --> $DIR/normalization-2.rs:89:5 @@ -159,7 +170,11 @@ LL | fn test_variants<'a, 'b, 'c>() { LL | >::Unit; | ^^^^^^^^^^^^^^ requires that `'c` must outlive `'static` -help: the following changes may resolve your lifetime errors +error: one or more lifetime errors were found in this item + --> $DIR/normalization-2.rs:88:4 + | +LL | fn test_variants<'a, 'b, 'c>() { + | ^^^^^^^^^^^^^ | = help: replace `'a` with `'static` = help: replace `'b` with `'static` @@ -182,11 +197,14 @@ LL | fn test_method_call<'a, 'b>(x: MyTy<()>) { LL | x.trait_method::>(); | ^^^^^^^^^^^^ requires that `'b` must outlive `'static` -help: the following changes may resolve your lifetime errors +error: one or more lifetime errors were found in this item + --> $DIR/normalization-2.rs:97:4 + | +LL | fn test_method_call<'a, 'b>(x: MyTy<()>) { + | ^^^^^^^^^^^^^^^^ | = help: replace `'a` with `'static` = help: replace `'b` with `'static` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error: lifetime may not live long enough --> $DIR/normalization-2.rs:117:5 @@ -224,7 +242,11 @@ LL | fn test_struct_path<'a, 'b, 'c, 'd>() { LL | as Project>::Struct { x: None, }; // with SelfTy | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'d` must outlive `'static` -help: the following changes may resolve your lifetime errors +error: one or more lifetime errors were found in this item + --> $DIR/normalization-2.rs:104:4 + | +LL | fn test_struct_path<'a, 'b, 'c, 'd>() { + | ^^^^^^^^^^^^^^^^ | = help: replace `'a` with `'static` = help: replace `'b` with `'static` @@ -285,7 +307,11 @@ LL | fn test_pattern<'a, 'b, 'c, 'd, 'e, 'f>() { LL | >::Unit => {}, | ^^^^^^^^^^^^^^ requires that `'f` must outlive `'static` -help: the following changes may resolve your lifetime errors +error: one or more lifetime errors were found in this item + --> $DIR/normalization-2.rs:129:4 + | +LL | fn test_pattern<'a, 'b, 'c, 'd, 'e, 'f>() { + | ^^^^^^^^^^^^ | = help: replace `'a` with `'static` = help: replace `'b` with `'static` @@ -294,5 +320,5 @@ help: the following changes may resolve your lifetime errors = help: replace `'e` with `'static` = help: replace `'f` with `'static` -error: aborting due to 28 previous errors +error: aborting due to 35 previous errors diff --git a/tests/ui/regions/regions-infer-not-param.rs b/tests/ui/regions/regions-infer-not-param.rs index c3766bce18a2c..381163e565040 100644 --- a/tests/ui/regions/regions-infer-not-param.rs +++ b/tests/ui/regions/regions-infer-not-param.rs @@ -17,7 +17,7 @@ fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p } fn take_indirect1(p: Indirect1) -> Indirect1 { p } -fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p } +fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p } //~ ERROR one or more lifetime //~^ ERROR lifetime may not live long enough //~| ERROR lifetime may not live long enough diff --git a/tests/ui/regions/regions-infer-not-param.stderr b/tests/ui/regions/regions-infer-not-param.stderr index d12f07a772880..74b2638c1c681 100644 --- a/tests/ui/regions/regions-infer-not-param.stderr +++ b/tests/ui/regions/regions-infer-not-param.stderr @@ -34,7 +34,13 @@ LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p } = note: the struct `Indirect2<'a>` is invariant over the parameter `'a` = help: see for more information about variance -help: `'b` and `'a` must be the same: replace one with the other +error: one or more lifetime errors were found in this item + --> $DIR/regions-infer-not-param.rs:20:4 + | +LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p } + | ^^^^^^^^^^^^^^ + | + = help: `'b` and `'a` must be the same: replace one with the other -error: aborting due to 3 previous errors +error: aborting due to 4 previous errors From 31fafe3aedff09387c0d6791b586e76ff66ef912 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Thu, 17 Sep 2026 14:40:55 +1000 Subject: [PATCH 18/25] Clarify `Level::{Help,Note,FailureNote}` details And remove `struct_help`, to discourage use of `Level::Help`. (`Sublevel::Help` is fine.) --- compiler/rustc_errors/src/lib.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 9ca0344058d7f..e1fd8344e73fd 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -1148,11 +1148,6 @@ impl<'a> DiagCtxtHandle<'a> { self.create_note(note).emit() } - #[track_caller] - pub fn struct_help(self, msg: impl Into) -> Diag<'a, ()> { - Diag::new(self, Help, msg) - } - #[track_caller] pub fn struct_failure_note(self, msg: impl Into) -> Diag<'a, ()> { Diag::new(self, FailureNote, msg) @@ -1572,7 +1567,7 @@ impl DelayedDiagInner { /// | ForceWarning | - | () | yes | lint-only /// | Warning | - | () | yes | yes /// | Note | - | () | rare | - -/// | Help | - | () | rare | - +/// | Help | - | () | don't use | - /// | FailureNote | - | () | rare | - /// | Allow | - | () | yes | lint-only /// | Expect | - | () | yes | lint-only @@ -1607,14 +1602,18 @@ pub enum Level { /// Will be skipped if `can_emit_warnings` is false. Warning, - /// A message giving additional context. + /// A rarely-used level for output that isn't an error or a warning. Note, /// A message suggesting how to fix something. + /// + /// FIXME(nnethercote) Do not use this! Currently only exists to support `proc_macro::Help`, + /// part of the unstable `proc_macro_diagnostic` feature (see #54140). Should be removed + /// because help messages are fine as subdiagnostics but are silly as top-level diagnostics. Help, - /// Similar to `Note`, but used in cases where compilation has failed. When printed for human - /// consumption, it doesn't have any kind of `note:` label. + /// Similar to `Note`, but even rarer. Lacks the a trailing blank line that all other + /// diagnostics have. Also, when printed for human consumption it doesn't have a `note:` label. FailureNote, /// Only used for lints. @@ -1666,13 +1665,13 @@ pub enum Sublevel { /// See `Level::Warning`. Warning, - /// See `Level::Note`. + /// A message giving additional context. Note, /// A note that is only emitted once. OnceNote, - /// See `Level::Help`. + /// A message suggesting how to fix something. Help, /// A help that is only emitted once. From 6d008f38f8529cc7bc5c9dc0787a90544f43c9a8 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 17 Sep 2026 01:54:53 -0400 Subject: [PATCH 19/25] libm: Avoid `debug_assert_eq!` if `feature = "compiler-builtins"` If `fmaf16` is exported, the existing version hits errors like: error: `compiler_builtins` cannot call functions through upstream monomorphizations; encountered invalid call from `core::fmt::num::::fmt` to `core::fmt::num::imp::::fmt` --> library/core/src/fmt/num.rs:85:17 | 79 | / macro_rules! impl_Debug { 80 | | ($($T:ident)*) => { 81 | | $( 82 | | #[stable(feature = "rust1", since = "1.0.0")] ... | 85 | | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... | 96 | | }; 97 | | } | |_- in this expansion of `impl_Debug!` ... 595 | / impl_Debug! { 596 | | i8 i16 i32 i64 i128 isize 597 | | u8 u16 u32 u64 u128 usize 598 | | } | |_- in this macro invocation Presumably this comes from the call to `panicking::assert_failed!` that needs to be monomorphized. Avoid any formatting by using a plain `debug_assert!` if we are building the crate as compiler-builtins. --- .../compiler-builtins/libm/src/math/fmaf16.rs | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/library/compiler-builtins/libm/src/math/fmaf16.rs b/library/compiler-builtins/libm/src/math/fmaf16.rs index 8d1c5bccf8527..6d00832065e18 100644 --- a/library/compiler-builtins/libm/src/math/fmaf16.rs +++ b/library/compiler-builtins/libm/src/math/fmaf16.rs @@ -88,11 +88,22 @@ pub fn fmaf16(x: f16, y: f16, z: f16) -> f16 { // create a U21.43 fixed-point value. At the maximum exponent, there are five zeros before // the explicit leading 1 (intentional so this truncates to the final repr). if let Some(mshift) = mexp.checked_sub(5) { - debug_assert_eq!( - unbounded_shr_u64(m64, 64 - mshift), - 0, - "data shifted out {m} {mshift}" - ); + cfg_select_nofmt! { + feature = "compiler-builtins" => { + // Avoid formatting calls to `core` when building as compiler-builtins + debug_assert!( + unbounded_shr_u64(m64, 64 - mshift) == 0, + "data shifted out" + ); + } + _ => { + debug_assert_eq!( + unbounded_shr_u64(m64, 64 - mshift), + 0, + "data shifted out {m} {mshift}" + ); + } + } m64 <<= mshift; } else { // The lower few bits here would be on the order of 2^-43, which is too small to show up From 362d116adfac5d69e9d315ce45f91e2560e54270 Mon Sep 17 00:00:00 2001 From: Trevor Gross Date: Thu, 10 Sep 2026 14:16:54 -0400 Subject: [PATCH 20/25] c-b: Export `fmaf16` now that we have an implementation LLVM doesn't currently emit this but probably should, and this is valuable as a fallback in any case. --- library/compiler-builtins/compiler-builtins/src/math/mod.rs | 1 + library/core/src/num/imp/libm.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/library/compiler-builtins/compiler-builtins/src/math/mod.rs b/library/compiler-builtins/compiler-builtins/src/math/mod.rs index 3dfa3863bb770..03a564ca0e543 100644 --- a/library/compiler-builtins/compiler-builtins/src/math/mod.rs +++ b/library/compiler-builtins/compiler-builtins/src/math/mod.rs @@ -27,6 +27,7 @@ pub mod full_availability { fn fabsf16(x: f16) -> f16; fn fdimf16(x: f16, y: f16) -> f16; fn floorf16(x: f16) -> f16; + fn fmaf16(x: f16, y: f16, z: f16) -> f16; fn fmaxf16(x: f16, y: f16) -> f16; fn fmaximum_numf16(x: f16, y: f16) -> f16; fn fmaximumf16(x: f16, y: f16) -> f16; diff --git a/library/core/src/num/imp/libm.rs b/library/core/src/num/imp/libm.rs index 388f1479b2461..a8d6bdc0b5d7c 100644 --- a/library/core/src/num/imp/libm.rs +++ b/library/core/src/num/imp/libm.rs @@ -33,6 +33,7 @@ unsafe extern "C" { pub(crate) safe fn fma(x: f64, y: f64, z: f64) -> f64; pub(crate) safe fn fmaf(x: f32, y: f32, z: f32) -> f32; pub(crate) safe fn fmaf128(x: f128, y: f128, z: f128) -> f128; + pub(crate) safe fn fmaf16(x: f16, y: f16, z: f16) -> f16; pub(crate) safe fn fmax(x: f64, y: f64) -> f64; pub(crate) safe fn fmaxf(x: f32, y: f32) -> f32; pub(crate) safe fn fmaxf128(x: f128, y: f128) -> f128; From 2be177e4a2b01471d86b31bc9d0c35fc65f42e79 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 17 Sep 2026 11:33:58 +0200 Subject: [PATCH 21/25] Use HashMap::entry --- .../rustc_data_structures/src/flock/unix.rs | 48 ++++++++++--------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_data_structures/src/flock/unix.rs b/compiler/rustc_data_structures/src/flock/unix.rs index bceeb84ff52fa..35f865b68a7ce 100644 --- a/compiler/rustc_data_structures/src/flock/unix.rs +++ b/compiler/rustc_data_structures/src/flock/unix.rs @@ -1,3 +1,4 @@ +use std::collections::hash_map::Entry; use std::fs::File; use std::os::unix::prelude::*; use std::path::{Path, PathBuf}; @@ -31,35 +32,38 @@ impl Lock { pub fn try_lock(p: &Path, file: File, exclusive: bool) -> io::Result { let mut locks = LOCK_REGISTRY.lock().unwrap(); - let file = if let Some(state) = locks.get_mut(p) { - // We must not open the file again if there is an existing lock to prevent the close - // from unlocking the file even when another `Lock` already had the lock held before - // this `Lock::try_lock` call. - match state { - LockState::Exclusive { extra_files } => { - // Retain file to prevent unlock on close - extra_files.push(file); + let file = match locks.entry(p.to_owned()) { + Entry::Occupied(mut state) => { + // We must not open the file again if there is an existing lock to prevent the close + // from unlocking the file even when another `Lock` already had the lock held before + // this `Lock::try_lock` call. + match state.get_mut() { + LockState::Exclusive { extra_files } => { + // Retain file to prevent unlock on close + extra_files.push(file); - return Err(io::ErrorKind::WouldBlock.into()); - } - LockState::Shared(file) => { - if exclusive { return Err(io::ErrorKind::WouldBlock.into()); - } else { - Arc::clone(file) + } + LockState::Shared(file) => { + if exclusive { + return Err(io::ErrorKind::WouldBlock.into()); + } else { + Arc::clone(file) + } } } } - } else { - let file = Arc::new(UnlockGuard::try_lock(file, exclusive)?); + Entry::Vacant(vacant) => { + let file = Arc::new(UnlockGuard::try_lock(file, exclusive)?); - if exclusive { - locks.insert(p.to_owned(), LockState::Exclusive { extra_files: vec![] }); - } else { - locks.insert(p.to_owned(), LockState::Shared(Arc::clone(&file))); - } + if exclusive { + vacant.insert(LockState::Exclusive { extra_files: vec![] }); + } else { + vacant.insert(LockState::Shared(Arc::clone(&file))); + } - file + file + } }; Ok(Lock { path: p.to_owned(), file: Some(file) }) From 3c8dcac341ba84870f8fd97d1816a406414ff917 Mon Sep 17 00:00:00 2001 From: Yukang Date: Thu, 10 Sep 2026 19:04:14 +0800 Subject: [PATCH 22/25] Add regression tests for derive suggestions on generated enums --- .../auxiliary/generated-enum-derive.rs | 23 +++++++++ ...e-suggestion-issue-148207.generated.stderr | 33 +++++++++++++ ...enerated-derive-suggestion-issue-148207.rs | 45 ++++++++++++++++++ ...erive-suggestion-issue-148207.source.fixed | 47 +++++++++++++++++++ ...rive-suggestion-issue-148207.source.stderr | 33 +++++++++++++ 5 files changed, 181 insertions(+) create mode 100644 tests/ui/derives/auxiliary/generated-enum-derive.rs create mode 100644 tests/ui/derives/generated-derive-suggestion-issue-148207.generated.stderr create mode 100644 tests/ui/derives/generated-derive-suggestion-issue-148207.rs create mode 100644 tests/ui/derives/generated-derive-suggestion-issue-148207.source.fixed create mode 100644 tests/ui/derives/generated-derive-suggestion-issue-148207.source.stderr diff --git a/tests/ui/derives/auxiliary/generated-enum-derive.rs b/tests/ui/derives/auxiliary/generated-enum-derive.rs new file mode 100644 index 0000000000000..cdc601dc9fb77 --- /dev/null +++ b/tests/ui/derives/auxiliary/generated-enum-derive.rs @@ -0,0 +1,23 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; + +// Like a compile-time generator, emit a macro whose output uses the attribute's call site. +#[proc_macro_attribute] +pub fn generator(_: TokenStream, _: TokenStream) -> TokenStream { + "macro_rules! gen_enums_from_list { + ([\"X\"]) => { enum Position1 { X } }; + }" + .parse() + .unwrap() +} + +#[proc_macro] +pub fn generated_enum(_: TokenStream) -> TokenStream { + "enum Generated { X }".parse().unwrap() +} + +#[proc_macro_attribute] +pub fn passthrough(_: TokenStream, item: TokenStream) -> TokenStream { + item +} diff --git a/tests/ui/derives/generated-derive-suggestion-issue-148207.generated.stderr b/tests/ui/derives/generated-derive-suggestion-issue-148207.generated.stderr new file mode 100644 index 0000000000000..85437173eaf4a --- /dev/null +++ b/tests/ui/derives/generated-derive-suggestion-issue-148207.generated.stderr @@ -0,0 +1,33 @@ +error[E0277]: `Position1` doesn't implement `Debug` + --> $DIR/generated-derive-suggestion-issue-148207.rs:33:26 + | +LL | println!("{:?}", p1); + | ---- ^^ `Position1` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | | + | required by this formatting parameter + | + = help: the trait `Debug` is not implemented for `Position1` +help: consider annotating `Position1` with `#[derive(Debug)]` + | +LL + #[derive(Debug)] +LL | #[generated_enum_derive::generator] + | + +error[E0277]: `Generated` doesn't implement `Debug` + --> $DIR/generated-derive-suggestion-issue-148207.rs:35:26 + | +LL | println!("{:?}", Generated::X); + | ---- ^^^^^^^^^^^^ `Generated` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | | + | required by this formatting parameter + | + = help: the trait `Debug` is not implemented for `Generated` +help: consider annotating `Generated` with `#[derive(Debug)]` + | +LL + #[derive(Debug)] +LL | generated_enum_derive::generated_enum!(); + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/derives/generated-derive-suggestion-issue-148207.rs b/tests/ui/derives/generated-derive-suggestion-issue-148207.rs new file mode 100644 index 0000000000000..d3ba4d7c907f2 --- /dev/null +++ b/tests/ui/derives/generated-derive-suggestion-issue-148207.rs @@ -0,0 +1,45 @@ +//@ revisions: source generated +//@ [source] run-rustfix +//@ proc-macro: generated-enum-derive.rs + +//! Regression test for https://github.com/rust-lang/rust/issues/148207. +//! Derive suggestions must target a source ADT, not its code generator. + +#![allow(dead_code)] + +extern crate generated_enum_derive; + +#[cfg(generated)] +#[generated_enum_derive::generator] +fn gen_enums_from_list() {} + +#[cfg(generated)] +gen_enums_from_list!(["X"]); + +#[cfg(generated)] +generated_enum_derive::generated_enum!(); + +#[cfg(source)] +enum Source { X } + +#[cfg(source)] +#[generated_enum_derive::passthrough] +enum Forwarded { X } + +fn main() { + #[cfg(generated)] + { + let p1 = Position1::X; + println!("{:?}", p1); + //[generated]~^ ERROR `Position1` doesn't implement `Debug` + println!("{:?}", Generated::X); + //[generated]~^ ERROR `Generated` doesn't implement `Debug` + } + #[cfg(source)] + { + println!("{:?}", Source::X); + //[source]~^ ERROR `Source` doesn't implement `Debug` + println!("{:?}", Forwarded::X); + //[source]~^ ERROR `Forwarded` doesn't implement `Debug` + } +} diff --git a/tests/ui/derives/generated-derive-suggestion-issue-148207.source.fixed b/tests/ui/derives/generated-derive-suggestion-issue-148207.source.fixed new file mode 100644 index 0000000000000..da54b5f592371 --- /dev/null +++ b/tests/ui/derives/generated-derive-suggestion-issue-148207.source.fixed @@ -0,0 +1,47 @@ +//@ revisions: source generated +//@ [source] run-rustfix +//@ proc-macro: generated-enum-derive.rs + +//! Regression test for https://github.com/rust-lang/rust/issues/148207. +//! Derive suggestions must target a source ADT, not its code generator. + +#![allow(dead_code)] + +extern crate generated_enum_derive; + +#[cfg(generated)] +#[generated_enum_derive::generator] +fn gen_enums_from_list() {} + +#[cfg(generated)] +gen_enums_from_list!(["X"]); + +#[cfg(generated)] +generated_enum_derive::generated_enum!(); + +#[cfg(source)] +#[derive(Debug)] +enum Source { X } + +#[cfg(source)] +#[generated_enum_derive::passthrough] +#[derive(Debug)] +enum Forwarded { X } + +fn main() { + #[cfg(generated)] + { + let p1 = Position1::X; + println!("{:?}", p1); + //[generated]~^ ERROR `Position1` doesn't implement `Debug` + println!("{:?}", Generated::X); + //[generated]~^ ERROR `Generated` doesn't implement `Debug` + } + #[cfg(source)] + { + println!("{:?}", Source::X); + //[source]~^ ERROR `Source` doesn't implement `Debug` + println!("{:?}", Forwarded::X); + //[source]~^ ERROR `Forwarded` doesn't implement `Debug` + } +} diff --git a/tests/ui/derives/generated-derive-suggestion-issue-148207.source.stderr b/tests/ui/derives/generated-derive-suggestion-issue-148207.source.stderr new file mode 100644 index 0000000000000..15b2dac2251fb --- /dev/null +++ b/tests/ui/derives/generated-derive-suggestion-issue-148207.source.stderr @@ -0,0 +1,33 @@ +error[E0277]: `Source` doesn't implement `Debug` + --> $DIR/generated-derive-suggestion-issue-148207.rs:40:26 + | +LL | println!("{:?}", Source::X); + | ---- ^^^^^^^^^ `Source` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | | + | required by this formatting parameter + | + = help: the trait `Debug` is not implemented for `Source` +help: consider annotating `Source` with `#[derive(Debug)]` + | +LL + #[derive(Debug)] +LL | enum Source { X } + | + +error[E0277]: `Forwarded` doesn't implement `Debug` + --> $DIR/generated-derive-suggestion-issue-148207.rs:42:26 + | +LL | println!("{:?}", Forwarded::X); + | ---- ^^^^^^^^^^^^ `Forwarded` cannot be formatted using `{:?}` because it doesn't implement `Debug` + | | + | required by this formatting parameter + | + = help: the trait `Debug` is not implemented for `Forwarded` +help: consider annotating `Forwarded` with `#[derive(Debug)]` + | +LL + #[derive(Debug)] +LL | enum Forwarded { X } + | + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From 4a1b4b0ba31b2f23c5c85701fdb6e7d6b9d683bb Mon Sep 17 00:00:00 2001 From: Yukang Date: Thu, 10 Sep 2026 19:13:41 +0800 Subject: [PATCH 23/25] Avoid derive edits on macro-generated types --- .../src/error_reporting/traits/suggestions.rs | 3 +++ ...e-suggestion-issue-148207.generated.stderr | 21 ++++++++++++------- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 76567e4d27d1d..8de0514b9ef27 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -5088,6 +5088,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { ty::Adt(adt, args) if adt.did().is_local() => (adt, args), _ => return false, }; + if !self.tcx.def_span(adt.did()).can_be_used_for_suggestions() { + return false; + } let is_derivable_trait = match diagnostic_name { sym::Copy | sym::Clone => true, _ if adt.is_union() => false, diff --git a/tests/ui/derives/generated-derive-suggestion-issue-148207.generated.stderr b/tests/ui/derives/generated-derive-suggestion-issue-148207.generated.stderr index 85437173eaf4a..db4ca4fda9fb2 100644 --- a/tests/ui/derives/generated-derive-suggestion-issue-148207.generated.stderr +++ b/tests/ui/derives/generated-derive-suggestion-issue-148207.generated.stderr @@ -6,12 +6,16 @@ LL | println!("{:?}", p1); | | | required by this formatting parameter | - = help: the trait `Debug` is not implemented for `Position1` -help: consider annotating `Position1` with `#[derive(Debug)]` +help: the trait `Debug` is not implemented for `Position1` + --> $DIR/generated-derive-suggestion-issue-148207.rs:13:1 | -LL + #[derive(Debug)] LL | #[generated_enum_derive::generator] - | + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | gen_enums_from_list!(["X"]); + | --------------------------- in this macro invocation + = note: add `#[derive(Debug)]` to `Position1` or manually `impl Debug for Position1` + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `gen_enums_from_list` (in Nightly builds, run with -Z macro-backtrace for more info) error[E0277]: `Generated` doesn't implement `Debug` --> $DIR/generated-derive-suggestion-issue-148207.rs:35:26 @@ -21,12 +25,13 @@ LL | println!("{:?}", Generated::X); | | | required by this formatting parameter | - = help: the trait `Debug` is not implemented for `Generated` -help: consider annotating `Generated` with `#[derive(Debug)]` +help: the trait `Debug` is not implemented for `Generated` + --> $DIR/generated-derive-suggestion-issue-148207.rs:20:1 | -LL + #[derive(Debug)] LL | generated_enum_derive::generated_enum!(); - | + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = note: add `#[derive(Debug)]` to `Generated` or manually `impl Debug for Generated` + = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `generated_enum_derive::generated_enum` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors From 0db4e5593107a9681f8faa1033b3f92ebbb6b8de Mon Sep 17 00:00:00 2001 From: sgasho Date: Wed, 16 Sep 2026 18:01:12 +0000 Subject: [PATCH 24/25] offload: add libLLVM rpath for libomptarget --- src/bootstrap/src/core/build_steps/llvm.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs index f89ccb4c2a6a8..096184ee9e5b4 100644 --- a/src/bootstrap/src/core/build_steps/llvm.rs +++ b/src/bootstrap/src/core/build_steps/llvm.rs @@ -1409,6 +1409,21 @@ impl CommandLineStep for OmpOffload { ldflags.push_all(format!("-L{}", dir.display())); } + if builder.config.rpath_enabled(target) + && helpers::use_host_linker(target) + && llvm_output.link_shared() + && target.contains("linux") + { + // Same logic as in Lld::run + // We inform libomptarget.so where it can find LLVM's libraries + // by adding an rpath entry to the expected parent `lib` directory. + // + // Be careful when changing this path, we need to ensure it's quoted or escaped: + // `$ORIGIN` would otherwise be expanded when the `LdFlags` are passed verbatim to + // cmake. + ldflags.push_all("-Wl,-rpath,'$ORIGIN/../../../'"); + } + configure_cmake(builder, target, &mut cfg, true, ldflags, cflags, &[]); cfg.define("CMAKE_C_COMPILER", &clang) From 96009489074f6df7d84cd41502826c143ad8dfea Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Thu, 17 Sep 2026 15:51:39 +0200 Subject: [PATCH 25/25] Error on invalid uses of the `cfi_encoding` attribute. --- .../src/attributes/cfi_encoding.rs | 2 +- .../ui/sanitizer/cfi/invalid-attr-encoding.rs | 16 ++++++++------- .../cfi/invalid-attr-encoding.stderr | 20 +++++++++++++++++-- 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs b/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs index 8a9102a78e25b..fc75f0285bda1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfi_encoding.rs @@ -4,7 +4,7 @@ use super::prelude::*; pub(crate) struct CfiEncodingParser; impl SingleAttributeParser for CfiEncodingParser { const PATH: &[Symbol] = &[sym::cfi_encoding]; - const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowListWarnRest(&[ + const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Struct), Allow(Target::ForeignTy), Allow(Target::Enum), diff --git a/tests/ui/sanitizer/cfi/invalid-attr-encoding.rs b/tests/ui/sanitizer/cfi/invalid-attr-encoding.rs index 23ffabad62fe8..148711b877ad5 100644 --- a/tests/ui/sanitizer/cfi/invalid-attr-encoding.rs +++ b/tests/ui/sanitizer/cfi/invalid-attr-encoding.rs @@ -1,11 +1,13 @@ -// Verifies that invalid user-defined CFI encodings can't be used. -// -//@ needs-sanitizer-cfi -//@ compile-flags: -Clto -Cno-prepopulate-passes -Ctarget-feature=-crt-static -Zsanitizer=cfi +//! Checks for invalid uses of the `cfi_encoding` attribute -#![feature(cfi_encoding, no_core)] -#![no_core] -#![no_main] +#![feature(cfi_encoding)] +#![crate_type = "lib"] #[cfi_encoding] //~ ERROR malformed `cfi_encoding` attribute input pub struct Type1(i32); + +#[cfi_encoding = "Foo"] //~ ERROR the `cfi_encoding` attribute cannot be used on traits +pub trait X {} + +#[cfi_encoding = "Bar"] //~ ERROR the `cfi_encoding` attribute cannot be used on type aliases +pub type Y = Type1; diff --git a/tests/ui/sanitizer/cfi/invalid-attr-encoding.stderr b/tests/ui/sanitizer/cfi/invalid-attr-encoding.stderr index 620957e6e95d4..b1e8908cfee31 100644 --- a/tests/ui/sanitizer/cfi/invalid-attr-encoding.stderr +++ b/tests/ui/sanitizer/cfi/invalid-attr-encoding.stderr @@ -1,5 +1,5 @@ error[E0539]: malformed `cfi_encoding` attribute input - --> $DIR/invalid-attr-encoding.rs:10:3 + --> $DIR/invalid-attr-encoding.rs:6:3 | LL | #[cfi_encoding] | ^^^^^^^^^^^^ expected this to be of the form `cfi_encoding = "..."` @@ -9,6 +9,22 @@ help: must be of the form LL | #[cfi_encoding = "encoding"] | ++++++++++++ -error: aborting due to 1 previous error +error: the `cfi_encoding` attribute cannot be used on traits + --> $DIR/invalid-attr-encoding.rs:9:3 + | +LL | #[cfi_encoding = "Foo"] + | ^^^^^^^^^^^^ + | + = help: the `cfi_encoding` attribute can only be applied to data types + +error: the `cfi_encoding` attribute cannot be used on type aliases + --> $DIR/invalid-attr-encoding.rs:12:3 + | +LL | #[cfi_encoding = "Bar"] + | ^^^^^^^^^^^^ + | + = help: the `cfi_encoding` attribute can only be applied to data types + +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0539`.