From a63036074c5cf4beddac868a3a67229fe577bb48 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Fri, 12 Jun 2026 17:49:42 +0800 Subject: [PATCH 1/8] Query the trait solver in slow path --- compiler/rustc_lint/src/builtin.rs | 62 +++++++++++++------ .../impl-debug-for-alias-type.rs | 22 +++++++ .../impl-debug-for-project.rs | 20 ++++++ .../missing-debug-implementations-lint.rs | 0 .../missing-debug-implementations-lint.stderr | 0 5 files changed, 85 insertions(+), 19 deletions(-) create mode 100644 tests/ui/lint/missing-debug-implementations-lint/impl-debug-for-alias-type.rs create mode 100644 tests/ui/lint/missing-debug-implementations-lint/impl-debug-for-project.rs rename tests/ui/lint/{ => missing-debug-implementations-lint}/missing-debug-implementations-lint.rs (100%) rename tests/ui/lint/{ => missing-debug-implementations-lint}/missing-debug-implementations-lint.stderr (100%) diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 2c7ccfb25ae9b..c822651b47e00 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -685,37 +685,61 @@ impl_lint_pass!(MissingDebugImplementations => [MISSING_DEBUG_IMPLEMENTATIONS]); impl<'tcx> LateLintPass<'tcx> for MissingDebugImplementations { fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) { - if !cx.effective_visibilities.is_reachable(item.owner_id.def_id) { + let def_id = item.owner_id.def_id; + if !cx.effective_visibilities.is_reachable(def_id) { return; } - match item.kind { - hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) | hir::ItemKind::Enum(..) => {} + let is_generic = match item.kind { + hir::ItemKind::Struct(_, generics, _) + | hir::ItemKind::Union(_, generics, _) + | hir::ItemKind::Enum(_, generics, _) => !generics.params.is_empty(), _ => return, - } + }; + + let tcx = cx.tcx; // Avoid listing trait impls if the trait is allowed. - if cx.tcx.lint_level_spec_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id()).is_allow() { + if tcx.lint_level_spec_at_node(MISSING_DEBUG_IMPLEMENTATIONS, item.hir_id()).is_allow() { return; } - let Some(debug) = cx.tcx.get_diagnostic_item(sym::Debug) else { return }; + let Some(debug) = tcx.get_diagnostic_item(sym::Debug) else { return }; - let has_impl = cx - .tcx - .non_blanket_impls_for_ty( - debug, - cx.tcx.type_of(item.owner_id).instantiate_identity().skip_norm_wip(), - ) + let ty = tcx.type_of(item.owner_id); + if tcx + .non_blanket_impls_for_ty(debug, ty.instantiate_identity().skip_norm_wip()) .next() - .is_some(); - if !has_impl { - cx.emit_span_lint( - MISSING_DEBUG_IMPLEMENTATIONS, - item.span, - BuiltinMissingDebugImpl { tcx: cx.tcx, def_id: debug }, - ); + .is_some() + { + return; } + + let infcx = tcx.infer_ctxt().build(cx.typing_mode()); + if is_generic { + let args = infcx.fresh_args_for_item(item.span, def_id.to_def_id()); + if infcx + .type_implements_trait_shallow( + debug, + ty.instantiate(tcx, args).skip_norm_wip(), + cx.param_env, + ) + .is_some() + { + return; + } + } else if infcx + .type_implements_trait(debug, [ty.instantiate_identity().skip_norm_wip()], cx.param_env) + .must_apply_modulo_regions() + { + return; + } + + cx.emit_span_lint( + MISSING_DEBUG_IMPLEMENTATIONS, + item.span, + BuiltinMissingDebugImpl { tcx: cx.tcx, def_id: debug }, + ); } } diff --git a/tests/ui/lint/missing-debug-implementations-lint/impl-debug-for-alias-type.rs b/tests/ui/lint/missing-debug-implementations-lint/impl-debug-for-alias-type.rs new file mode 100644 index 0000000000000..5222df24b61a6 --- /dev/null +++ b/tests/ui/lint/missing-debug-implementations-lint/impl-debug-for-alias-type.rs @@ -0,0 +1,22 @@ +//@ check-pass + +#![feature(lazy_type_alias)] +#![deny(missing_debug_implementations)] + +pub struct Local; + +pub type Alias = Local; + +impl std::fmt::Debug for Alias { + fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Ok(()) } +} + +pub struct Generic(T); + +pub type GenericAlias = Generic; + +impl std::fmt::Debug for GenericAlias { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Ok(()) } +} + +fn main() {} diff --git a/tests/ui/lint/missing-debug-implementations-lint/impl-debug-for-project.rs b/tests/ui/lint/missing-debug-implementations-lint/impl-debug-for-project.rs new file mode 100644 index 0000000000000..ecdca8c211382 --- /dev/null +++ b/tests/ui/lint/missing-debug-implementations-lint/impl-debug-for-project.rs @@ -0,0 +1,20 @@ +//@ check-pass + +#![deny(missing_debug_implementations)] + +pub struct Local; + +impl std::fmt::Debug for ::Output { + fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Ok(()) } +} + +pub trait Identity { type Output; } +impl Identity for T { type Output = T; } + +pub struct Generic(T); + +impl std::fmt::Debug for as Identity>::Output { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { Ok(()) } +} + +fn main() {} diff --git a/tests/ui/lint/missing-debug-implementations-lint.rs b/tests/ui/lint/missing-debug-implementations-lint/missing-debug-implementations-lint.rs similarity index 100% rename from tests/ui/lint/missing-debug-implementations-lint.rs rename to tests/ui/lint/missing-debug-implementations-lint/missing-debug-implementations-lint.rs diff --git a/tests/ui/lint/missing-debug-implementations-lint.stderr b/tests/ui/lint/missing-debug-implementations-lint/missing-debug-implementations-lint.stderr similarity index 100% rename from tests/ui/lint/missing-debug-implementations-lint.stderr rename to tests/ui/lint/missing-debug-implementations-lint/missing-debug-implementations-lint.stderr From 1f7863d3a1b485262e70a0d73653399e9fc16328 Mon Sep 17 00:00:00 2001 From: Augie Fackler Date: Fri, 12 Jun 2026 14:58:37 -0400 Subject: [PATCH 2/8] tests: adapt two tests for LLVM 23 changes LLVM 23 recently changed SimplifyCFG to avoid integer lookup tables, and that perturbed these two tests in ways that look harmless to me. --- tests/codegen-llvm/issues/issue-118306.rs | 13 ++++++--- tests/codegen-llvm/pow_known_base.rs | 33 ++++++++++++++++------- 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/tests/codegen-llvm/issues/issue-118306.rs b/tests/codegen-llvm/issues/issue-118306.rs index ea8b24317ff15..19623e17815d0 100644 --- a/tests/codegen-llvm/issues/issue-118306.rs +++ b/tests/codegen-llvm/issues/issue-118306.rs @@ -1,5 +1,8 @@ //@ compile-flags: -Copt-level=3 //@ only-x86_64 +//@ revisions: LLVM22 LLVM23 +//@ [LLVM22] max-llvm-major-version: 22 +//@ [LLVM23] min-llvm-version: 23 // Test for #118306. // Make sure we don't create `br` or `select` instructions. @@ -11,9 +14,13 @@ pub fn branchy(input: u64) -> u64 { // CHECK-LABEL: @branchy( // CHECK-NEXT: start: // CHECK-NEXT: [[_2:%.*]] = and i64 [[INPUT:%.*]], 3 - // CHECK-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds{{( nuw)?}} {{\[4 x i64\]|i64|\[8 x i8\]}}, ptr @switch.table.branchy{{(, i64 0)?}}, i64 [[_2]] - // CHECK-NEXT: [[SWITCH_LOAD:%.*]] = load i64, ptr [[SWITCH_GEP]] - // CHECK-NEXT: ret i64 [[SWITCH_LOAD]] + // LLVM22-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds{{( nuw)?}} {{\[4 x i64\]|i64|\[8 x i8\]}}, ptr @switch.table.branchy{{(, i64 0)?}}, i64 [[_2]] + // LLVM22-NEXT: [[SWITCH_LOAD:%.*]] = load i64, ptr [[SWITCH_GEP]] + // LLVM22-NEXT: ret i64 [[SWITCH_LOAD]] + // LLVM23-NEXT: [[SWITCH_GEP:%.*]] = getelementptr inbounds{{( nuw)?}} i8, ptr @switch.table.branchy, i64 [[_2]] + // LLVM23-NEXT: [[SWITCH_LOAD:%.*]] = load i8, ptr [[SWITCH_GEP]], align 1 + // LLVM23-NEXT: [[SWITCH_EXT:%.*]] = zext i8 [[SWITCH_LOAD]] to i64 + // LLVM23-NEXT: ret i64 [[SWITCH_EXT]] match input % 4 { 1 | 2 => 1, 3 => 2, diff --git a/tests/codegen-llvm/pow_known_base.rs b/tests/codegen-llvm/pow_known_base.rs index ea0e40515f9e4..f7e649ef089ea 100644 --- a/tests/codegen-llvm/pow_known_base.rs +++ b/tests/codegen-llvm/pow_known_base.rs @@ -1,4 +1,7 @@ //@ compile-flags: -Copt-level=3 +//@ revisions: LLVM22 LLVM23 +//@ [LLVM22] max-llvm-major-version: 22 +//@ [LLVM23] min-llvm-version: 23 // Test that `pow` can use a faster implementation when `base` is a // known power of two @@ -21,11 +24,16 @@ pub fn pow2(exp: u32) -> u32 { pub fn pow4(exp: u32) -> u32 { // CHECK: %[[ICMP1:.+]] = icmp slt i32 %exp, 0 // CHECK: %[[SHIFT_AMOUNT:.+]] = shl i32 %exp, 1 - // CHECK: %[[ICMP2:.+]] = icmp ult i32 %[[SHIFT_AMOUNT]], 32 - // CHECK: %[[POW:.+]] = shl nuw i32 1, %[[SHIFT_AMOUNT]] - // CHECK: %[[SEL:.+]] = select i1 %[[ICMP2]], i32 %[[POW]], i32 0 - // CHECK: %[[RET:.+]] = select i1 %[[ICMP1]], i32 0, i32 %[[SEL]] - // CHECK: ret i32 %[[RET]] + // LLVM22: %[[ICMP2:.+]] = icmp ult i32 %[[SHIFT_AMOUNT]], 32 + // LLVM22: %[[POW:.+]] = shl nuw i32 1, %[[SHIFT_AMOUNT]] + // LLVM22: %[[SEL:.+]] = select i1 %[[ICMP2]], i32 %[[POW]], i32 0 + // LLVM22: %[[RET:.+]] = select i1 %[[ICMP1]], i32 0, i32 %[[SEL]] + // LLVM22: ret i32 %[[RET]] + // LLVM23: %[[ICMP2:.+]] = icmp ugt i32 %[[SHIFT_AMOUNT]], 31 + // LLVM23: %[[POW:.+]] = shl nuw i32 1, %[[SHIFT_AMOUNT]] + // LLVM23: %[[COND:.+]] = or i1 %[[ICMP1]], %[[ICMP2]] + // LLVM23: %[[RET:.+]] = select i1 %[[COND]], i32 0, i32 %[[POW]] + // LLVM23: ret i32 %[[RET]] 4u32.pow(exp) } @@ -35,11 +43,16 @@ pub fn pow4(exp: u32) -> u32 { pub fn pow16(exp: u32) -> u32 { // CHECK: %[[ICMP1:.+]] = icmp ugt i32 %exp, 1073741823 // CHECK: %[[SHIFT_AMOUNT:.+]] = shl i32 %exp, 2 - // CHECK: %[[ICMP2:.+]] = icmp ult i32 %[[SHIFT_AMOUNT]], 32 - // CHECK: %[[POW:.+]] = shl nuw i32 1, %[[SHIFT_AMOUNT]] - // CHECK: %[[SEL:.+]] = select i1 %[[ICMP2]], i32 %[[POW]], i32 0 - // CHECK: %[[RET:.+]] = select i1 %[[ICMP1]], i32 0, i32 %[[SEL]] - // CHECK: ret i32 %[[RET]] + // LLVM22: %[[ICMP2:.+]] = icmp ult i32 %[[SHIFT_AMOUNT]], 32 + // LLVM22: %[[POW:.+]] = shl nuw i32 1, %[[SHIFT_AMOUNT]] + // LLVM22: %[[SEL:.+]] = select i1 %[[ICMP2]], i32 %[[POW]], i32 0 + // LLVM22: %[[RET:.+]] = select i1 %[[ICMP1]], i32 0, i32 %[[SEL]] + // LLVM22: ret i32 %[[RET]] + // LLVM23: %[[ICMP2:.+]] = icmp ugt i32 %[[SHIFT_AMOUNT]], 31 + // LLVM23: %[[POW:.+]] = shl nuw i32 1, %[[SHIFT_AMOUNT]] + // LLVM23: %[[COND:.+]] = or i1 %[[ICMP1]], %[[ICMP2]] + // LLVM23: %[[RET:.+]] = select i1 %[[COND]], i32 0, i32 %[[POW]] + // LLVM23: ret i32 %[[RET]] 16u32.pow(exp) } From 0ab14caeaf5f7ef987e047ccbbbdda59876f86de Mon Sep 17 00:00:00 2001 From: Haoran Wang Date: Mon, 15 Jun 2026 19:35:07 +0800 Subject: [PATCH 3/8] Don't suggest adding `in` to a `for` loop that already has one When a `for` loop is missing its `in`, the parser suggested inserting one based only on the token after the pattern. A malformed binding such as `for i i in 0..10` was therefore "corrected" to `for i in i in 0..10`, which does not parse. Only suggest inserting `in` when the loop header does not already contain one before the body. --- compiler/rustc_parse/src/errors.rs | 2 +- compiler/rustc_parse/src/parser/expr.rs | 33 ++++++++++++++++--- .../for-loop-no-spurious-in-suggestion.rs | 9 +++++ .../for-loop-no-spurious-in-suggestion.stderr | 14 ++++++++ 4 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 tests/ui/suggestions/for-loop-no-spurious-in-suggestion.rs create mode 100644 tests/ui/suggestions/for-loop-no-spurious-in-suggestion.stderr diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 3a726cbce8182..0c850b69ca7f1 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -689,7 +689,7 @@ pub(crate) struct MissingInInForLoop { #[primary_span] pub span: Span, #[subdiagnostic] - pub sub: MissingInInForLoopSub, + pub sub: Option, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index e414e10e84169..c81b3dd6ab5c0 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -3086,18 +3086,41 @@ impl<'a> Parser<'a> { } fn error_missing_in_for_loop(&mut self) { - let (span, sub): (_, fn(_) -> _) = if self.token.is_ident_named(sym::of) { + let (span, sub) = if self.token.is_ident_named(sym::of) { // Possibly using JS syntax (#75311). let span = self.token.span; self.bump(); - (span, errors::MissingInInForLoopSub::InNotOf) + (span, Some(errors::MissingInInForLoopSub::InNotOf(span))) } else if self.eat(exp!(Eq)) { - (self.prev_token.span, errors::MissingInInForLoopSub::InNotEq) + let span = self.prev_token.span; + (span, Some(errors::MissingInInForLoopSub::InNotEq(span))) } else { - (self.prev_token.span.between(self.token.span), errors::MissingInInForLoopSub::AddIn) + let span = self.prev_token.span.between(self.token.span); + let sub = (!self.for_loop_head_has_in()) + .then_some(errors::MissingInInForLoopSub::AddIn(span)); + (span, sub) }; - self.dcx().emit_err(errors::MissingInInForLoop { span, sub: sub(span) }); + self.dcx().emit_err(errors::MissingInInForLoop { span, sub }); + } + + /// Whether the `for` loop header already contains an `in` before its body. + /// If it does, the binding is malformed (e.g. `for i i in 0..10`) rather + /// than missing `in`, so suggesting another `in` would just be invalid too. + fn for_loop_head_has_in(&self) -> bool { + let mut dist = 0; + loop { + let (is_in, is_end) = self.look_ahead(dist, |t| { + (t.is_keyword(kw::In), matches!(t.kind, token::OpenBrace | token::Eof)) + }); + if is_in { + return true; + } + if is_end { + return false; + } + dist += 1; + } } /// Parses a `while` or `while let` expression (`while` token already eaten). diff --git a/tests/ui/suggestions/for-loop-no-spurious-in-suggestion.rs b/tests/ui/suggestions/for-loop-no-spurious-in-suggestion.rs new file mode 100644 index 0000000000000..142f925665881 --- /dev/null +++ b/tests/ui/suggestions/for-loop-no-spurious-in-suggestion.rs @@ -0,0 +1,9 @@ +// Regression test for https://github.com/rust-lang/rust/issues/103561: when the +// `for` loop header already contains an `in`, a missing `in` is not the problem, +// so we must not suggest inserting another one (which would not compile). + +fn main() { + for i i in 0..10 {} + //~^ ERROR missing `in` in `for` loop + //~| ERROR expected `{`, found keyword `in` +} diff --git a/tests/ui/suggestions/for-loop-no-spurious-in-suggestion.stderr b/tests/ui/suggestions/for-loop-no-spurious-in-suggestion.stderr new file mode 100644 index 0000000000000..472ee7d9ddca2 --- /dev/null +++ b/tests/ui/suggestions/for-loop-no-spurious-in-suggestion.stderr @@ -0,0 +1,14 @@ +error: missing `in` in `for` loop + --> $DIR/for-loop-no-spurious-in-suggestion.rs:6:10 + | +LL | for i i in 0..10 {} + | ^ + +error: expected `{`, found keyword `in` + --> $DIR/for-loop-no-spurious-in-suggestion.rs:6:13 + | +LL | for i i in 0..10 {} + | ^^ expected `{` + +error: aborting due to 2 previous errors + From b77dcf4641a02f5dd0e6da86ad22327786c55943 Mon Sep 17 00:00:00 2001 From: Kevin Valerio Date: Tue, 16 Jun 2026 15:16:56 +0200 Subject: [PATCH 4/8] fix(rustc_middle): preserve track_caller for vtable shims --- compiler/rustc_middle/src/ty/instance.rs | 4 +++- .../track-caller-vtable-shim.rs | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 tests/ui/unsized-locals/track-caller-vtable-shim.rs diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index f85355330d6b8..8734d2cde8152 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -310,7 +310,9 @@ impl<'tcx> InstanceKind<'tcx> { pub fn requires_caller_location(&self, tcx: TyCtxt<'_>) -> bool { match *self { - InstanceKind::Item(def_id) | InstanceKind::Virtual(def_id, _) => { + InstanceKind::Item(def_id) + | InstanceKind::Virtual(def_id, _) + | InstanceKind::VTableShim(def_id) => { tcx.body_codegen_attrs(def_id).flags.contains(CodegenFnAttrFlags::TRACK_CALLER) } InstanceKind::ClosureOnceShim { call_once: _, closure: _, track_caller } => { diff --git a/tests/ui/unsized-locals/track-caller-vtable-shim.rs b/tests/ui/unsized-locals/track-caller-vtable-shim.rs new file mode 100644 index 0000000000000..575f48249e331 --- /dev/null +++ b/tests/ui/unsized-locals/track-caller-vtable-shim.rs @@ -0,0 +1,21 @@ +//@ run-pass + +#![feature(unsized_fn_params)] +#![allow(internal_features)] + +trait TrackedByValue { + #[track_caller] + fn consume(self, expected_line: u32); +} + +impl TrackedByValue for u8 { + fn consume(self, expected_line: u32) { + assert_eq!(self, 7); + assert_eq!(std::panic::Location::caller().line(), expected_line); + } +} + +fn main() { + let obj = Box::new(7_u8) as Box; + obj.consume(line!()); +} From 115338beb8bb6f3294caa4c5fe9018560ba3b285 Mon Sep 17 00:00:00 2001 From: Leonard Chan Date: Thu, 11 Jun 2026 20:25:24 +0000 Subject: [PATCH 5/8] rustc_session: accumulate multiple -Zsanitizer target modifiers When passing multiple `-Zsanitizer` flags to the compiler (e.g., `-Zsanitizer=address -Zsanitizer=shadow-call-stack`), the options parser was overwriting the previous values in `target_modifiers` instead of accumulating them. This resulted in only the last sanitizer being recorded in the crate metadata's target modifiers, even though the frontend correctly enabled all of them. The only way to provide multiple sanitizers was to combine them into a comma-separated list passed to a single `-Zsanitizer=` flag, but this does not fit well into the GN build system where different targets may pass different combinations of sanitizer flags. Consequently, this caused spurious "incompatible target modifiers" ABI mismatch errors when linking against dependencies compiled with accumulated flags (e.g., `-Zsanitizer=address,shadow-call-stack`). Fix this by entry-modifying the `target_modifiers` map to accumulate the sanitizers as a comma-separated list when the option is `sanitizer`. Also add a codegen test verifying that both CFI and SafeStack attributes/metadata are present when enabled together via multiple flags. Test: ./x.py test tests/codegen-llvm/sanitizer/multiple-sanitizers.rs --- compiler/rustc_session/src/options.rs | 20 ++++++++++++++++++- .../sanitizer/multiple-sanitizers.rs | 18 +++++++++++++++++ ...zers-safestack-and-kcfi.missed_both.stderr | 2 +- ...zers-safestack-and-kcfi.missed_kcfi.stderr | 2 +- ...safestack-and-kcfi.missed_safestack.stderr | 2 +- .../sanitizers-safestack-and-kcfi.rs | 4 +++- 6 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 tests/codegen-llvm/sanitizer/multiple-sanitizers.rs diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 0582791dad005..e80f89edb485f 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -726,7 +726,25 @@ fn build_options( } if let Some(tmod) = *tmod { let v = value.map_or(String::new(), ToOwned::to_owned); - collected_options.target_modifiers.insert(tmod, v); + + // Accumulate all the -Zsanitizer flags into a single target modifier. + match tmod { + OptionsTargetModifiers::UnstableOptions( + UnstableOptionsTargetModifiers::Sanitizer, + ) => { + collected_options + .target_modifiers + .entry(tmod) + .and_modify(|existing| { + existing.push(','); + existing.push_str(&v); + }) + .or_insert(v); + } + _ => { + collected_options.target_modifiers.insert(tmod, v); + } + } } if let Some(mitigation) = mitigation { collected_options.mitigations.reset_mitigation(*mitigation, index); diff --git a/tests/codegen-llvm/sanitizer/multiple-sanitizers.rs b/tests/codegen-llvm/sanitizer/multiple-sanitizers.rs new file mode 100644 index 0000000000000..862999e7ac026 --- /dev/null +++ b/tests/codegen-llvm/sanitizer/multiple-sanitizers.rs @@ -0,0 +1,18 @@ +// Verifies that multiple compatible sanitizers (CFI + SafeStack) can be enabled together +// and their target modifiers accumulate instead of overwriting. +// +//@ only-x86_64 +//@ only-linux +//@ needs-sanitizer-cfi +//@ needs-sanitizer-safestack +//@ compile-flags: -Zsanitizer=cfi -Zsanitizer=safestack -Clto -Ccodegen-units=1 -C unsafe-allow-abi-mismatch=sanitizer + +#![crate_type = "lib"] + +// CHECK: ; Function Attrs:{{.*}}safestack +// CHECK: define{{.*}}foo{{.*}}!type +pub fn foo(f: fn(i32) -> i32, arg: i32) -> i32 { + f(arg) +} + +// CHECK: attributes #0 = {{.*}}safestack{{.*}} diff --git a/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_both.stderr b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_both.stderr index 440a91c7707f8..44a2c24b2324e 100644 --- a/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_both.stderr +++ b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_both.stderr @@ -1,5 +1,5 @@ error: mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizers_safestack_and_kcfi` - --> $DIR/sanitizers-safestack-and-kcfi.rs:16:1 + --> $DIR/sanitizers-safestack-and-kcfi.rs:18:1 | LL | #![feature(no_core)] | ^ diff --git a/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_kcfi.stderr b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_kcfi.stderr index 6cdd7facc3781..e951b36969b78 100644 --- a/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_kcfi.stderr +++ b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_kcfi.stderr @@ -1,5 +1,5 @@ error: mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizers_safestack_and_kcfi` - --> $DIR/sanitizers-safestack-and-kcfi.rs:16:1 + --> $DIR/sanitizers-safestack-and-kcfi.rs:18:1 | LL | #![feature(no_core)] | ^ diff --git a/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_safestack.stderr b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_safestack.stderr index ecfbcace39fe5..9fd2738ef45ab 100644 --- a/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_safestack.stderr +++ b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.missed_safestack.stderr @@ -1,5 +1,5 @@ error: mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizers_safestack_and_kcfi` - --> $DIR/sanitizers-safestack-and-kcfi.rs:16:1 + --> $DIR/sanitizers-safestack-and-kcfi.rs:18:1 | LL | #![feature(no_core)] | ^ diff --git a/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.rs b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.rs index 6c3ceb7e91008..eb4b4d6194228 100644 --- a/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.rs +++ b/tests/ui/target_modifiers/sanitizers-safestack-and-kcfi.rs @@ -4,14 +4,16 @@ //@ aux-build:safestack-and-kcfi.rs //@ compile-flags: -Cpanic=abort -//@ revisions: good good_reverted missed_safestack missed_kcfi missed_both +//@ revisions: good good_reverted good_multiple missed_safestack missed_kcfi missed_both //@[good] compile-flags: -Zsanitizer=safestack,kcfi //@[good_reverted] compile-flags: -Zsanitizer=kcfi,safestack +//@[good_multiple] compile-flags: -Zsanitizer=safestack -Zsanitizer=kcfi //@[missed_safestack] compile-flags: -Zsanitizer=kcfi //@[missed_kcfi] compile-flags: -Zsanitizer=safestack // [missed_both] no additional compile-flags: //@[good] check-pass //@[good_reverted] check-pass +//@[good_multiple] check-pass #![feature(no_core)] //[missed_safestack]~^ ERROR mixing `-Zsanitizer` will cause an ABI mismatch in crate `sanitizers_safestack_and_kcfi` From 439746b582dfda6e59ca866fb05ea110f365ae62 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Wed, 17 Jun 2026 11:51:55 +0300 Subject: [PATCH 6/8] Stabilize `strip_circumfix` --- compiler/rustc_metadata/src/lib.rs | 2 +- compiler/rustc_trait_selection/src/lib.rs | 2 +- library/core/src/slice/mod.rs | 4 +--- library/core/src/str/mod.rs | 4 +--- 4 files changed, 4 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_metadata/src/lib.rs b/compiler/rustc_metadata/src/lib.rs index 51c56d777a751..e782414c62a0a 100644 --- a/compiler/rustc_metadata/src/lib.rs +++ b/compiler/rustc_metadata/src/lib.rs @@ -1,6 +1,7 @@ // tidy-alphabetical-start #![allow(internal_features)] #![cfg_attr(bootstrap, feature(result_option_map_or_default))] +#![cfg_attr(bootstrap, feature(strip_circumfix))] #![feature(error_iter)] #![feature(file_buffered)] #![feature(gen_blocks)] @@ -8,7 +9,6 @@ #![feature(min_specialization)] #![feature(never_type)] #![feature(proc_macro_internals)] -#![feature(strip_circumfix)] #![feature(trusted_len)] // tidy-alphabetical-end diff --git a/compiler/rustc_trait_selection/src/lib.rs b/compiler/rustc_trait_selection/src/lib.rs index 9f1bfc018f3a2..8900687036d41 100644 --- a/compiler/rustc_trait_selection/src/lib.rs +++ b/compiler/rustc_trait_selection/src/lib.rs @@ -11,6 +11,7 @@ //! This API is completely unstable and subject to change. // tidy-alphabetical-start +#![cfg_attr(bootstrap, feature(strip_circumfix))] #![feature(associated_type_defaults)] #![feature(default_field_values)] #![feature(deref_patterns)] @@ -18,7 +19,6 @@ #![feature(iter_intersperse)] #![feature(iterator_try_reduce)] #![feature(never_type)] -#![feature(strip_circumfix)] #![feature(try_blocks)] #![feature(unwrap_infallible)] #![feature(yeet_expr)] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index ae6cc46a22a84..ede5985a4211a 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -2744,8 +2744,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(strip_circumfix)] - /// /// let v = &[10, 50, 40, 30]; /// assert_eq!(v.strip_circumfix(&[10], &[30]), Some(&[50, 40][..])); /// assert_eq!(v.strip_circumfix(&[10], &[40, 30]), Some(&[50][..])); @@ -2756,7 +2754,7 @@ impl [T] { /// assert_eq!(v.strip_circumfix(&[10, 50], &[]), Some(&[40, 30][..])); /// ``` #[must_use = "returns the subslice without modifying the original"] - #[unstable(feature = "strip_circumfix", issue = "147946")] + #[stable(feature = "strip_circumfix", since = "CURRENT_RUSTC_VERSION")] pub fn strip_circumfix(&self, prefix: &P, suffix: &S) -> Option<&[T]> where T: PartialEq, diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 9be44cd89de5e..68cdb69059f05 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -2510,15 +2510,13 @@ impl str { /// # Examples /// /// ``` - /// #![feature(strip_circumfix)] - /// /// assert_eq!("bar:hello:foo".strip_circumfix("bar:", ":foo"), Some("hello")); /// assert_eq!("bar:foo".strip_circumfix("foo", "foo"), None); /// assert_eq!("foo:bar;".strip_circumfix("foo:", ';'), Some("bar")); /// ``` #[must_use = "this returns the remaining substring as a new slice, \ without modifying the original"] - #[unstable(feature = "strip_circumfix", issue = "147946")] + #[stable(feature = "strip_circumfix", since = "CURRENT_RUSTC_VERSION")] pub fn strip_circumfix(&self, prefix: P, suffix: S) -> Option<&str> where for<'a> S::Searcher<'a>: ReverseSearcher<'a>, From 6f4711f559aacef09f45d5b4ef14b570c7520b87 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Wed, 17 Jun 2026 13:05:13 +0300 Subject: [PATCH 7/8] Add simple test for incremental compilation of delegations --- tests/incremental/delegation-ice-155729.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 tests/incremental/delegation-ice-155729.rs diff --git a/tests/incremental/delegation-ice-155729.rs b/tests/incremental/delegation-ice-155729.rs new file mode 100644 index 0000000000000..5316e7b66cd5b --- /dev/null +++ b/tests/incremental/delegation-ice-155729.rs @@ -0,0 +1,14 @@ +//@ revisions: bpass + +#![feature(fn_delegation)] + +pub mod to_reuse { + pub fn bar() {} +} + +mod a { + use to_reuse; + reuse to_reuse::bar; +} + +fn main() {} From d50d1db7e539ee53c329afd18f4f77050a42beb3 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Wed, 17 Jun 2026 13:46:30 +0200 Subject: [PATCH 8/8] Move `UnusedDuplicate` diag struct to `rustc_attr_parsing` --- compiler/rustc_attr_parsing/src/attributes/doc.rs | 8 ++------ compiler/rustc_attr_parsing/src/context.rs | 14 +++----------- .../rustc_attr_parsing/src/session_diagnostics.rs | 13 +++++++++++++ compiler/rustc_errors/src/lib.rs | 1 - compiler/rustc_errors/src/lints.rs | 15 --------------- 5 files changed, 18 insertions(+), 33 deletions(-) delete mode 100644 compiler/rustc_errors/src/lints.rs diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 382f0539f7678..e91ab584bcf47 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -22,7 +22,7 @@ use crate::diagnostics::{ use crate::parser::{ArgParser, MetaItemOrLitParser, MetaItemParser, OwnedPathParser}; use crate::session_diagnostics::{ DocAliasBadChar, DocAliasEmpty, DocAliasMalformed, DocAliasStartEnd, DocAttrNotCrateLevel, - DocAttributeNotAttribute, DocKeywordNotKeyword, + DocAttributeNotAttribute, DocKeywordNotKeyword, UnusedDuplicate, }; fn check_keyword(cx: &mut AcceptContext<'_, '_>, keyword: Symbol, span: Span) -> bool { @@ -159,11 +159,7 @@ impl DocParser { let unused_span = path.span(); cx.emit_lint( rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - rustc_errors::lints::UnusedDuplicate { - this: unused_span, - other: used_span, - warning: true, - }, + UnusedDuplicate { this: unused_span, other: used_span, warning: true }, unused_span, ); return; diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index fb5ca0375900a..2811bc8ff5683 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -71,7 +71,7 @@ use crate::parser::{ }; use crate::session_diagnostics::{ AttributeParseError, AttributeParseErrorReason, AttributeParseErrorSuggestions, - ParsedDescription, + ParsedDescription, UnusedDuplicate, }; use crate::target_checking::AllowedTargets; use crate::{AttrSuggestionStyle, AttributeParser, AttributeTemplate, EmitAttribute}; @@ -436,11 +436,7 @@ impl<'f, 'sess: 'f> SharedContext<'f, 'sess> { pub(crate) fn warn_unused_duplicate(&mut self, used_span: Span, unused_span: Span) { self.emit_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - rustc_errors::lints::UnusedDuplicate { - this: unused_span, - other: used_span, - warning: false, - }, + UnusedDuplicate { this: unused_span, other: used_span, warning: false }, unused_span, ) } @@ -452,11 +448,7 @@ impl<'f, 'sess: 'f> SharedContext<'f, 'sess> { ) { self.emit_lint( rustc_session::lint::builtin::UNUSED_ATTRIBUTES, - rustc_errors::lints::UnusedDuplicate { - this: unused_span, - other: used_span, - warning: true, - }, + UnusedDuplicate { this: unused_span, other: used_span, warning: true }, unused_span, ) } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index 193ffe195fb6c..d1470d870d130 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -1045,3 +1045,16 @@ pub(crate) struct AdditionalCommaSuggestion { #[primary_span] pub span: Span, } + +#[derive(Diagnostic)] +#[diag("unused attribute")] +pub(crate) struct UnusedDuplicate { + #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] + pub this: Span, + #[note("attribute also specified here")] + pub other: Span, + #[warning( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" + )] + pub warning: bool, +} diff --git a/compiler/rustc_errors/src/lib.rs b/compiler/rustc_errors/src/lib.rs index 76cdb10f429c7..e9bb6701c38f1 100644 --- a/compiler/rustc_errors/src/lib.rs +++ b/compiler/rustc_errors/src/lib.rs @@ -79,7 +79,6 @@ mod diagnostic_impls; pub mod emitter; pub mod formatting; pub mod json; -pub mod lints; mod lock; pub mod markdown; pub mod timings; diff --git a/compiler/rustc_errors/src/lints.rs b/compiler/rustc_errors/src/lints.rs deleted file mode 100644 index 9c93a09bf764c..0000000000000 --- a/compiler/rustc_errors/src/lints.rs +++ /dev/null @@ -1,15 +0,0 @@ -use rustc_macros::Diagnostic; -use rustc_span::Span; - -#[derive(Diagnostic)] -#[diag("unused attribute")] -pub struct UnusedDuplicate { - #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] - pub this: Span, - #[note("attribute also specified here")] - pub other: Span, - #[warning( - "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" - )] - pub warning: bool, -}