From 11c867e3a510ab73e59fa837c0aab5fc362f449b Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Tue, 23 Jun 2026 17:45:40 +0700 Subject: [PATCH 001/215] Rename `negated` to `is_negated_pat`. This better matches how the argument is actually used. See `rustc_hir::intravisit::{walk_expr,walk_pat_expr}`. --- compiler/rustc_hir/src/intravisit.rs | 2 +- compiler/rustc_lint/src/late.rs | 4 ++-- compiler/rustc_lint/src/passes.rs | 2 +- compiler/rustc_lint/src/types.rs | 12 +++++++++--- compiler/rustc_lint/src/types/literal.rs | 4 ++-- src/tools/clippy/clippy_lints/src/approx_const.rs | 2 +- 6 files changed, 16 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs index 9e0eaef596420..451dd7e1dda76 100644 --- a/compiler/rustc_hir/src/intravisit.rs +++ b/compiler/rustc_hir/src/intravisit.rs @@ -342,7 +342,7 @@ pub trait Visitor<'v>: Sized { fn visit_pat_expr(&mut self, expr: &'v PatExpr<'v>) -> Self::Result { walk_pat_expr(self, expr) } - fn visit_lit(&mut self, _hir_id: HirId, _lit: Lit, _negated: bool) -> Self::Result { + fn visit_lit(&mut self, _hir_id: HirId, _lit: Lit, _is_negated_pat: bool) -> Self::Result { Self::Result::output() } fn visit_anon_const(&mut self, c: &'v AnonConst) -> Self::Result { diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 1f37ac2d3e0bc..0be242ffa9336 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -151,8 +151,8 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas hir_visit::walk_pat(self, p); } - fn visit_lit(&mut self, hir_id: HirId, lit: hir::Lit, negated: bool) { - lint_callback!(self, check_lit, hir_id, lit, negated); + fn visit_lit(&mut self, hir_id: HirId, lit: hir::Lit, is_negated_pat: bool) { + lint_callback!(self, check_lit, hir_id, lit, is_negated_pat); } fn visit_expr_field(&mut self, field: &'tcx hir::ExprField<'tcx>) { diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 5c101048b8010..cd9e08bf9cb53 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -21,7 +21,7 @@ macro_rules! late_lint_methods { fn check_stmt(a: &'tcx rustc_hir::Stmt<'tcx>); fn check_arm(a: &'tcx rustc_hir::Arm<'tcx>); fn check_pat(a: &'tcx rustc_hir::Pat<'tcx>); - fn check_lit(hir_id: rustc_hir::HirId, a: rustc_hir::Lit, negated: bool); + fn check_lit(hir_id: rustc_hir::HirId, a: rustc_hir::Lit, is_negated_pat: bool); fn check_expr(a: &'tcx rustc_hir::Expr<'tcx>); fn check_expr_post(a: &'tcx rustc_hir::Expr<'tcx>); fn check_ty(a: &'tcx rustc_hir::Ty<'tcx, rustc_hir::AmbigArg>); diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index a12e347284f7c..e0f4432466fce 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -539,12 +539,18 @@ fn lint_fn_pointer<'tcx>( } impl<'tcx> LateLintPass<'tcx> for TypeLimits { - fn check_lit(&mut self, cx: &LateContext<'tcx>, hir_id: HirId, lit: hir::Lit, negated: bool) { - if negated { + fn check_lit( + &mut self, + cx: &LateContext<'tcx>, + hir_id: HirId, + lit: hir::Lit, + is_negated_pat: bool, + ) { + if is_negated_pat { self.negated_expr_id = Some(hir_id); self.negated_expr_span = Some(lit.span); } - lint_literal(cx, self, hir_id, lit.span, &lit, negated); + lint_literal(cx, self, hir_id, lit.span, &lit, is_negated_pat); } fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) { diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index 45d79fec75ae9..78895b839494c 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -404,7 +404,7 @@ pub(crate) fn lint_literal<'tcx>( hir_id: HirId, span: Span, lit: &hir::Lit, - negated: bool, + is_negated_pat: bool, ) { match *cx.typeck_results().node_type(hir_id).kind() { ty::Int(t) => { @@ -416,7 +416,7 @@ pub(crate) fn lint_literal<'tcx>( }; } ty::Uint(t) => { - assert!(!negated); + assert!(!is_negated_pat); lint_uint_literal(cx, hir_id, span, lit, t) } ty::Float(t) => { diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs index 2ea921e5d4614..b7b9befd10b0b 100644 --- a/src/tools/clippy/clippy_lints/src/approx_const.rs +++ b/src/tools/clippy/clippy_lints/src/approx_const.rs @@ -75,7 +75,7 @@ impl ApproxConstant { } impl LateLintPass<'_> for ApproxConstant { - fn check_lit(&mut self, cx: &LateContext<'_>, _hir_id: HirId, lit: Lit, _negated: bool) { + fn check_lit(&mut self, cx: &LateContext<'_>, _hir_id: HirId, lit: Lit, _is_negated_pat: bool) { match lit.node { LitKind::Float(s, LitFloatType::Suffixed(fty)) => match fty { FloatTy::F16 => self.check_known_consts(cx, lit.span, s, "f16"), From 2f44f6c6d038f818c6e8e874227b7639ae4f11ba Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 1 May 2026 16:21:52 +0200 Subject: [PATCH 002/215] Fix trait method resolution on an adjusted never type --- compiler/rustc_hir_typeck/src/method/probe.rs | 30 ++++++++++++++----- tests/ui/never_type/basic/clone-never.rs | 5 ++-- tests/ui/never_type/basic/clone-never.stderr | 16 ---------- tests/ui/never_type/basic/method-on-never.rs | 20 +++++++++++++ 4 files changed, 45 insertions(+), 26 deletions(-) create mode 100644 tests/ui/never_type/basic/method-on-never.rs diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 4258896deec70..544556d29ac20 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -459,6 +459,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we encountered an `_` type or an error type during autoderef, this is // ambiguous. if let Some(bad_ty) = &steps.opt_bad_ty { + // Ended up encountering a type variable when doing autoderef, + // but it may not be a type variable after processing obligations + // in our local `FnCtxt`, so don't call `structurally_resolve_type`. + let ty = &bad_ty.ty; + let ty = self + .probe_instantiate_query_response(span, &orig_values, ty) + .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); + let ty = ty.value; + if is_suggestion.0 { // Ambiguity was encountered during a suggestion. There's really // not much use in suggesting methods in this case. @@ -482,15 +491,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, MissingTypeAnnot, ); + // If `ty` is an inference variable that was created by being adjusted from the never type, + // We demand the type to be equal to the never type, so we can probe the never type for methods + // (see https://github.com/rust-lang/rust/issues/143349) + } else if let ty::Infer(ty::TyVar(ty_id)) = *ty.kind() + && let ty_id = self.root_var(ty_id) + && let root_ty = Ty::new_var(self.tcx, ty_id) + && self + .diverging_type_vars + .borrow() + .iter() + .any(|&candidate_id| self.root_var(candidate_id) == ty_id) + { + self.demand_eqtype(span, root_ty, self.tcx.types.never); } else { - // Ended up encountering a type variable when doing autoderef, - // but it may not be a type variable after processing obligations - // in our local `FnCtxt`, so don't call `structurally_resolve_type`. - let ty = &bad_ty.ty; - let ty = self - .probe_instantiate_query_response(span, &orig_values, ty) - .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); - let ty = self.resolve_vars_if_possible(ty.value); let guar = match *ty.kind() { _ if let Some(guar) = self.tainted_by_errors() => guar, ty::Infer(ty::TyVar(_)) => { diff --git a/tests/ui/never_type/basic/clone-never.rs b/tests/ui/never_type/basic/clone-never.rs index 76ee3c5f5d566..79fe72d2e36d0 100644 --- a/tests/ui/never_type/basic/clone-never.rs +++ b/tests/ui/never_type/basic/clone-never.rs @@ -1,6 +1,7 @@ -//! regression test for issue #2151 +//@ check-pass +// Regression test for https://github.com/rust-lang/rust/issues/143349 fn main() { - let x = panic!(); //~ ERROR type annotations needed + let x = panic!(); x.clone(); } diff --git a/tests/ui/never_type/basic/clone-never.stderr b/tests/ui/never_type/basic/clone-never.stderr index ea2f55f17381e..e69de29bb2d1d 100644 --- a/tests/ui/never_type/basic/clone-never.stderr +++ b/tests/ui/never_type/basic/clone-never.stderr @@ -1,16 +0,0 @@ -error[E0282]: type annotations needed - --> $DIR/clone-never.rs:4:9 - | -LL | let x = panic!(); - | ^ -LL | x.clone(); - | - type must be known at this point - | -help: consider giving `x` an explicit type - | -LL | let x: /* Type */ = panic!(); - | ++++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/never_type/basic/method-on-never.rs b/tests/ui/never_type/basic/method-on-never.rs new file mode 100644 index 0000000000000..893d5cf911395 --- /dev/null +++ b/tests/ui/never_type/basic/method-on-never.rs @@ -0,0 +1,20 @@ +//@ check-pass +// Regression test for https://github.com/rust-lang/rust/issues/143349 + +#![feature(never_type)] + +trait Trait { + fn method(&self); +} +impl Trait for ! { + fn method(&self) { + todo!() + } +} + +fn main() { + let x = loop {}; + x.method(); + + { loop {} }.method(); +} From ba62168b3f6f91c2cc1ea196e02435ab9860123d Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 3 May 2026 12:53:47 +0200 Subject: [PATCH 003/215] Add FCW lint `trait_method_on_coerced_never_type` --- compiler/rustc_hir_typeck/src/method/probe.rs | 12 +++++ compiler/rustc_lint_defs/src/builtin.rs | 33 +++++++++++++ tests/ui/never_type/basic/clone-never.rs | 2 + tests/ui/never_type/basic/clone-never.stderr | 25 ++++++++++ tests/ui/never_type/basic/method-on-never.rs | 4 ++ .../never_type/basic/method-on-never.stderr | 47 +++++++++++++++++++ 6 files changed, 123 insertions(+) create mode 100644 tests/ui/never_type/basic/method-on-never.stderr diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 544556d29ac20..c5c20d7737275 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -12,6 +12,7 @@ use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt}; use rustc_infer::traits::{ObligationCauseCode, PredicateObligation, query}; +use rustc_lint::builtin::TRAIT_METHOD_ON_COERCED_NEVER_TYPE; use rustc_macros::Diagnostic; use rustc_middle::middle::stability; use rustc_middle::ty::elaborate::supertrait_def_ids; @@ -394,6 +395,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { #[diag("type annotations needed")] struct MissingTypeAnnot; + #[derive(Diagnostic)] + #[diag("trait method call on a coerced never type")] + #[help("consider providing a type annotation")] + struct TraitMethodOnCoercedNeverType; + let mut orig_values = OriginalQueryValues::default(); let predefined_opaques_in_body = if self.next_trait_solver() { self.tcx.mk_predefined_opaques_in_body_from_iter( @@ -503,6 +509,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .iter() .any(|&candidate_id| self.root_var(candidate_id) == ty_id) { + self.tcx.emit_node_span_lint( + TRAIT_METHOD_ON_COERCED_NEVER_TYPE, + scope_expr_id, + span, + TraitMethodOnCoercedNeverType, + ); self.demand_eqtype(span, root_ty, self.tcx.types.never); } else { let guar = match *ty.kind() { diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index d38b1cf47bd6f..9ca9273cc9ff8 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -112,6 +112,7 @@ pub mod hardwired { TEST_UNSTABLE_LINT, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, TEXT_DIRECTION_CODEPOINT_IN_LITERAL, + TRAIT_METHOD_ON_COERCED_NEVER_TYPE, TRIVIAL_CASTS, TRIVIAL_NUMERIC_CASTS, TYVAR_BEHIND_RAW_POINTER, @@ -5578,3 +5579,35 @@ declare_lint! { "usage of `unsafe` code and other potentially unsound constructs", @eval_always = true } + +declare_lint! { + /// The `trait_method_on_coerced_never_type` lint detects situations in which a never type, which + /// was coerced to any, has a trait method on it. + /// + /// ### Example + /// + /// ```rust,no_run + /// fn main() { + /// let x = panic!(); + /// x.clone(); + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Calling trait methods on a coerced `!` was previously disallowed for the never type, + /// but it did work for empty enums such as `Infallible` since these don't coerce. + /// This means that changing the definition of `Infallible` to become a type alias to `!` (a long-term goal), + /// would break code that called a trait method on `Infallible`, in such a way that the `!` would coerce. + /// + /// Therefore, to aid in the transition of changing `Infallible` to a type alias, this is temporarily allowed with a FCW. + pub TRAIT_METHOD_ON_COERCED_NEVER_TYPE, + Warn, + "detects trait method calls on an coerced never type", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #156047), + report_in_deps: true, + }; +} diff --git a/tests/ui/never_type/basic/clone-never.rs b/tests/ui/never_type/basic/clone-never.rs index 79fe72d2e36d0..3466a4c78c026 100644 --- a/tests/ui/never_type/basic/clone-never.rs +++ b/tests/ui/never_type/basic/clone-never.rs @@ -4,4 +4,6 @@ fn main() { let x = panic!(); x.clone(); + //~^ WARN [trait_method_on_coerced_never_type] + //~| WARN previously accepted } diff --git a/tests/ui/never_type/basic/clone-never.stderr b/tests/ui/never_type/basic/clone-never.stderr index e69de29bb2d1d..ed726bd48f11d 100644 --- a/tests/ui/never_type/basic/clone-never.stderr +++ b/tests/ui/never_type/basic/clone-never.stderr @@ -0,0 +1,25 @@ +warning: trait method call on a coerced never type + --> $DIR/clone-never.rs:6:7 + | +LL | x.clone(); + | ^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + +warning: 1 warning emitted + +Future incompatibility report: Future breakage diagnostic: +warning: trait method call on a coerced never type + --> $DIR/clone-never.rs:6:7 + | +LL | x.clone(); + | ^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + diff --git a/tests/ui/never_type/basic/method-on-never.rs b/tests/ui/never_type/basic/method-on-never.rs index 893d5cf911395..22198bc202fd6 100644 --- a/tests/ui/never_type/basic/method-on-never.rs +++ b/tests/ui/never_type/basic/method-on-never.rs @@ -15,6 +15,10 @@ impl Trait for ! { fn main() { let x = loop {}; x.method(); + //~^ WARN [trait_method_on_coerced_never_type] + //~| WARN previously accepted { loop {} }.method(); + //~^ WARN [trait_method_on_coerced_never_type] + //~| WARN previously accepted } diff --git a/tests/ui/never_type/basic/method-on-never.stderr b/tests/ui/never_type/basic/method-on-never.stderr new file mode 100644 index 0000000000000..1fa7d160e3b79 --- /dev/null +++ b/tests/ui/never_type/basic/method-on-never.stderr @@ -0,0 +1,47 @@ +warning: trait method call on a coerced never type + --> $DIR/method-on-never.rs:17:7 + | +LL | x.method(); + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + +warning: trait method call on a coerced never type + --> $DIR/method-on-never.rs:21:17 + | +LL | { loop {} }.method(); + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + +warning: 2 warnings emitted + +Future incompatibility report: Future breakage diagnostic: +warning: trait method call on a coerced never type + --> $DIR/method-on-never.rs:17:7 + | +LL | x.method(); + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + +Future breakage diagnostic: +warning: trait method call on a coerced never type + --> $DIR/method-on-never.rs:21:17 + | +LL | { loop {} }.method(); + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + From e234c7ff5f0c68b99f4186da41bf40e8251e96e2 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 19 Jun 2026 12:14:26 +0200 Subject: [PATCH 004/215] Use `sub_unification_table_root_var` instead of `root_var` to check that the ty vars are equal --- compiler/rustc_hir_typeck/src/method/probe.rs | 4 +- .../rustc_infer/src/infer/type_variable.rs | 2 +- .../question-mark-type-inference-in-chain.rs | 17 ++++- ...estion-mark-type-inference-in-chain.stderr | 73 ++++++++++++++---- tests/ui/never_type/basic/method-on-never.rs | 44 ++++++++++- .../never_type/basic/method-on-never.stderr | 76 +++++++++++++++++-- 6 files changed, 189 insertions(+), 27 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index c5c20d7737275..f09f882168caa 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -501,13 +501,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We demand the type to be equal to the never type, so we can probe the never type for methods // (see https://github.com/rust-lang/rust/issues/143349) } else if let ty::Infer(ty::TyVar(ty_id)) = *ty.kind() - && let ty_id = self.root_var(ty_id) + && let ty_id = self.sub_unification_table_root_var(ty_id) && let root_ty = Ty::new_var(self.tcx, ty_id) && self .diverging_type_vars .borrow() .iter() - .any(|&candidate_id| self.root_var(candidate_id) == ty_id) + .any(|&candidate_id| self.sub_unification_table_root_var(candidate_id) == ty_id) { self.tcx.emit_node_span_lint( TRAIT_METHOD_ON_COERCED_NEVER_TYPE, diff --git a/compiler/rustc_infer/src/infer/type_variable.rs b/compiler/rustc_infer/src/infer/type_variable.rs index 4c56bf0923c39..4411e03014123 100644 --- a/compiler/rustc_infer/src/infer/type_variable.rs +++ b/compiler/rustc_infer/src/infer/type_variable.rs @@ -246,7 +246,7 @@ impl<'tcx> TypeVariableTable<'_, 'tcx> { } /// Returns the "root" variable of `vid` in the `sub_unification_table` - /// equivalence table. All type variables that have been are related via + /// equivalence table. All type variables that have been related via /// equality or subtyping will yield the same root variable (per the /// union-find algorithm), so `sub_unification_table_root_var(a) /// == sub_unification_table_root_var(b)` implies that: diff --git a/tests/ui/inference/question-mark-type-inference-in-chain.rs b/tests/ui/inference/question-mark-type-inference-in-chain.rs index 90adeb0236eb2..e3a613cc21295 100644 --- a/tests/ui/inference/question-mark-type-inference-in-chain.rs +++ b/tests/ui/inference/question-mark-type-inference-in-chain.rs @@ -31,10 +31,15 @@ fn parse(_s: &str) -> std::result::Result { pub fn error1(lines: &[&str]) -> Result> { let mut tags = lines.iter().map(|e| parse(e)).collect()?; - //~^ ERROR: type annotations needed - //~| HELP: consider giving `tags` an explicit type - tags.sort(); //~ NOTE: type must be known at this point + tags.sort(); + //~^ WARN trait method call on a coerced never type + //~| WARN previously accepted + //~| NOTE for more information, see issue + //~| NOTE `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + //~| ERROR no method named `sort` found for type `!` in the current scope [E0599] + //~| HELP consider providing a type annotation + //~| NOTE method not found in `!` Ok(tags) } @@ -59,6 +64,12 @@ pub fn error3(lines: &[&str]) -> Result> { //~| NOTE: in this expansion of desugaring of operator `?` //~| NOTE: in this expansion of desugaring of operator `?` tags.sort(); + //~^ WARN trait method call on a coerced never type + //~| WARN previously accepted + //~| NOTE for more information, see issue + //~| ERROR no method named `sort` found for type `!` in the current scope [E0599] + //~| HELP consider providing a type annotation + //~| NOTE method not found in `!` Ok(tags) } diff --git a/tests/ui/inference/question-mark-type-inference-in-chain.stderr b/tests/ui/inference/question-mark-type-inference-in-chain.stderr index 2e1e9346d4e65..21ff6df87f9de 100644 --- a/tests/ui/inference/question-mark-type-inference-in-chain.stderr +++ b/tests/ui/inference/question-mark-type-inference-in-chain.stderr @@ -1,19 +1,22 @@ -error[E0282]: type annotations needed - --> $DIR/question-mark-type-inference-in-chain.rs:33:9 +warning: trait method call on a coerced never type + --> $DIR/question-mark-type-inference-in-chain.rs:35:10 | -LL | let mut tags = lines.iter().map(|e| parse(e)).collect()?; - | ^^^^^^^^ -... LL | tags.sort(); - | ---- type must be known at this point + | ^^^^ | -help: consider giving `tags` an explicit type + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + +error[E0599]: no method named `sort` found for type `!` in the current scope + --> $DIR/question-mark-type-inference-in-chain.rs:35:10 | -LL | let mut tags: Vec<_> = lines.iter().map(|e| parse(e)).collect()?; - | ++++++++ +LL | tags.sort(); + | ^^^^ method not found in `!` error[E0283]: type annotations needed - --> $DIR/question-mark-type-inference-in-chain.rs:43:65 + --> $DIR/question-mark-type-inference-in-chain.rs:48:65 | LL | let mut tags: Vec = lines.iter().map(|e| parse(e)).collect()?; | ^^^^^^^ cannot infer type of the type parameter `B` declared on the method `collect` @@ -27,15 +30,31 @@ LL | let mut tags: Vec = lines.iter().map(|e| parse(e)).collect:: $DIR/question-mark-type-inference-in-chain.rs:55:20 + --> $DIR/question-mark-type-inference-in-chain.rs:60:20 | LL | let mut tags = lines.iter().map(|e| parse(e)).collect::>()?; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the `?` operator cannot be applied to type `Vec>` | = help: the nightly-only, unstable trait `Try` is not implemented for `Vec>` +warning: trait method call on a coerced never type + --> $DIR/question-mark-type-inference-in-chain.rs:66:10 + | +LL | tags.sort(); + | ^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + +error[E0599]: no method named `sort` found for type `!` in the current scope + --> $DIR/question-mark-type-inference-in-chain.rs:66:10 + | +LL | tags.sort(); + | ^^^^ method not found in `!` + error[E0277]: a value of type `std::result::Result, AnotherError>` cannot be built from an iterator over elements of type `std::result::Result` - --> $DIR/question-mark-type-inference-in-chain.rs:74:20 + --> $DIR/question-mark-type-inference-in-chain.rs:85:20 | LL | .collect::>>()?; | ------- ^^^^^^^^^^^^^^^^^^^^ value of type `std::result::Result, AnotherError>` cannot be built from `std::iter::Iterator>` @@ -47,7 +66,7 @@ help: the trait `FromIterator>` is not implemented --> $SRC_DIR/core/src/result.rs:LL:COL = help: for that trait implementation, expected `AnotherError`, found `Error` note: the method call chain might not have had the expected associated types - --> $DIR/question-mark-type-inference-in-chain.rs:71:10 + --> $DIR/question-mark-type-inference-in-chain.rs:82:10 | LL | let mut tags = lines | ----- this expression has type `&[&str]` @@ -60,7 +79,31 @@ LL | .map(|e| parse(e)) note: required by a bound in `collect` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL -error: aborting due to 4 previous errors +error: aborting due to 5 previous errors; 2 warnings emitted -Some errors have detailed explanations: E0277, E0282, E0283. +Some errors have detailed explanations: E0277, E0283, E0599. For more information about an error, try `rustc --explain E0277`. +Future incompatibility report: Future breakage diagnostic: +warning: trait method call on a coerced never type + --> $DIR/question-mark-type-inference-in-chain.rs:35:10 + | +LL | tags.sort(); + | ^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + +Future breakage diagnostic: +warning: trait method call on a coerced never type + --> $DIR/question-mark-type-inference-in-chain.rs:66:10 + | +LL | tags.sort(); + | ^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + diff --git a/tests/ui/never_type/basic/method-on-never.rs b/tests/ui/never_type/basic/method-on-never.rs index 22198bc202fd6..9e430ed678512 100644 --- a/tests/ui/never_type/basic/method-on-never.rs +++ b/tests/ui/never_type/basic/method-on-never.rs @@ -12,7 +12,32 @@ impl Trait for ! { } } -fn main() { +struct Adhoc; +struct Error; + +#[doc(hidden)] +trait AdhocKind: Sized { + #[inline] + fn anyhow_kind(&self) -> Adhoc { + Adhoc + } +} + +impl AdhocKind for &T where T: ?Sized + Send + Sync + 'static {} + +impl Adhoc { + #[cold] + fn new(self, message: M) -> Error + where + M: Send + Sync + 'static, + { + Error + } +} + +fn temp() -> Result { todo!() } + +fn main() -> Result<(), ()> { let x = loop {}; x.method(); //~^ WARN [trait_method_on_coerced_never_type] @@ -21,4 +46,21 @@ fn main() { { loop {} }.method(); //~^ WARN [trait_method_on_coerced_never_type] //~| WARN previously accepted + + let e = match loop {} { + y => y.method(), + //~^ WARN [trait_method_on_coerced_never_type] + //~| WARN previously accepted + }; + + let error = match loop {} { + error => (&error).anyhow_kind().new(error), + //~^ WARN [trait_method_on_coerced_never_type] + //~| WARN previously accepted + }; + + let res = temp()?; + res.method(); + //~^ WARN [trait_method_on_coerced_never_type] + //~| WARN previously accepted } diff --git a/tests/ui/never_type/basic/method-on-never.stderr b/tests/ui/never_type/basic/method-on-never.stderr index 1fa7d160e3b79..977439bed38e9 100644 --- a/tests/ui/never_type/basic/method-on-never.stderr +++ b/tests/ui/never_type/basic/method-on-never.stderr @@ -1,5 +1,5 @@ warning: trait method call on a coerced never type - --> $DIR/method-on-never.rs:17:7 + --> $DIR/method-on-never.rs:42:7 | LL | x.method(); | ^^^^^^ @@ -10,7 +10,7 @@ LL | x.method(); = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default warning: trait method call on a coerced never type - --> $DIR/method-on-never.rs:21:17 + --> $DIR/method-on-never.rs:46:17 | LL | { loop {} }.method(); | ^^^^^^ @@ -19,11 +19,41 @@ LL | { loop {} }.method(); = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 -warning: 2 warnings emitted +warning: trait method call on a coerced never type + --> $DIR/method-on-never.rs:51:16 + | +LL | y => y.method(), + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + +warning: trait method call on a coerced never type + --> $DIR/method-on-never.rs:57:27 + | +LL | error => (&error).anyhow_kind().new(error), + | ^^^^^^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + +warning: trait method call on a coerced never type + --> $DIR/method-on-never.rs:63:9 + | +LL | res.method(); + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + +warning: 5 warnings emitted Future incompatibility report: Future breakage diagnostic: warning: trait method call on a coerced never type - --> $DIR/method-on-never.rs:17:7 + --> $DIR/method-on-never.rs:42:7 | LL | x.method(); | ^^^^^^ @@ -35,7 +65,7 @@ LL | x.method(); Future breakage diagnostic: warning: trait method call on a coerced never type - --> $DIR/method-on-never.rs:21:17 + --> $DIR/method-on-never.rs:46:17 | LL | { loop {} }.method(); | ^^^^^^ @@ -45,3 +75,39 @@ LL | { loop {} }.method(); = note: for more information, see issue #156047 = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default +Future breakage diagnostic: +warning: trait method call on a coerced never type + --> $DIR/method-on-never.rs:51:16 + | +LL | y => y.method(), + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + +Future breakage diagnostic: +warning: trait method call on a coerced never type + --> $DIR/method-on-never.rs:57:27 + | +LL | error => (&error).anyhow_kind().new(error), + | ^^^^^^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + +Future breakage diagnostic: +warning: trait method call on a coerced never type + --> $DIR/method-on-never.rs:63:9 + | +LL | res.method(); + | ^^^^^^ + | + = help: consider providing a type annotation + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #156047 + = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + From dcac71550a723ba6c035f6b09c98878a5d6a112b Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Tue, 23 Jun 2026 18:27:33 +0700 Subject: [PATCH 005/215] Refactor negation handling in `TypeLimits` lints. --- compiler/rustc_lint/src/types.rs | 39 ++++++++++++++++-------- compiler/rustc_lint/src/types/literal.rs | 15 +++++---- 2 files changed, 33 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index e0f4432466fce..52e89487d4832 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -194,10 +194,15 @@ declare_lint! { #[derive(Copy, Clone, Default)] pub(crate) struct TypeLimits { - /// Id of the last visited negated expression - negated_expr_id: Option, - /// Span of the last visited negated expression - negated_expr_span: Option, + last_visited_negation: Option, +} + +#[derive(Copy, Clone)] +struct NegationInfo { + /// A negation expression (a `rustc_hir::ExprKind::Unary`) + negation_span: Span, + /// The operand of the negation expression. + negated_id: hir::HirId, } impl_lint_pass!(TypeLimits => [ @@ -210,7 +215,7 @@ impl_lint_pass!(TypeLimits => [ impl TypeLimits { pub(crate) fn new() -> TypeLimits { - TypeLimits { negated_expr_id: None, negated_expr_span: None } + TypeLimits { last_visited_negation: None } } } @@ -546,20 +551,28 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { lit: hir::Lit, is_negated_pat: bool, ) { - if is_negated_pat { - self.negated_expr_id = Some(hir_id); - self.negated_expr_span = Some(lit.span); - } - lint_literal(cx, self, hir_id, lit.span, &lit, is_negated_pat); + let surrounding_negation = if is_negated_pat { + // In this case, lit.span refers to a `rustc_hir::hir::PatExprKind::Lit`, + // which includes the minus sign in front. + Some(lit.span) + } else if let Some(negation_info) = self.last_visited_negation + && negation_info.negated_id == hir_id + { + Some(negation_info.negation_span) + } else { + None + }; + lint_literal(cx, hir_id, lit.span, &lit, surrounding_negation); } fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) { match e.kind { hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { // Propagate negation, if the negation itself isn't negated - if self.negated_expr_id != Some(e.hir_id) { - self.negated_expr_id = Some(expr.hir_id); - self.negated_expr_span = Some(e.span); + if self.last_visited_negation.is_none_or(|negation| negation.negated_id != e.hir_id) + { + self.last_visited_negation = + Some(NegationInfo { negation_span: e.span, negated_id: expr.hir_id }); } } hir::ExprKind::Binary(binop, ref l, ref r) => { diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index 78895b839494c..bed26ee6f3d25 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -17,7 +17,7 @@ use crate::lints::{ OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt, RangeEndpointOutOfRange, SurrogateCharCast, TooLargeCharCast, UseInclusiveRange, }; -use crate::types::{OVERFLOWING_LITERALS, TypeLimits}; +use crate::types::OVERFLOWING_LITERALS; /// Attempts to special-case the overflowing literal lint when it occurs as a range endpoint (`expr..MAX+1`). /// Returns `true` iff the lint was emitted. @@ -260,17 +260,17 @@ fn literal_to_i128(val: u128, negative: bool) -> Option { fn lint_int_literal<'tcx>( cx: &LateContext<'tcx>, - type_limits: &TypeLimits, hir_id: HirId, span: Span, lit: &hir::Lit, t: ty::IntTy, v: u128, + surrounding_negation: Option, ) { let int_type = t.normalize(cx.sess().target.pointer_width); let (min, max) = int_ty_range(int_type); let max = max as u128; - let negative = type_limits.negated_expr_id == Some(hir_id); + let negative = surrounding_negation.is_some(); // Detect literal value out of range [min, max] inclusive // avoiding use of -min to prevent overflow/panic @@ -294,7 +294,7 @@ fn lint_int_literal<'tcx>( return; } - let span = if negative { type_limits.negated_expr_span.unwrap() } else { span }; + let span = surrounding_negation.unwrap_or(span); let lit = cx .sess() .source_map() @@ -400,23 +400,22 @@ fn float_is_infinite(v: Symbol) -> Option { pub(crate) fn lint_literal<'tcx>( cx: &LateContext<'tcx>, - type_limits: &TypeLimits, hir_id: HirId, span: Span, lit: &hir::Lit, - is_negated_pat: bool, + surrounding_negation: Option, ) { match *cx.typeck_results().node_type(hir_id).kind() { ty::Int(t) => { match lit.node { ast::LitKind::Int(v, ast::LitIntType::Signed(_) | ast::LitIntType::Unsuffixed) => { - lint_int_literal(cx, type_limits, hir_id, span, lit, t, v.get()) + lint_int_literal(cx, hir_id, span, lit, t, v.get(), surrounding_negation) } _ => bug!(), }; } ty::Uint(t) => { - assert!(!is_negated_pat); + assert!(surrounding_negation.is_none()); lint_uint_literal(cx, hir_id, span, lit, t) } ty::Float(t) => { From dd1bdbb8b547eb88d0b16bcb3eabb75dcc64ff32 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Tue, 23 Jun 2026 18:52:26 +0700 Subject: [PATCH 006/215] Add literal overflow lint test with repeated negations. --- tests/ui/lint/lint-type-overflow2.rs | 3 - tests/ui/lint/lint-type-overflow2.stderr | 49 ++------ tests/ui/lint/lint-type-overflow3.rs | 24 ++++ tests/ui/lint/lint-type-overflow3.stderr | 145 +++++++++++++++++++++++ 4 files changed, 182 insertions(+), 39 deletions(-) create mode 100644 tests/ui/lint/lint-type-overflow3.rs create mode 100644 tests/ui/lint/lint-type-overflow3.stderr diff --git a/tests/ui/lint/lint-type-overflow2.rs b/tests/ui/lint/lint-type-overflow2.rs index d3ff02aeb722f..622208b33cb08 100644 --- a/tests/ui/lint/lint-type-overflow2.rs +++ b/tests/ui/lint/lint-type-overflow2.rs @@ -5,9 +5,6 @@ #![deny(overflowing_literals)] fn main() { - let x2: i8 = --128; //~ ERROR literal out of range for `i8` - //~| WARN use of a double negation - let x = -65520.0_f16; //~ ERROR literal out of range for `f16` let x = 65520.0_f16; //~ ERROR literal out of range for `f16` let x = -3.40282357e+38_f32; //~ ERROR literal out of range for `f32` diff --git a/tests/ui/lint/lint-type-overflow2.stderr b/tests/ui/lint/lint-type-overflow2.stderr index c045d243753e4..f8c0c6d4ef3a1 100644 --- a/tests/ui/lint/lint-type-overflow2.stderr +++ b/tests/ui/lint/lint-type-overflow2.stderr @@ -1,25 +1,10 @@ -warning: use of a double negation - --> $DIR/lint-type-overflow2.rs:8:18 - | -LL | let x2: i8 = --128; - | ^^^^^ - | - = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages - = note: use `-= 1` if you meant to decrement the value - = note: `#[warn(double_negations)]` on by default -help: add parentheses for clarity - | -LL | let x2: i8 = -(-128); - | + + - -error: literal out of range for `i8` - --> $DIR/lint-type-overflow2.rs:8:20 +error: literal out of range for `f16` + --> $DIR/lint-type-overflow2.rs:8:14 | -LL | let x2: i8 = --128; - | ^^^ +LL | let x = -65520.0_f16; + | ^^^^^^^^^^^ | - = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127` - = help: consider using the type `u8` instead + = note: the literal `65520.0_f16` does not fit into the type `f16` and will be converted to `f16::INFINITY` note: the lint level is defined here --> $DIR/lint-type-overflow2.rs:5:9 | @@ -27,15 +12,7 @@ LL | #![deny(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ error: literal out of range for `f16` - --> $DIR/lint-type-overflow2.rs:11:14 - | -LL | let x = -65520.0_f16; - | ^^^^^^^^^^^ - | - = note: the literal `65520.0_f16` does not fit into the type `f16` and will be converted to `f16::INFINITY` - -error: literal out of range for `f16` - --> $DIR/lint-type-overflow2.rs:12:14 + --> $DIR/lint-type-overflow2.rs:9:14 | LL | let x = 65520.0_f16; | ^^^^^^^^^^^ @@ -43,7 +20,7 @@ LL | let x = 65520.0_f16; = note: the literal `65520.0_f16` does not fit into the type `f16` and will be converted to `f16::INFINITY` error: literal out of range for `f32` - --> $DIR/lint-type-overflow2.rs:13:14 + --> $DIR/lint-type-overflow2.rs:10:14 | LL | let x = -3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ @@ -51,7 +28,7 @@ LL | let x = -3.40282357e+38_f32; = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY` error: literal out of range for `f32` - --> $DIR/lint-type-overflow2.rs:14:14 + --> $DIR/lint-type-overflow2.rs:11:14 | LL | let x = 3.40282357e+38_f32; | ^^^^^^^^^^^^^^^^^^ @@ -59,7 +36,7 @@ LL | let x = 3.40282357e+38_f32; = note: the literal `3.40282357e+38_f32` does not fit into the type `f32` and will be converted to `f32::INFINITY` error: literal out of range for `f64` - --> $DIR/lint-type-overflow2.rs:15:14 + --> $DIR/lint-type-overflow2.rs:12:14 | LL | let x = -1.7976931348623159e+308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -67,7 +44,7 @@ LL | let x = -1.7976931348623159e+308_f64; = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY` error: literal out of range for `f64` - --> $DIR/lint-type-overflow2.rs:16:14 + --> $DIR/lint-type-overflow2.rs:13:14 | LL | let x = 1.7976931348623159e+308_f64; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -75,7 +52,7 @@ LL | let x = 1.7976931348623159e+308_f64; = note: the literal `1.7976931348623159e+308_f64` does not fit into the type `f64` and will be converted to `f64::INFINITY` error: literal out of range for `f128` - --> $DIR/lint-type-overflow2.rs:17:14 + --> $DIR/lint-type-overflow2.rs:14:14 | LL | let x = -1.1897314953572317650857593266280075e+4932_f128; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -83,12 +60,12 @@ LL | let x = -1.1897314953572317650857593266280075e+4932_f128; = note: the literal `1.1897314953572317650857593266280075e+4932_f128` does not fit into the type `f128` and will be converted to `f128::INFINITY` error: literal out of range for `f128` - --> $DIR/lint-type-overflow2.rs:18:14 + --> $DIR/lint-type-overflow2.rs:15:14 | LL | let x = 1.1897314953572317650857593266280075e+4932_f128; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the literal `1.1897314953572317650857593266280075e+4932_f128` does not fit into the type `f128` and will be converted to `f128::INFINITY` -error: aborting due to 9 previous errors; 1 warning emitted +error: aborting due to 8 previous errors diff --git a/tests/ui/lint/lint-type-overflow3.rs b/tests/ui/lint/lint-type-overflow3.rs new file mode 100644 index 0000000000000..5166ff64b59a3 --- /dev/null +++ b/tests/ui/lint/lint-type-overflow3.rs @@ -0,0 +1,24 @@ +//@ compile-flags: --emit link +// The above flags (which are passed in `cargo build`) are required for +// the `arithmetic_overflow` lint to function. + +#![deny(overflowing_literals, arithmetic_overflow)] + +fn main() { + let x: i8 = 128; + //~^ ERROR literal out of range for `i8` + let x: i8 = -128; + let x: i8 = --128; //~ WARN use of a double negation + //~^ ERROR literal out of range for `i8` + //~| ERROR this arithmetic operation will overflow + let x: i8 = ---128; //~ WARN use of a double negation + //~^ ERROR this arithmetic operation will overflow + let x: i8 = ----128; //~ WARN use of a double negation + //~^ ERROR literal out of range for `i8` + //~| ERROR this arithmetic operation will overflow + let x: i8 = -----128; //~ WARN use of a double negation + //~^ ERROR this arithmetic operation will overflow + let x: i8 = ------128; //~ WARN use of a double negation + //~^ ERROR literal out of range for `i8` + //~| ERROR this arithmetic operation will overflow +} diff --git a/tests/ui/lint/lint-type-overflow3.stderr b/tests/ui/lint/lint-type-overflow3.stderr new file mode 100644 index 0000000000000..211a6853ab17b --- /dev/null +++ b/tests/ui/lint/lint-type-overflow3.stderr @@ -0,0 +1,145 @@ +warning: use of a double negation + --> $DIR/lint-type-overflow3.rs:11:17 + | +LL | let x: i8 = --128; + | ^^^^^ + | + = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages + = note: use `-= 1` if you meant to decrement the value + = note: `#[warn(double_negations)]` on by default +help: add parentheses for clarity + | +LL | let x: i8 = -(-128); + | + + + +warning: use of a double negation + --> $DIR/lint-type-overflow3.rs:14:18 + | +LL | let x: i8 = ---128; + | ^^^^^ + | + = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages + = note: use `-= 1` if you meant to decrement the value +help: add parentheses for clarity + | +LL | let x: i8 = --(-128); + | + + + +warning: use of a double negation + --> $DIR/lint-type-overflow3.rs:16:19 + | +LL | let x: i8 = ----128; + | ^^^^^ + | + = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages + = note: use `-= 1` if you meant to decrement the value +help: add parentheses for clarity + | +LL | let x: i8 = ---(-128); + | + + + +warning: use of a double negation + --> $DIR/lint-type-overflow3.rs:19:20 + | +LL | let x: i8 = -----128; + | ^^^^^ + | + = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages + = note: use `-= 1` if you meant to decrement the value +help: add parentheses for clarity + | +LL | let x: i8 = ----(-128); + | + + + +warning: use of a double negation + --> $DIR/lint-type-overflow3.rs:21:21 + | +LL | let x: i8 = ------128; + | ^^^^^ + | + = note: the prefix `--` could be misinterpreted as a decrement operator which exists in other languages + = note: use `-= 1` if you meant to decrement the value +help: add parentheses for clarity + | +LL | let x: i8 = -----(-128); + | + + + +error: this arithmetic operation will overflow + --> $DIR/lint-type-overflow3.rs:11:17 + | +LL | let x: i8 = --128; + | ^^^^^ attempt to negate `i8::MIN`, which would overflow + | +note: the lint level is defined here + --> $DIR/lint-type-overflow3.rs:5:31 + | +LL | #![deny(overflowing_literals, arithmetic_overflow)] + | ^^^^^^^^^^^^^^^^^^^ + +error: this arithmetic operation will overflow + --> $DIR/lint-type-overflow3.rs:14:18 + | +LL | let x: i8 = ---128; + | ^^^^^ attempt to negate `i8::MIN`, which would overflow + +error: this arithmetic operation will overflow + --> $DIR/lint-type-overflow3.rs:16:19 + | +LL | let x: i8 = ----128; + | ^^^^^ attempt to negate `i8::MIN`, which would overflow + +error: this arithmetic operation will overflow + --> $DIR/lint-type-overflow3.rs:19:20 + | +LL | let x: i8 = -----128; + | ^^^^^ attempt to negate `i8::MIN`, which would overflow + +error: this arithmetic operation will overflow + --> $DIR/lint-type-overflow3.rs:21:21 + | +LL | let x: i8 = ------128; + | ^^^^^ attempt to negate `i8::MIN`, which would overflow + +error: literal out of range for `i8` + --> $DIR/lint-type-overflow3.rs:8:17 + | +LL | let x: i8 = 128; + | ^^^ + | + = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127` + = help: consider using the type `u8` instead +note: the lint level is defined here + --> $DIR/lint-type-overflow3.rs:5:9 + | +LL | #![deny(overflowing_literals, arithmetic_overflow)] + | ^^^^^^^^^^^^^^^^^^^^ + +error: literal out of range for `i8` + --> $DIR/lint-type-overflow3.rs:11:19 + | +LL | let x: i8 = --128; + | ^^^ + | + = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127` + = help: consider using the type `u8` instead + +error: literal out of range for `i8` + --> $DIR/lint-type-overflow3.rs:16:21 + | +LL | let x: i8 = ----128; + | ^^^ + | + = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127` + = help: consider using the type `u8` instead + +error: literal out of range for `i8` + --> $DIR/lint-type-overflow3.rs:21:23 + | +LL | let x: i8 = ------128; + | ^^^ + | + = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127` + = help: consider using the type `u8` instead + +error: aborting due to 9 previous errors; 5 warnings emitted + From 8d254b83ae5fd7eeb348f7994ffaafad6d6e4953 Mon Sep 17 00:00:00 2001 From: "Tim (Theemathas Chirananthavat)" Date: Tue, 23 Jun 2026 18:54:29 +0700 Subject: [PATCH 007/215] Fix `overflowing_literals` lint with repeated negation Fixes https://github.com/rust-lang/rust/issues/158295 The `overflowing_literal` lint is, I believe, supposed to consider the literal and not the surrounding context when deciding whether to lint. This is in contrast to the `arithmetic_overflow` lint, which only considers code that executes at run time, which excludes the negation inside a literal. According to [the reference](https://doc.rust-lang.org/1.96.0/reference/expressions/operator-expr.html#r-expr.operator.int-overflow.unary-neg), a negation in front of an integer does not result in an overflow. I think of this as: a negation, and only one negation, in front of an integer, is "part of" the integer. Therefore, the `overflowing_literal` lint should consider that when linting. It should not consider the second or third negation. Therefore, this commit changes `overflowing_literals` so that it only lints on `128_i8` when there is no preceding negation at all. This is in contrast to the previous behavior, which lints on `128_i8` preceded by an even number of negations. --- compiler/rustc_lint/src/types.rs | 8 ++--- tests/ui/lint/lint-type-overflow3.rs | 9 ++--- tests/ui/lint/lint-type-overflow3.stderr | 45 +++++------------------- 3 files changed, 14 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 52e89487d4832..9a93d63bd96a6 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -568,12 +568,8 @@ impl<'tcx> LateLintPass<'tcx> for TypeLimits { fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx hir::Expr<'tcx>) { match e.kind { hir::ExprKind::Unary(hir::UnOp::Neg, expr) => { - // Propagate negation, if the negation itself isn't negated - if self.last_visited_negation.is_none_or(|negation| negation.negated_id != e.hir_id) - { - self.last_visited_negation = - Some(NegationInfo { negation_span: e.span, negated_id: expr.hir_id }); - } + self.last_visited_negation = + Some(NegationInfo { negation_span: e.span, negated_id: expr.hir_id }); } hir::ExprKind::Binary(binop, ref l, ref r) => { if is_comparison(binop.node) { diff --git a/tests/ui/lint/lint-type-overflow3.rs b/tests/ui/lint/lint-type-overflow3.rs index 5166ff64b59a3..d51f94fae696c 100644 --- a/tests/ui/lint/lint-type-overflow3.rs +++ b/tests/ui/lint/lint-type-overflow3.rs @@ -9,16 +9,13 @@ fn main() { //~^ ERROR literal out of range for `i8` let x: i8 = -128; let x: i8 = --128; //~ WARN use of a double negation - //~^ ERROR literal out of range for `i8` - //~| ERROR this arithmetic operation will overflow + //~^ ERROR this arithmetic operation will overflow let x: i8 = ---128; //~ WARN use of a double negation //~^ ERROR this arithmetic operation will overflow let x: i8 = ----128; //~ WARN use of a double negation - //~^ ERROR literal out of range for `i8` - //~| ERROR this arithmetic operation will overflow + //~^ ERROR this arithmetic operation will overflow let x: i8 = -----128; //~ WARN use of a double negation //~^ ERROR this arithmetic operation will overflow let x: i8 = ------128; //~ WARN use of a double negation - //~^ ERROR literal out of range for `i8` - //~| ERROR this arithmetic operation will overflow + //~^ ERROR this arithmetic operation will overflow } diff --git a/tests/ui/lint/lint-type-overflow3.stderr b/tests/ui/lint/lint-type-overflow3.stderr index 211a6853ab17b..3dc7fbec1eebf 100644 --- a/tests/ui/lint/lint-type-overflow3.stderr +++ b/tests/ui/lint/lint-type-overflow3.stderr @@ -13,7 +13,7 @@ LL | let x: i8 = -(-128); | + + warning: use of a double negation - --> $DIR/lint-type-overflow3.rs:14:18 + --> $DIR/lint-type-overflow3.rs:13:18 | LL | let x: i8 = ---128; | ^^^^^ @@ -26,7 +26,7 @@ LL | let x: i8 = --(-128); | + + warning: use of a double negation - --> $DIR/lint-type-overflow3.rs:16:19 + --> $DIR/lint-type-overflow3.rs:15:19 | LL | let x: i8 = ----128; | ^^^^^ @@ -39,7 +39,7 @@ LL | let x: i8 = ---(-128); | + + warning: use of a double negation - --> $DIR/lint-type-overflow3.rs:19:20 + --> $DIR/lint-type-overflow3.rs:17:20 | LL | let x: i8 = -----128; | ^^^^^ @@ -52,7 +52,7 @@ LL | let x: i8 = ----(-128); | + + warning: use of a double negation - --> $DIR/lint-type-overflow3.rs:21:21 + --> $DIR/lint-type-overflow3.rs:19:21 | LL | let x: i8 = ------128; | ^^^^^ @@ -77,25 +77,25 @@ LL | #![deny(overflowing_literals, arithmetic_overflow)] | ^^^^^^^^^^^^^^^^^^^ error: this arithmetic operation will overflow - --> $DIR/lint-type-overflow3.rs:14:18 + --> $DIR/lint-type-overflow3.rs:13:18 | LL | let x: i8 = ---128; | ^^^^^ attempt to negate `i8::MIN`, which would overflow error: this arithmetic operation will overflow - --> $DIR/lint-type-overflow3.rs:16:19 + --> $DIR/lint-type-overflow3.rs:15:19 | LL | let x: i8 = ----128; | ^^^^^ attempt to negate `i8::MIN`, which would overflow error: this arithmetic operation will overflow - --> $DIR/lint-type-overflow3.rs:19:20 + --> $DIR/lint-type-overflow3.rs:17:20 | LL | let x: i8 = -----128; | ^^^^^ attempt to negate `i8::MIN`, which would overflow error: this arithmetic operation will overflow - --> $DIR/lint-type-overflow3.rs:21:21 + --> $DIR/lint-type-overflow3.rs:19:21 | LL | let x: i8 = ------128; | ^^^^^ attempt to negate `i8::MIN`, which would overflow @@ -114,32 +114,5 @@ note: the lint level is defined here LL | #![deny(overflowing_literals, arithmetic_overflow)] | ^^^^^^^^^^^^^^^^^^^^ -error: literal out of range for `i8` - --> $DIR/lint-type-overflow3.rs:11:19 - | -LL | let x: i8 = --128; - | ^^^ - | - = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127` - = help: consider using the type `u8` instead - -error: literal out of range for `i8` - --> $DIR/lint-type-overflow3.rs:16:21 - | -LL | let x: i8 = ----128; - | ^^^ - | - = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127` - = help: consider using the type `u8` instead - -error: literal out of range for `i8` - --> $DIR/lint-type-overflow3.rs:21:23 - | -LL | let x: i8 = ------128; - | ^^^ - | - = note: the literal `128` does not fit into the type `i8` whose range is `-128..=127` - = help: consider using the type `u8` instead - -error: aborting due to 9 previous errors; 5 warnings emitted +error: aborting due to 6 previous errors; 5 warnings emitted From 7f3569b1741e9fec563abe7d6a3ca80af282d3e8 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 29 Jun 2026 20:09:50 +0200 Subject: [PATCH 008/215] Implement diagnostic::opaque attribute --- .../src/attributes/diagnostic/mod.rs | 1 + .../src/attributes/diagnostic/opaque.rs | 64 +++++++++++++++++++ compiler/rustc_attr_parsing/src/context.rs | 2 + .../rustc_attr_parsing/src/diagnostics.rs | 4 ++ compiler/rustc_errors/src/emitter.rs | 14 ++-- compiler/rustc_expand/src/base.rs | 16 +++-- compiler/rustc_feature/src/builtin_attrs.rs | 1 + compiler/rustc_feature/src/unstable.rs | 2 + .../rustc_hir/src/attrs/data_structures.rs | 3 + .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_passes/src/check_attr.rs | 1 + compiler/rustc_resolve/src/macros.rs | 1 + compiler/rustc_span/src/hygiene.rs | 5 ++ compiler/rustc_span/src/lib.rs | 31 +++++---- compiler/rustc_span/src/symbol.rs | 2 + .../opaque/auxiliary/wrap.rs | 9 +++ .../diagnostic_namespace/opaque/duplicate.rs | 9 +++ .../opaque/duplicate.stderr | 19 ++++++ ...highlight_maccall.-Zmacro-backtrace.stderr | 16 +++++ .../opaque/highlight_maccall.default.stderr | 8 +++ .../opaque/highlight_maccall.rs | 17 +++++ ...ly_point_at_input.-Zmacro-backtrace.stderr | 18 ++++++ .../opaque/only_point_at_input.default.stderr | 15 +++++ .../opaque/only_point_at_input.rs | 23 +++++++ .../feature-gate-diagnostic-opaque.rs | 13 ++++ .../feature-gate-diagnostic-opaque.stderr | 23 +++++++ .../ui/include-macros/mismatched-types.stderr | 9 +-- 27 files changed, 296 insertions(+), 31 deletions(-) create mode 100644 compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs create mode 100644 tests/ui/diagnostic_namespace/opaque/auxiliary/wrap.rs create mode 100644 tests/ui/diagnostic_namespace/opaque/duplicate.rs create mode 100644 tests/ui/diagnostic_namespace/opaque/duplicate.stderr create mode 100644 tests/ui/diagnostic_namespace/opaque/highlight_maccall.-Zmacro-backtrace.stderr create mode 100644 tests/ui/diagnostic_namespace/opaque/highlight_maccall.default.stderr create mode 100644 tests/ui/diagnostic_namespace/opaque/highlight_maccall.rs create mode 100644 tests/ui/diagnostic_namespace/opaque/only_point_at_input.-Zmacro-backtrace.stderr create mode 100644 tests/ui/diagnostic_namespace/opaque/only_point_at_input.default.stderr create mode 100644 tests/ui/diagnostic_namespace/opaque/only_point_at_input.rs create mode 100644 tests/ui/feature-gates/feature-gate-diagnostic-opaque.rs create mode 100644 tests/ui/feature-gates/feature-gate-diagnostic-opaque.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 435367e7dcfbe..2486b5e3a65ed 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -28,6 +28,7 @@ pub(crate) mod on_type_error; pub(crate) mod on_unimplemented; pub(crate) mod on_unknown; pub(crate) mod on_unmatched_args; +pub(crate) mod opaque; #[derive(Copy, Clone)] pub(crate) enum Mode { diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs new file mode 100644 index 0000000000000..0a18b69dda0ed --- /dev/null +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/opaque.rs @@ -0,0 +1,64 @@ +use rustc_feature::AttributeStability; +use rustc_hir::Target; +use rustc_hir::attrs::AttributeKind; +use rustc_session::lint::builtin::MALFORMED_DIAGNOSTIC_ATTRIBUTES; +use rustc_span::{Span, sym}; + +use crate::attributes::{AcceptMapping, AttributeParser}; +use crate::context::{AcceptContext, FinalizeContext}; +use crate::diagnostics::OpaqueDoesNotExpectArgs; +use crate::parser::ArgParser; +use crate::target_checking::AllowedTargets; +use crate::target_checking::Policy::Allow; +use crate::{template, unstable}; + +#[derive(Default)] +pub(crate) struct OpaqueParser { + attr_span: Option, +} + +impl AttributeParser for OpaqueParser { + const ATTRIBUTES: AcceptMapping = &[ + ( + &[sym::diagnostic, sym::opaque], + template!(Word), + AttributeStability::Stable, // Unstable, stability checked manually in the parser + |this, cx, args| { + if !cx.features().diagnostic_opaque() { + return; + } + this.parse(cx, args); + }, + ), + ( + // For use on exported macros, where using tool attributes is an error. + &[sym::rustc_diagnostic_opaque], + template!(Word), + unstable!( + rustc_attrs, + "see `#[diagnostic::opaque]` for the nightly equivalent of this attribute" + ), + OpaqueParser::parse, + ), + ]; + const ALLOWED_TARGETS: AllowedTargets<'_> = + AllowedTargets::AllowListWarnRest(&[Allow(Target::MacroDef)]); + + fn finalize(self, _cx: &FinalizeContext<'_, '_>) -> Option { + if let Some(_) = self.attr_span { Some(AttributeKind::Opaque) } else { None } + } +} + +impl OpaqueParser { + fn parse<'sess>(&mut self, cx: &mut AcceptContext<'_, 'sess>, args: &ArgParser) { + let attr_span = cx.attr_span; + if let Some(earlier_span) = self.attr_span { + cx.warn_unused_duplicate(earlier_span, attr_span); + } + self.attr_span = Some(attr_span); + + if !matches!(args, ArgParser::NoArgs) { + cx.emit_lint(MALFORMED_DIAGNOSTIC_ATTRIBUTES, OpaqueDoesNotExpectArgs, attr_span); + } + } +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 1595d2f80ddc7..a14a8d4381a0b 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -36,6 +36,7 @@ use crate::attributes::diagnostic::on_type_error::*; use crate::attributes::diagnostic::on_unimplemented::*; use crate::attributes::diagnostic::on_unknown::*; use crate::attributes::diagnostic::on_unmatched_args::*; +use crate::attributes::diagnostic::opaque::*; use crate::attributes::doc::*; use crate::attributes::dummy::*; use crate::attributes::inline::*; @@ -151,6 +152,7 @@ attribute_parsers!( OnUnimplementedParser, OnUnknownParser, OnUnmatchedArgsParser, + OpaqueParser, RustcAlignParser, RustcAlignStaticParser, RustcCguTestAttributeParser, diff --git a/compiler/rustc_attr_parsing/src/diagnostics.rs b/compiler/rustc_attr_parsing/src/diagnostics.rs index 50667952b814d..e360f5221c138 100644 --- a/compiler/rustc_attr_parsing/src/diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/diagnostics.rs @@ -279,6 +279,10 @@ pub(crate) struct AttrCrateLevelOnly; #[diag("`#[diagnostic::do_not_recommend]` does not expect any arguments")] pub(crate) struct DoNotRecommendDoesNotExpectArgs; +#[derive(Diagnostic)] +#[diag("`#[diagnostic::opaque]` does not expect any arguments")] +pub(crate) struct OpaqueDoesNotExpectArgs; + #[derive(Diagnostic)] #[diag("invalid `crate_type` value")] pub(crate) struct UnknownCrateTypes { diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index fa3ff21b2726f..d6651f7801e11 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -188,8 +188,7 @@ pub trait Emitter { self.render_multispans_macro_backtrace(span, children, backtrace); if !backtrace { - // Skip builtin macros, as their expansion isn't relevant to the end user. This includes - // actual intrinsics, like `asm!`. + // Skip macros annotated with `#[diagnostic::opaque]`. Builtin macros are "opaque" too. if let Some((macro_kind, name, _)) = has_macro_spans.first() && let Some((_, _, false)) = has_macro_spans.last() { @@ -334,6 +333,13 @@ pub trait Emitter { // we move these spans from the external macros to their corresponding use site. fn fix_multispan_in_extern_macros(&self, span: &mut MultiSpan) { let Some(source_map) = self.source_map() else { return }; + let should_hide = |span| { + source_map.is_imported(span) || { + let expn = span.data().ctxt.outer_expn_data(); + expn.diagnostic_opaque && matches!(expn.kind, ExpnKind::Macro(MacroKind::Bang, _)) + } + }; + // First, find all the spans in external macros and point instead at their use site. let replacements: Vec<(Span, Span)> = span .primary_spans() @@ -341,11 +347,11 @@ pub trait Emitter { .copied() .chain(span.span_labels().iter().map(|sp_label| sp_label.span)) .filter_map(|sp| { - if !sp.is_dummy() && source_map.is_imported(sp) { + if !sp.is_dummy() && should_hide(sp) { let mut span = sp; while let Some(callsite) = span.parent_callsite() { span = callsite; - if !source_map.is_imported(span) { + if !should_hide(span) { return Some((sp, span)); } } diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index ee516991cfbfc..b829ff7eb1abd 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -807,6 +807,8 @@ pub struct SyntaxExtension { /// Suppresses the "this error originates in the macro" note when a diagnostic points at this /// macro. pub hide_backtrace: bool, + /// Prevents diagnostics pointing into this macro. + pub diagnostic_opaque: bool, } impl SyntaxExtension { @@ -841,6 +843,7 @@ impl SyntaxExtension { local_inner_macros: false, collapse_debuginfo: false, hide_backtrace: false, + diagnostic_opaque: false, } } @@ -870,12 +873,6 @@ impl SyntaxExtension { collapse_table[flag as usize][attr as usize] } - fn get_hide_backtrace(attrs: &[hir::Attribute]) -> bool { - // FIXME(estebank): instead of reusing `#[rustc_diagnostic_item]` as a proxy, introduce a - // new attribute purely for this under the `#[diagnostic]` namespace. - find_attr!(attrs, RustcDiagnosticItem(..)) - } - /// Constructs a syntax extension with the given properties /// and other properties converted from attributes. pub fn new( @@ -910,7 +907,10 @@ impl SyntaxExtension { // Not a built-in macro None => (None, helper_attrs), }; - let hide_backtrace = builtin_name.is_some() || Self::get_hide_backtrace(attrs); + let diagnostic_opaque = builtin_name.is_some() + || (!sess.opts.unstable_opts.macro_backtrace + && find_attr!(attrs, Opaque | RustcDiagnosticItem(..))); + let hide_backtrace = diagnostic_opaque; let stability = find_attr!(attrs, Stability { stability, .. } => *stability); @@ -939,6 +939,7 @@ impl SyntaxExtension { local_inner_macros, collapse_debuginfo, hide_backtrace, + diagnostic_opaque, } } @@ -1025,6 +1026,7 @@ impl SyntaxExtension { self.local_inner_macros, self.collapse_debuginfo, self.hide_backtrace, + self.diagnostic_opaque, ) } } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index e081c2989d3c6..be6979314b53a 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -320,6 +320,7 @@ pub static BUILTIN_ATTRIBUTES: &[Symbol] = &[ // Used by the `rustc::bad_opt_access` lint on fields // types (as well as any others in future). sym::rustc_lint_opt_deny_field_access, + sym::rustc_diagnostic_opaque, // ========================================================================== // Internal attributes, Const related: diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index fc38d9de6e143..4c5e999117248 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -518,6 +518,8 @@ declare_features! ( (unstable, diagnostic_on_unknown, "1.96.0", Some(152900)), /// Allows macros to customize macro argument matcher diagnostics. (unstable, diagnostic_on_unmatched_args, "1.97.0", Some(155642)), + // Used by macros to not show their bodies in error messages. No-op with `-Z macro-backtrace`. + (unstable, diagnostic_opaque, "CURRENT_RUSTC_VERSION", Some(158813)), /// Allows `#[doc(cfg(...))]`. (unstable, doc_cfg, "1.21.0", Some(43781)), /// Allows `#[doc(masked)]`. diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 17d00863d99d5..3e8c11f0166c1 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1263,6 +1263,9 @@ pub enum AttributeKind { directive: Option>, }, + /// Represents `#[diagnostic::opaque]`. + Opaque, + /// Represents `#[optimize(size|speed)]` Optimize(OptimizeAttr, Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index f5a6eeec07406..100979b8159d3 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -83,6 +83,7 @@ impl AttributeKind { OnUnimplemented { .. } => Yes, OnUnknown { .. } => Yes, OnUnmatchedArgs { .. } => Yes, + Opaque => Yes, Optimize(..) => No, PanicRuntime => No, PatchableFunctionEntry { .. } => Yes, diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 7ae6005e9bc98..90339d5bac686 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -296,6 +296,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { AttributeKind::NoStd { .. } => (), AttributeKind::OnUnknown { .. } => (), AttributeKind::OnUnmatchedArgs { .. } => (), + AttributeKind::Opaque => (), AttributeKind::Optimize(..) => (), AttributeKind::PanicRuntime => (), AttributeKind::PatchableFunctionEntry { .. } => (), diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 595c2fdf011dd..171b2cfc0d9ab 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -721,6 +721,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { (sym::on_unknown, Some(sym::diagnostic_on_unknown)), (sym::on_unmatched_args, Some(sym::diagnostic_on_unmatched_args)), (sym::on_type_error, Some(sym::diagnostic_on_type_error)), + (sym::opaque, Some(sym::diagnostic_opaque)), ]; if res == Res::NonMacroAttr(NonMacroAttrKind::Tool) diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 1c742052783cd..3c3fe9389245d 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1022,6 +1022,8 @@ pub struct ExpnData { pub(crate) collapse_debuginfo: bool, /// When true, we do not display the note telling people to use the `-Zmacro-backtrace` flag. pub hide_backtrace: bool, + /// Prevents diagnostics pointing into this, if it is a macro expansion. + pub diagnostic_opaque: bool, } impl !PartialEq for ExpnData {} @@ -1041,6 +1043,7 @@ impl ExpnData { local_inner_macros: bool, collapse_debuginfo: bool, hide_backtrace: bool, + diagnostic_opaque: bool, ) -> ExpnData { ExpnData { kind, @@ -1056,6 +1059,7 @@ impl ExpnData { local_inner_macros, collapse_debuginfo, hide_backtrace, + diagnostic_opaque, } } @@ -1081,6 +1085,7 @@ impl ExpnData { local_inner_macros: false, collapse_debuginfo: false, hide_backtrace: false, + diagnostic_opaque: false, } } diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index 5015741f10c5f..80d40c2f4c5b5 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -1238,20 +1238,25 @@ impl Span { /// If "self" is the span of the outer_ident, and "within" is the span of the `($ident,)` /// expr, then this will return the span of the `$ident` macro variable. pub fn within_macro(self, within: Span, sm: &SourceMap) -> Option { - match Span::prepare_to_combine(self, within) { - // Only return something if it doesn't overlap with the original span, - // and the span isn't "imported" (i.e. from unavailable sources). - // FIXME: This does limit the usefulness of the error when the macro is - // from a foreign crate; we could also take into account `-Zmacro-backtrace`, - // which doesn't redact this span (but that would mean passing in even more - // args to this function, lol). - Ok((self_, _, parent)) - if self_.hi < self.lo() || self.hi() < self_.lo && !sm.is_imported(within) => - { - Some(Span::new(self_.lo, self_.hi, self_.ctxt, parent)) - } - _ => None, + let (self_, _, parent) = Span::prepare_to_combine(self, within).ok()?; + + // Only return something if it doesn't overlap with the original span + // and the span isn't "imported" (i.e. from unavailable sources). + // FIXME: This does limit the usefulness of the error when the macro is + // from a foreign crate; we could also take into account `-Zmacro-backtrace`, + // which doesn't redact this span (but that would mean passing in even more + // args to this function, lol). + if self.data().contains(self_) || sm.is_imported(within) { + return None; } + + // Don't return something if it's marked with `#[diagnostic::opaque]`. + // This already accounts for `-Zmacro-backtrace`. + if within.data().ctxt.outer_expn_data().diagnostic_opaque { + return None; + } + + Some(Span::new(self_.lo, self_.hi, self_.ctxt, parent)) } pub fn from_inner(self, inner: InnerSpan) -> Span { diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 71eae246ebaff..6ba3112f3e7e0 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -819,6 +819,7 @@ symbols! { diagnostic_on_unknown, diagnostic_on_unmatch_args, diagnostic_on_unmatched_args, + diagnostic_opaque, dialect, direct, discriminant_kind, @@ -1775,6 +1776,7 @@ symbols! { rustc_deprecated_safe_2024, rustc_diagnostic_item, rustc_diagnostic_macros, + rustc_diagnostic_opaque, rustc_do_not_const_check, rustc_doc_primitive, rustc_driver, diff --git a/tests/ui/diagnostic_namespace/opaque/auxiliary/wrap.rs b/tests/ui/diagnostic_namespace/opaque/auxiliary/wrap.rs new file mode 100644 index 0000000000000..f046e82b8f9e7 --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/auxiliary/wrap.rs @@ -0,0 +1,9 @@ +#![feature(diagnostic_opaque)] + +#[diagnostic::opaque] +#[macro_export] +macro_rules! wrap { + ($x:ident) => {{ + let x = blah::$x; + }}; +} diff --git a/tests/ui/diagnostic_namespace/opaque/duplicate.rs b/tests/ui/diagnostic_namespace/opaque/duplicate.rs new file mode 100644 index 0000000000000..2341e72ddc5e3 --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/duplicate.rs @@ -0,0 +1,9 @@ +#![crate_type = "lib"] +#![feature(diagnostic_opaque)] +#![deny(unused_attributes)] +#[diagnostic::opaque] +#[diagnostic::opaque] +//~^ERROR unused attribute +macro_rules! m { + () => {} +} diff --git a/tests/ui/diagnostic_namespace/opaque/duplicate.stderr b/tests/ui/diagnostic_namespace/opaque/duplicate.stderr new file mode 100644 index 0000000000000..d4420d3b63a9f --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/duplicate.stderr @@ -0,0 +1,19 @@ +error: unused attribute + --> $DIR/duplicate.rs:5:1 + | +LL | #[diagnostic::opaque] + | ^^^^^^^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/duplicate.rs:4:1 + | +LL | #[diagnostic::opaque] + | ^^^^^^^^^^^^^^^^^^^^^ +note: the lint level is defined here + --> $DIR/duplicate.rs:3:9 + | +LL | #![deny(unused_attributes)] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/diagnostic_namespace/opaque/highlight_maccall.-Zmacro-backtrace.stderr b/tests/ui/diagnostic_namespace/opaque/highlight_maccall.-Zmacro-backtrace.stderr new file mode 100644 index 0000000000000..d53e5cb526061 --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/highlight_maccall.-Zmacro-backtrace.stderr @@ -0,0 +1,16 @@ +error: oh no + --> $DIR/highlight_maccall.rs:9:9 + | +LL | / macro_rules! my_error { +LL | | () => {{ +LL | | compile_error!("oh no") + | | ^^^^^^^^^^^^^^^^^^^^^^^ +... | +LL | | } + | |_- in this expansion of `my_error!` +... +LL | my_error!(); + | ----------- in this macro invocation + +error: aborting due to 1 previous error + diff --git a/tests/ui/diagnostic_namespace/opaque/highlight_maccall.default.stderr b/tests/ui/diagnostic_namespace/opaque/highlight_maccall.default.stderr new file mode 100644 index 0000000000000..07521e003e3e2 --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/highlight_maccall.default.stderr @@ -0,0 +1,8 @@ +error: oh no + --> $DIR/highlight_maccall.rs:16:5 + | +LL | my_error!(); + | ^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/diagnostic_namespace/opaque/highlight_maccall.rs b/tests/ui/diagnostic_namespace/opaque/highlight_maccall.rs new file mode 100644 index 0000000000000..f8095838b2368 --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/highlight_maccall.rs @@ -0,0 +1,17 @@ +//@revisions: default -Zmacro-backtrace +//@[-Zmacro-backtrace] compile-flags: -Z macro-backtrace + +#![feature(diagnostic_opaque)] + +#[diagnostic::opaque] +macro_rules! my_error { + () => {{ + compile_error!("oh no") + //~^ ERROR oh no + }} +} + + +fn main() { + my_error!(); +} diff --git a/tests/ui/diagnostic_namespace/opaque/only_point_at_input.-Zmacro-backtrace.stderr b/tests/ui/diagnostic_namespace/opaque/only_point_at_input.-Zmacro-backtrace.stderr new file mode 100644 index 0000000000000..47ed10e2bcfdc --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/only_point_at_input.-Zmacro-backtrace.stderr @@ -0,0 +1,18 @@ +error[E0425]: cannot find value `x` in module `blah` + --> $DIR/only_point_at_input.rs:17:17 + | +LL | wrap::wrap!(x); + | ^ not found in `blah` + +error[E0425]: cannot find value `x` in module `blah` + --> $DIR/only_point_at_input.rs:20:17 + | +LL | let x = blah::$x; + | -- due to this macro variable +... +LL | local_wrap!(x); + | ^ not found in `blah` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/diagnostic_namespace/opaque/only_point_at_input.default.stderr b/tests/ui/diagnostic_namespace/opaque/only_point_at_input.default.stderr new file mode 100644 index 0000000000000..366e1c3aead24 --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/only_point_at_input.default.stderr @@ -0,0 +1,15 @@ +error[E0425]: cannot find value `x` in module `blah` + --> $DIR/only_point_at_input.rs:17:17 + | +LL | wrap::wrap!(x); + | ^ not found in `blah` + +error[E0425]: cannot find value `x` in module `blah` + --> $DIR/only_point_at_input.rs:20:17 + | +LL | local_wrap!(x); + | ^ not found in `blah` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/diagnostic_namespace/opaque/only_point_at_input.rs b/tests/ui/diagnostic_namespace/opaque/only_point_at_input.rs new file mode 100644 index 0000000000000..003b6dbacf6c0 --- /dev/null +++ b/tests/ui/diagnostic_namespace/opaque/only_point_at_input.rs @@ -0,0 +1,23 @@ +//@revisions: default -Zmacro-backtrace +//@[-Zmacro-backtrace] compile-flags: -Z macro-backtrace +//@ aux-crate:wrap=wrap.rs +#![feature(diagnostic_opaque)] + +mod blah {} + +#[diagnostic::opaque] +macro_rules! local_wrap { + ($x:ident) => {{ + let x = blah::$x; + }}; +} + + +fn main() { + wrap::wrap!(x); + //~^ ERROR cannot find value `x` in module `blah` + + local_wrap!(x); + //~^ ERROR cannot find value `x` in module `blah` + +} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-opaque.rs b/tests/ui/feature-gates/feature-gate-diagnostic-opaque.rs new file mode 100644 index 0000000000000..0dc3a4569098f --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-opaque.rs @@ -0,0 +1,13 @@ +#![crate_type = "lib"] +#![feature(decl_macro)] +#![deny(unknown_diagnostic_attributes)] + +#[diagnostic::opaque] +//~^ ERROR unknown diagnostic attribute +macro_rules! foo { + () => {} +} + +#[diagnostic::opaque] +//~^ ERROR unknown diagnostic attribute +macro bar() {} diff --git a/tests/ui/feature-gates/feature-gate-diagnostic-opaque.stderr b/tests/ui/feature-gates/feature-gate-diagnostic-opaque.stderr new file mode 100644 index 0000000000000..90426a1324e81 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-diagnostic-opaque.stderr @@ -0,0 +1,23 @@ +error: unknown diagnostic attribute + --> $DIR/feature-gate-diagnostic-opaque.rs:5:15 + | +LL | #[diagnostic::opaque] + | ^^^^^^ + | + = help: add `#![feature(diagnostic_opaque)]` to the crate attributes to enable +note: the lint level is defined here + --> $DIR/feature-gate-diagnostic-opaque.rs:3:9 + | +LL | #![deny(unknown_diagnostic_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: unknown diagnostic attribute + --> $DIR/feature-gate-diagnostic-opaque.rs:11:15 + | +LL | #[diagnostic::opaque] + | ^^^^^^ + | + = help: add `#![feature(diagnostic_opaque)]` to the crate attributes to enable + +error: aborting due to 2 previous errors + diff --git a/tests/ui/include-macros/mismatched-types.stderr b/tests/ui/include-macros/mismatched-types.stderr index 4fab832c2d8e8..ab6b599c2beda 100644 --- a/tests/ui/include-macros/mismatched-types.stderr +++ b/tests/ui/include-macros/mismatched-types.stderr @@ -1,13 +1,8 @@ error[E0308]: mismatched types - --> $DIR/file.txt:0:1 - | -LL | - | ^ expected `&[u8]`, found `&str` - | - ::: $DIR/mismatched-types.rs:2:12 + --> $DIR/mismatched-types.rs:2:20 | LL | let b: &[u8] = include_str!("file.txt"); - | ----- ------------------------ in this macro invocation + | ----- ^^^^^^^^^^^^^^^^^^^^^^^^ expected `&[u8]`, found `&str` | | | expected due to this | From 6a17b7948e0c47fc46f01b3d282f515e436b9a4f Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 29 Jun 2026 20:31:38 +0200 Subject: [PATCH 009/215] Remove `hide_backtrace` --- compiler/rustc_errors/src/emitter.rs | 2 +- compiler/rustc_expand/src/base.rs | 10 ++-------- compiler/rustc_span/src/hygiene.rs | 8 ++------ 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_errors/src/emitter.rs b/compiler/rustc_errors/src/emitter.rs index d6651f7801e11..6d5f8462ff496 100644 --- a/compiler/rustc_errors/src/emitter.rs +++ b/compiler/rustc_errors/src/emitter.rs @@ -175,7 +175,7 @@ pub trait Emitter { ExpnKind::Desugaring(..) | ExpnKind::AstPass(..) => None, ExpnKind::Macro(macro_kind, name) => { - Some((macro_kind, name, expn_data.hide_backtrace)) + Some((macro_kind, name, expn_data.diagnostic_opaque)) } } }) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index b829ff7eb1abd..08142f40aeff3 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -804,10 +804,8 @@ pub struct SyntaxExtension { /// Should debuginfo for the macro be collapsed to the outermost expansion site (in other /// words, was the macro definition annotated with `#[collapse_debuginfo]`)? pub collapse_debuginfo: bool, - /// Suppresses the "this error originates in the macro" note when a diagnostic points at this - /// macro. - pub hide_backtrace: bool, - /// Prevents diagnostics pointing into this macro. + /// Prevents diagnostics pointing into this macro and suppresses the "this error originates in + /// the macro" note when a diagnostic points at this macro. pub diagnostic_opaque: bool, } @@ -842,7 +840,6 @@ impl SyntaxExtension { allow_internal_unsafe: false, local_inner_macros: false, collapse_debuginfo: false, - hide_backtrace: false, diagnostic_opaque: false, } } @@ -910,7 +907,6 @@ impl SyntaxExtension { let diagnostic_opaque = builtin_name.is_some() || (!sess.opts.unstable_opts.macro_backtrace && find_attr!(attrs, Opaque | RustcDiagnosticItem(..))); - let hide_backtrace = diagnostic_opaque; let stability = find_attr!(attrs, Stability { stability, .. } => *stability); @@ -938,7 +934,6 @@ impl SyntaxExtension { allow_internal_unsafe, local_inner_macros, collapse_debuginfo, - hide_backtrace, diagnostic_opaque, } } @@ -1025,7 +1020,6 @@ impl SyntaxExtension { self.allow_internal_unsafe, self.local_inner_macros, self.collapse_debuginfo, - self.hide_backtrace, self.diagnostic_opaque, ) } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 3c3fe9389245d..1cdce5cd04567 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -1020,9 +1020,8 @@ pub struct ExpnData { /// Should debuginfo for the macro be collapsed to the outermost expansion site (in other /// words, was the macro definition annotated with `#[collapse_debuginfo]`)? pub(crate) collapse_debuginfo: bool, - /// When true, we do not display the note telling people to use the `-Zmacro-backtrace` flag. - pub hide_backtrace: bool, - /// Prevents diagnostics pointing into this, if it is a macro expansion. + /// When true, we prevent diagnostics pointing into this macro, if it is one, and we do not + /// display the note telling people to use the `-Zmacro-backtrace` flag. pub diagnostic_opaque: bool, } @@ -1042,7 +1041,6 @@ impl ExpnData { allow_internal_unsafe: bool, local_inner_macros: bool, collapse_debuginfo: bool, - hide_backtrace: bool, diagnostic_opaque: bool, ) -> ExpnData { ExpnData { @@ -1058,7 +1056,6 @@ impl ExpnData { allow_internal_unsafe, local_inner_macros, collapse_debuginfo, - hide_backtrace, diagnostic_opaque, } } @@ -1084,7 +1081,6 @@ impl ExpnData { allow_internal_unsafe: false, local_inner_macros: false, collapse_debuginfo: false, - hide_backtrace: false, diagnostic_opaque: false, } } From 106bb96346de3b9c334b11c58a6588d09d153abf Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Sun, 5 Jul 2026 17:33:52 +0200 Subject: [PATCH 010/215] Remove `rustc_diagnostic_item` hack --- compiler/rustc_expand/src/base.rs | 3 +-- library/alloc/src/macros.rs | 2 ++ library/core/src/lib.rs | 1 + library/core/src/macros/mod.rs | 8 ++++++++ library/core/src/pin.rs | 1 + library/std/src/macros.rs | 3 +++ library/std/src/thread/local.rs | 1 + 7 files changed, 17 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 08142f40aeff3..323299c74d6fb 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -905,8 +905,7 @@ impl SyntaxExtension { None => (None, helper_attrs), }; let diagnostic_opaque = builtin_name.is_some() - || (!sess.opts.unstable_opts.macro_backtrace - && find_attr!(attrs, Opaque | RustcDiagnosticItem(..))); + || (!sess.opts.unstable_opts.macro_backtrace && find_attr!(attrs, Opaque)); let stability = find_attr!(attrs, Stability { stability, .. } => *stability); diff --git a/library/alloc/src/macros.rs b/library/alloc/src/macros.rs index b99107fb345a4..d93e279df7cfb 100644 --- a/library/alloc/src/macros.rs +++ b/library/alloc/src/macros.rs @@ -39,6 +39,7 @@ #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "vec_macro"] #[allow_internal_unstable(rustc_attrs, liballoc_internals)] +#[rustc_diagnostic_opaque] macro_rules! vec { () => ( $crate::vec::Vec::new() @@ -108,6 +109,7 @@ macro_rules! vec { #[stable(feature = "rust1", since = "1.0.0")] #[allow_internal_unstable(hint_must_use, liballoc_internals)] #[rustc_diagnostic_item = "format_macro"] +#[rustc_diagnostic_opaque] macro_rules! format { ($($arg:tt)*) => { $crate::__export::must_use({ diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 8e310cc7d2155..7aab486903a06 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -121,6 +121,7 @@ #![feature(derive_const)] #![feature(diagnostic_on_const)] #![feature(diagnostic_on_unmatched_args)] +#![feature(diagnostic_opaque)] #![feature(doc_cfg)] #![feature(doc_notable_trait)] #![feature(extern_types)] diff --git a/library/core/src/macros/mod.rs b/library/core/src/macros/mod.rs index 932a2fc0bad92..4b1ce0351f7d7 100644 --- a/library/core/src/macros/mod.rs +++ b/library/core/src/macros/mod.rs @@ -39,6 +39,7 @@ macro_rules! panic { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "assert_eq_macro"] #[allow_internal_unstable(panic_internals)] +#[rustc_diagnostic_opaque] macro_rules! assert_eq { ($left:expr, $right:expr $(,)?) => {{ match (&$left, &$right) { @@ -95,6 +96,7 @@ macro_rules! assert_eq { #[stable(feature = "assert_ne", since = "1.13.0")] #[rustc_diagnostic_item = "assert_ne_macro"] #[allow_internal_unstable(panic_internals)] +#[rustc_diagnostic_opaque] macro_rules! assert_ne { ($left:expr, $right:expr $(,)?) => {{ match (&$left, &$right) { @@ -284,6 +286,7 @@ pub macro cfg_select($($tt:tt)*) { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "debug_assert_macro"] #[allow_internal_unstable(edition_panic)] +#[rustc_diagnostic_opaque] macro_rules! debug_assert { ($($arg:tt)*) => { if $crate::cfg!(debug_assertions) { @@ -424,6 +427,7 @@ pub macro debug_assert_matches($($arg:tt)*) { #[stable(feature = "matches_macro", since = "1.42.0")] #[rustc_diagnostic_item = "matches_macro"] #[allow_internal_unstable(non_exhaustive_omitted_patterns_lint, stmt_expr_attributes)] +#[rustc_diagnostic_opaque] macro_rules! matches { ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { #[allow(non_exhaustive_omitted_patterns)] @@ -600,6 +604,7 @@ macro_rules! r#try { #[macro_export] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "write_macro"] +#[rustc_diagnostic_opaque] macro_rules! write { ($dst:expr, $($arg:tt)*) => { $dst.write_fmt($crate::format_args!($($arg)*)) @@ -638,6 +643,7 @@ macro_rules! write { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "writeln_macro"] #[allow_internal_unstable(format_args_nl)] +#[rustc_diagnostic_opaque] macro_rules! writeln { ($dst:expr $(,)?) => { $crate::write!($dst, "\n") @@ -793,6 +799,7 @@ macro_rules! unreachable { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "unimplemented_macro"] #[allow_internal_unstable(panic_internals)] +#[rustc_diagnostic_opaque] macro_rules! unimplemented { () => { $crate::panicking::panic("not implemented") @@ -873,6 +880,7 @@ macro_rules! unimplemented { #[stable(feature = "todo_macro", since = "1.40.0")] #[rustc_diagnostic_item = "todo_macro"] #[allow_internal_unstable(panic_internals)] +#[rustc_diagnostic_opaque] macro_rules! todo { () => { $crate::panicking::panic("not yet implemented") diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 40f774806046a..b766a767df64d 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -2025,6 +2025,7 @@ unsafe impl PinCoerceUnsized for Pin {} #[rustc_diagnostic_item = "pin_macro"] // `super` gets removed by rustfmt #[rustfmt::skip] +#[diagnostic::opaque] pub macro pin($value:expr $(,)?) { 'p: { super let mut pinned = $value; diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 8bcf870e9aeb4..64415809b4a33 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -82,6 +82,7 @@ macro_rules! panic { #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "print_macro")] #[allow_internal_unstable(print_internals)] +#[rustc_diagnostic_opaque] macro_rules! print { ($($arg:tt)*) => {{ $crate::io::_print($crate::format_args!($($arg)*)); @@ -138,6 +139,7 @@ macro_rules! print { #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "println_macro")] #[allow_internal_unstable(print_internals, format_args_nl)] +#[rustc_diagnostic_opaque] macro_rules! println { () => { $crate::print!("\n") @@ -352,6 +354,7 @@ macro_rules! eprintln { #[macro_export] #[cfg_attr(not(test), rustc_diagnostic_item = "dbg_macro")] #[stable(feature = "dbg_macro", since = "1.32.0")] +#[rustc_diagnostic_opaque] macro_rules! dbg { // NOTE: We cannot use `concat!` to make a static string as a format argument // of `eprintln!` because `file!` could contain a `{` or diff --git a/library/std/src/thread/local.rs b/library/std/src/thread/local.rs index ec0ba9970e479..9365e54765d4c 100644 --- a/library/std/src/thread/local.rs +++ b/library/std/src/thread/local.rs @@ -346,6 +346,7 @@ pub macro thread_local_process_attrs { #[stable(feature = "rust1", since = "1.0.0")] #[cfg_attr(not(test), rustc_diagnostic_item = "thread_local_macro")] #[allow_internal_unstable(thread_local_internals)] +#[rustc_diagnostic_opaque] macro_rules! thread_local { () => {}; From 67fc269618b40c5a492caadbf7cfd994bb3b69f7 Mon Sep 17 00:00:00 2001 From: sgasho Date: Thu, 9 Jul 2026 14:58:36 +0900 Subject: [PATCH 011/215] Add autodiff support for x86_64 apple darwin --- src/doc/rustc-dev-guide/src/autodiff/installation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/autodiff/installation.md b/src/doc/rustc-dev-guide/src/autodiff/installation.md index 4176763a7bd62..52be519b36e8d 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/installation.md +++ b/src/doc/rustc-dev-guide/src/autodiff/installation.md @@ -3,7 +3,7 @@ Most users can enable `std::autodiff` on their latest nightly toolchain by installing the `enzyme` component with rustup, if they are using one of these platforms: - **Linux**: with `x86_64-unknown-linux-gnu` or `aarch64-unknown-linux-gnu` -- **macOS**: with `aarch64-apple-darwin` +- **macOS**: with `aarch64-apple-darwin` or `x86_64-apple-darwin` - **Windows**: with `x86_64-llvm-mingw` or `aarch64-llvm-mingw` As a rustc/enzyme/autodiff contributor, or if you need any other platform, you can build rustc including autodiff from source. From 033d8ca08192084447af76905b37343d9ac81e5f Mon Sep 17 00:00:00 2001 From: Manuel Drehwald Date: Thu, 9 Jul 2026 18:24:56 +0200 Subject: [PATCH 012/215] Add install notes for older rustup versions --- src/doc/rustc-dev-guide/src/autodiff/installation.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/doc/rustc-dev-guide/src/autodiff/installation.md b/src/doc/rustc-dev-guide/src/autodiff/installation.md index 52be519b36e8d..c0da9621f9777 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/installation.md +++ b/src/doc/rustc-dev-guide/src/autodiff/installation.md @@ -18,6 +18,12 @@ Please run: rustup +nightly component add enzyme ``` +Older rustup versions are not aware of this component, so if you run into issues try updating rustup itself: +```console +rustup update +rustup +nightly component add enzyme +``` + ## Installation guide for Nix On [Nix], you can declare a nightly Rust toolchain with the Enzyme component using the [oxalica rust-overlay]. From e5a3ddff86931b27c2e300501e9853fc56729742 Mon Sep 17 00:00:00 2001 From: Leonard Chan Date: Thu, 9 Jul 2026 17:31:27 +0000 Subject: [PATCH 013/215] fuchsia: Ensure all existing ported sanitizers for Fuchsia are supported --- .../rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs | 1 + .../src/spec/targets/riscv64gc_unknown_fuchsia.rs | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs index 6489e2bda8091..d16110cfd66f9 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_fuchsia.rs @@ -10,6 +10,7 @@ pub(crate) fn target() -> Target { base.stack_probes = StackProbeType::Inline; base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::CFI + | SanitizerSet::HWADDRESS | SanitizerSet::LEAK | SanitizerSet::SHADOWCALLSTACK; base.default_sanitizers = SanitizerSet::SHADOWCALLSTACK; diff --git a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs index 8f6bbe4f641d9..fecac5fd84a38 100644 --- a/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs +++ b/compiler/rustc_target/src/spec/targets/riscv64gc_unknown_fuchsia.rs @@ -10,7 +10,10 @@ pub(crate) fn target() -> Target { base.llvm_abiname = LlvmAbi::Lp64d; base.max_atomic_width = Some(64); base.stack_probes = StackProbeType::Inline; - base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::SHADOWCALLSTACK; + base.supported_sanitizers = SanitizerSet::ADDRESS + | SanitizerSet::CFI + | SanitizerSet::LEAK + | SanitizerSet::SHADOWCALLSTACK; base.default_sanitizers = SanitizerSet::SHADOWCALLSTACK; base.supports_xray = true; From cb0ba93c8506c5af9d12c9de2fdb844555723c1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=9D=E5=80=89=E6=B0=B4=E5=B8=8C?= Date: Fri, 3 Jul 2026 15:35:49 +0800 Subject: [PATCH 014/215] Support EII on Windows MSVC --- compiler/rustc_codegen_llvm/src/back/write.rs | 48 +++++++++++-------- compiler/rustc_codegen_llvm/src/base.rs | 6 +++ compiler/rustc_codegen_llvm/src/consts.rs | 14 ++++-- compiler/rustc_codegen_llvm/src/llvm/ffi.rs | 19 ++++---- .../rustc_monomorphize/src/partitioning.rs | 11 +++++ tests/ui/eii/default/call_default.rs | 4 +- tests/ui/eii/default/call_default_panics.rs | 4 +- tests/ui/eii/default/call_impl.rs | 4 +- tests/ui/eii/default/local_crate.rs | 4 +- tests/ui/eii/default/local_crate_explicit.rs | 4 +- tests/ui/eii/duplicate/duplicate1.rs | 4 +- tests/ui/eii/duplicate/duplicate2.rs | 4 +- tests/ui/eii/duplicate/duplicate3.rs | 4 +- tests/ui/eii/duplicate/multiple_impls.rs | 4 +- tests/ui/eii/eii_impl_with_contract.rs | 3 +- tests/ui/eii/linking/codegen_cross_crate.rs | 4 +- tests/ui/eii/linking/codegen_single_crate.rs | 4 +- tests/ui/eii/linking/same-symbol.rs | 4 +- tests/ui/eii/privacy1.rs | 4 +- tests/ui/eii/shadow_builtin.rs | 4 +- tests/ui/eii/static/argument_required.rs | 4 +- tests/ui/eii/static/cross_crate_decl.rs | 4 +- tests/ui/eii/static/cross_crate_def.rs | 4 +- tests/ui/eii/static/default.rs | 4 +- tests/ui/eii/static/default_cross_crate.rs | 4 +- .../static/default_cross_crate_explicit.rs | 4 +- tests/ui/eii/static/default_explicit.rs | 4 +- tests/ui/eii/static/duplicate.rs | 4 +- tests/ui/eii/static/mismatch_fn_static.rs | 4 +- tests/ui/eii/static/mismatch_mut.rs | 4 +- tests/ui/eii/static/mismatch_mut2.rs | 4 +- tests/ui/eii/static/mismatch_safety.rs | 4 +- tests/ui/eii/static/mismatch_safety2.rs | 4 +- tests/ui/eii/static/mismatch_static_fn.rs | 4 +- tests/ui/eii/static/multiple_impls.rs | 4 +- tests/ui/eii/static/mut.rs | 4 +- tests/ui/eii/static/same_address.rs | 4 +- tests/ui/eii/static/simple.rs | 4 +- tests/ui/eii/static/subtype.rs | 4 +- tests/ui/eii/static/subtype_wrong.rs | 4 +- tests/ui/eii/static/wrong_ty.rs | 4 +- 41 files changed, 137 insertions(+), 104 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/back/write.rs b/compiler/rustc_codegen_llvm/src/back/write.rs index 4426f6ebb3c17..149936c56c3b1 100644 --- a/compiler/rustc_codegen_llvm/src/back/write.rs +++ b/compiler/rustc_codegen_llvm/src/back/write.rs @@ -1292,7 +1292,8 @@ fn embed_bitcode( } } -// Create a `__imp_ = &symbol` global for every public static `symbol`. +// Create a `__imp_ = &symbol` global for each externally visible +// static data symbol, including aliases to static data. // This is required to satisfy `dllimport` references to static data in .rlibs // when using MSVC linker. We do this only for data, as linker can fix up // code references on its own. @@ -1305,31 +1306,38 @@ fn create_msvc_imps(cgcx: &CodegenContext, llcx: &llvm::Context, llmod: &llvm::M // names, so we need an extra underscore on x86. There's also a leading // '\x01' here which disables LLVM's symbol mangling (e.g., no extra // underscores added in front). - let prefix = if cgcx.target_arch == "x86" { "\x01__imp__" } else { "\x01__imp_" }; + let prefix: &[u8] = if cgcx.target_arch == "x86" { b"\x01__imp__" } else { b"\x01__imp_" }; let ptr_ty = llvm_type_ptr(llcx); - let globals = base::iter_globals(llmod) - .filter(|&val| { - llvm::get_linkage(val) == llvm::Linkage::ExternalLinkage && !llvm::is_declaration(val) - }) - .filter_map(|val| { - // Exclude some symbols that we know are not Rust symbols. - let name = llvm::get_value_name(val); - if ignored(&name) { None } else { Some((val, name)) } - }) - .map(move |(val, name)| { - let mut imp_name = prefix.as_bytes().to_vec(); - imp_name.extend(name); - let imp_name = CString::new(imp_name).unwrap(); - (imp_name, val) - }) - .collect::>(); + let symbols = std::iter::chain( + base::iter_globals(llmod), + base::iter_global_aliases(llmod).filter(|&val| { + llvm::LLVMGetTypeKind(unsafe { llvm::LLVMGlobalGetValueType(val) }).to_rust() + != llvm::TypeKind::Function + }), + ) + .map(|val| (val, llvm::get_linkage(val))) + .filter(|&(val, linkage)| { + matches!(linkage, llvm::Linkage::ExternalLinkage | llvm::Linkage::WeakAnyLinkage) + && !llvm::is_declaration(val) + }) + .collect::>(); + + for (val, linkage) in symbols { + let name = llvm::get_value_name(val); + // Exclude some symbols that we know are not Rust symbols. + if ignored(&name) { + continue; + } + + let mut imp_name = prefix.to_vec(); + imp_name.extend(name); + let imp_name = CString::new(imp_name).unwrap(); - for (imp_name, val) in globals { let imp = llvm::add_global(llmod, ptr_ty, &imp_name); llvm::set_initializer(imp, val); - llvm::set_linkage(imp, llvm::Linkage::ExternalLinkage); + llvm::set_linkage(imp, linkage); } // Use this function to exclude certain symbols from `__imp` generation. diff --git a/compiler/rustc_codegen_llvm/src/base.rs b/compiler/rustc_codegen_llvm/src/base.rs index bb4ae6e64560a..880376742510b 100644 --- a/compiler/rustc_codegen_llvm/src/base.rs +++ b/compiler/rustc_codegen_llvm/src/base.rs @@ -56,6 +56,12 @@ pub(crate) fn iter_globals(llmod: &llvm::Module) -> ValueIter<'_> { unsafe { ValueIter { cur: llvm::LLVMGetFirstGlobal(llmod), step: llvm::LLVMGetNextGlobal } } } +pub(crate) fn iter_global_aliases(llmod: &llvm::Module) -> ValueIter<'_> { + unsafe { + ValueIter { cur: llvm::LLVMGetFirstGlobalAlias(llmod), step: llvm::LLVMGetNextGlobalAlias } + } +} + pub(crate) fn compile_codegen_unit( tcx: TyCtxt<'_>, cgu_name: Symbol, diff --git a/compiler/rustc_codegen_llvm/src/consts.rs b/compiler/rustc_codegen_llvm/src/consts.rs index a4d52c18c890b..2552f4235e4ef 100644 --- a/compiler/rustc_codegen_llvm/src/consts.rs +++ b/compiler/rustc_codegen_llvm/src/consts.rs @@ -425,8 +425,11 @@ impl<'ll> CodegenCx<'ll, '_> { let dso_local = self.assume_dso_local(g, true); if !def_id.is_local() { + let is_eii = fn_attrs.flags.contains(CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM); let needs_dll_storage_attr = self.use_dll_storage_attrs - && !self.tcx.is_foreign_item(def_id) + // EII static declarations are encoded as foreign items, but their symbols are + // resolved by Rust crates, not native libraries. + && (!self.tcx.is_foreign_item(def_id) || is_eii) // Local definitions can never be imported, so we must not apply // the DLLImport annotation. && !dso_local @@ -446,10 +449,11 @@ impl<'ll> CodegenCx<'ll, '_> { if needs_dll_storage_attr { // This item is external but not foreign, i.e., it originates from an external Rust - // crate. Since we don't know whether this crate will be linked dynamically or - // statically in the final application, we always mark such symbols as 'dllimport'. - // If final linkage happens to be static, we rely on compiler-emitted __imp_ stubs - // to make things work. + // crate. EII static declarations are handled the same way, even though they are + // represented as foreign items. Since we don't know whether this crate will be + // linked dynamically or statically in the final application, we always mark such + // symbols as 'dllimport'. If final linkage happens to be static, we rely on + // compiler-emitted __imp_ stubs to make things work. // // However, in some scenarios we defer emission of statics to downstream // crates, so there are cases where a static with an upstream DefId diff --git a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs index bd770d286851b..8853392e4b9ab 100644 --- a/compiler/rustc_codegen_llvm/src/llvm/ffi.rs +++ b/compiler/rustc_codegen_llvm/src/llvm/ffi.rs @@ -1098,6 +1098,17 @@ unsafe extern "C" { pub(crate) safe fn LLVMSetTailCallKind(CallInst: &Value, kind: TailCallKind); pub(crate) safe fn LLVMSetExternallyInitialized(GlobalVar: &Value, IsExtInit: Bool); + // Operations on global aliases + pub(crate) fn LLVMAddAlias2<'ll>( + M: &'ll Module, + ValueTy: &Type, + AddressSpace: c_uint, + Aliasee: &Value, + Name: *const c_char, + ) -> &'ll Value; + pub(crate) fn LLVMGetFirstGlobalAlias(M: &Module) -> Option<&Value>; + pub(crate) fn LLVMGetNextGlobalAlias(GlobalAlias: &Value) -> Option<&Value>; + // Operations on attributes pub(crate) fn LLVMCreateStringAttribute( C: &Context, @@ -2640,14 +2651,6 @@ unsafe extern "C" { pub(crate) fn LLVMRustSetNoSanitizeAddress(Global: &Value); pub(crate) fn LLVMRustSetNoSanitizeHWAddress(Global: &Value); - pub(crate) fn LLVMAddAlias2<'ll>( - M: &'ll Module, - ValueTy: &Type, - AddressSpace: c_uint, - Aliasee: &Value, - Name: *const c_char, - ) -> &'ll Value; - pub(crate) fn LLVMRustConstPtrAuth( ptr: *const Value, key: u32, diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index bf4a2bdd15107..b1d8f55acd37f 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -770,6 +770,17 @@ fn static_visibility<'tcx>( *can_be_internalized = false; default_visibility(tcx, def_id, false) } else { + if tcx.def_kind(def_id).has_codegen_attrs() { + // Prevent EII and `rustc_std_internal_symbol` statics being internalized. + let attrs = tcx.codegen_fn_attrs(def_id); + if attrs.flags.intersects( + CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL + | CodegenFnAttrFlags::EXTERNALLY_IMPLEMENTABLE_ITEM, + ) { + *can_be_internalized = false; + } + } + Visibility::Hidden } } diff --git a/tests/ui/eii/default/call_default.rs b/tests/ui/eii/default/call_default.rs index 07b2a650d3c42..c090043106903 100644 --- a/tests/ui/eii/default/call_default.rs +++ b/tests/ui/eii/default/call_default.rs @@ -2,8 +2,8 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests EIIs with default implementations. // When there's no explicit declaration, the default should be called from the declaring crate. diff --git a/tests/ui/eii/default/call_default_panics.rs b/tests/ui/eii/default/call_default_panics.rs index 379ba8ea070b6..68740d5ab80d8 100644 --- a/tests/ui/eii/default/call_default_panics.rs +++ b/tests/ui/eii/default/call_default_panics.rs @@ -4,8 +4,8 @@ //@ needs-unwind //@ exec-env:RUST_BACKTRACE=1 //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // A small test to make sure that unwinding works properly. // // Functions can have target-cpu applied. On apple-darwin this is super important, diff --git a/tests/ui/eii/default/call_impl.rs b/tests/ui/eii/default/call_impl.rs index 4553427b8c799..f5b0cca2998f3 100644 --- a/tests/ui/eii/default/call_impl.rs +++ b/tests/ui/eii/default/call_impl.rs @@ -3,8 +3,8 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests EIIs with default implementations. // When an explicit implementation is given in one dependency, and the declaration is in another, // the explicit implementation is preferred. diff --git a/tests/ui/eii/default/local_crate.rs b/tests/ui/eii/default/local_crate.rs index fd4fd459c52fb..d6e992c409453 100644 --- a/tests/ui/eii/default/local_crate.rs +++ b/tests/ui/eii/default/local_crate.rs @@ -1,8 +1,8 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests EIIs with default implementations. // In the same crate, when there's no explicit declaration, the default should be called. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/default/local_crate_explicit.rs b/tests/ui/eii/default/local_crate_explicit.rs index 200905b8753a0..7c29fa0edd6ae 100644 --- a/tests/ui/eii/default/local_crate_explicit.rs +++ b/tests/ui/eii/default/local_crate_explicit.rs @@ -1,8 +1,8 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests EIIs with default implementations. // In the same crate, the explicit implementation should get priority. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/duplicate/duplicate1.rs b/tests/ui/eii/duplicate/duplicate1.rs index 3d770232af50f..0ac0f16af5078 100644 --- a/tests/ui/eii/duplicate/duplicate1.rs +++ b/tests/ui/eii/duplicate/duplicate1.rs @@ -1,8 +1,8 @@ //@ aux-build: impl1.rs //@ aux-build: impl2.rs //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // tests that EIIs error properly, even if the conflicting implementations live in another crate. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/duplicate/duplicate2.rs b/tests/ui/eii/duplicate/duplicate2.rs index 4311969ed8894..8b4d7c4913c91 100644 --- a/tests/ui/eii/duplicate/duplicate2.rs +++ b/tests/ui/eii/duplicate/duplicate2.rs @@ -2,8 +2,8 @@ //@ aux-build: impl2.rs //@ aux-build: impl3.rs //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests the error message when there are multiple implementations of an EII in many crates. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/duplicate/duplicate3.rs b/tests/ui/eii/duplicate/duplicate3.rs index 4504ba30c246e..96d6543130ccb 100644 --- a/tests/ui/eii/duplicate/duplicate3.rs +++ b/tests/ui/eii/duplicate/duplicate3.rs @@ -3,8 +3,8 @@ //@ aux-build: impl3.rs //@ aux-build: impl4.rs //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests the error message when there are multiple implementations of an EII in many crates. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/duplicate/multiple_impls.rs b/tests/ui/eii/duplicate/multiple_impls.rs index 5ce2a27e16957..80f6147789743 100644 --- a/tests/ui/eii/duplicate/multiple_impls.rs +++ b/tests/ui/eii/duplicate/multiple_impls.rs @@ -1,8 +1,8 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether one function could implement two EIIs. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/eii_impl_with_contract.rs b/tests/ui/eii/eii_impl_with_contract.rs index 43d34c294a79c..1789ec92ea60d 100644 --- a/tests/ui/eii/eii_impl_with_contract.rs +++ b/tests/ui/eii/eii_impl_with_contract.rs @@ -1,6 +1,7 @@ //@ run-pass //@ ignore-backends: gcc -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu #![feature(extern_item_impls)] #![feature(contracts)] diff --git a/tests/ui/eii/linking/codegen_cross_crate.rs b/tests/ui/eii/linking/codegen_cross_crate.rs index 192aac5920704..ab0d4d4b9eb25 100644 --- a/tests/ui/eii/linking/codegen_cross_crate.rs +++ b/tests/ui/eii/linking/codegen_cross_crate.rs @@ -3,8 +3,8 @@ //@ aux-build: codegen_cross_crate_other_crate.rs //@ compile-flags: -O //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether calling EIIs works with the declaration in another crate. extern crate codegen_cross_crate_other_crate as codegen; diff --git a/tests/ui/eii/linking/codegen_single_crate.rs b/tests/ui/eii/linking/codegen_single_crate.rs index d0e9c015da418..4faa30a79040f 100644 --- a/tests/ui/eii/linking/codegen_single_crate.rs +++ b/tests/ui/eii/linking/codegen_single_crate.rs @@ -1,8 +1,8 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether calling EIIs works with the declaration in the same crate. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/linking/same-symbol.rs b/tests/ui/eii/linking/same-symbol.rs index afba9b7750262..02518f6bced03 100644 --- a/tests/ui/eii/linking/same-symbol.rs +++ b/tests/ui/eii/linking/same-symbol.rs @@ -1,8 +1,8 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu #![feature(extern_item_impls)] pub mod a { diff --git a/tests/ui/eii/privacy1.rs b/tests/ui/eii/privacy1.rs index 60bf36074e0ae..95544ae867d89 100644 --- a/tests/ui/eii/privacy1.rs +++ b/tests/ui/eii/privacy1.rs @@ -2,8 +2,8 @@ //@ check-run-results //@ aux-build: other_crate_privacy1.rs //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether re-exports work. extern crate other_crate_privacy1 as codegen; diff --git a/tests/ui/eii/shadow_builtin.rs b/tests/ui/eii/shadow_builtin.rs index 6e6ecda2c207c..cd4c14514dc46 100644 --- a/tests/ui/eii/shadow_builtin.rs +++ b/tests/ui/eii/shadow_builtin.rs @@ -1,6 +1,6 @@ //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether calling EIIs works with the declaration in the same crate. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/argument_required.rs b/tests/ui/eii/static/argument_required.rs index 114b8a35de5cf..9b00dcf194387 100644 --- a/tests/ui/eii/static/argument_required.rs +++ b/tests/ui/eii/static/argument_required.rs @@ -1,6 +1,6 @@ //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether EIIs work on statics #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/cross_crate_decl.rs b/tests/ui/eii/static/cross_crate_decl.rs index 63e3511198e1d..75333c48e40ef 100644 --- a/tests/ui/eii/static/cross_crate_decl.rs +++ b/tests/ui/eii/static/cross_crate_decl.rs @@ -3,8 +3,8 @@ //@ aux-build: cross_crate_decl.rs //@ compile-flags: -O //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether calling EIIs works with the declaration in another crate. extern crate cross_crate_decl as codegen; diff --git a/tests/ui/eii/static/cross_crate_def.rs b/tests/ui/eii/static/cross_crate_def.rs index a0b6afbfd760b..ad8bbcd0fb963 100644 --- a/tests/ui/eii/static/cross_crate_def.rs +++ b/tests/ui/eii/static/cross_crate_def.rs @@ -3,8 +3,8 @@ //@ aux-build: cross_crate_def.rs //@ compile-flags: -O //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether calling EIIs works with the declaration and definition in another crate. extern crate cross_crate_def as codegen; diff --git a/tests/ui/eii/static/default.rs b/tests/ui/eii/static/default.rs index 6234ee2f0c15e..beb777cc32e82 100644 --- a/tests/ui/eii/static/default.rs +++ b/tests/ui/eii/static/default.rs @@ -1,8 +1,8 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // FIXME(#157649): static EII defaults currently fail to link on Apple targets. //@ ignore-apple // Tests static EIIs with default implementations. diff --git a/tests/ui/eii/static/default_cross_crate.rs b/tests/ui/eii/static/default_cross_crate.rs index f9de906ac267e..7a2e5ae3925ad 100644 --- a/tests/ui/eii/static/default_cross_crate.rs +++ b/tests/ui/eii/static/default_cross_crate.rs @@ -2,8 +2,8 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // FIXME(#157649): static EII defaults currently fail to link on Apple targets. //@ ignore-apple // Tests that a static EII default can be used from another crate. diff --git a/tests/ui/eii/static/default_cross_crate_explicit.rs b/tests/ui/eii/static/default_cross_crate_explicit.rs index 1f534e300e0c3..183342930ad8c 100644 --- a/tests/ui/eii/static/default_cross_crate_explicit.rs +++ b/tests/ui/eii/static/default_cross_crate_explicit.rs @@ -3,8 +3,8 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // FIXME(#157649): static EII defaults currently fail to link on Apple targets. //@ ignore-apple // Tests that an explicit static EII implementation overrides a cross-crate default. diff --git a/tests/ui/eii/static/default_explicit.rs b/tests/ui/eii/static/default_explicit.rs index 6cf36d8da50a5..8237b18106031 100644 --- a/tests/ui/eii/static/default_explicit.rs +++ b/tests/ui/eii/static/default_explicit.rs @@ -1,8 +1,8 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // FIXME(#157649): static EII defaults currently fail to link on Apple targets. //@ ignore-apple // Tests that an explicit static EII implementation overrides a local default. diff --git a/tests/ui/eii/static/duplicate.rs b/tests/ui/eii/static/duplicate.rs index 12b2e56c07e4e..a8db02f33051d 100644 --- a/tests/ui/eii/static/duplicate.rs +++ b/tests/ui/eii/static/duplicate.rs @@ -1,6 +1,6 @@ //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether EIIs work on statics #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/mismatch_fn_static.rs b/tests/ui/eii/static/mismatch_fn_static.rs index 298fdca18d967..8d8fa5b4d06c4 100644 --- a/tests/ui/eii/static/mismatch_fn_static.rs +++ b/tests/ui/eii/static/mismatch_fn_static.rs @@ -1,6 +1,6 @@ //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether EIIs work on statics #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/mismatch_mut.rs b/tests/ui/eii/static/mismatch_mut.rs index 87c2c4128aa5e..61c806fb976ca 100644 --- a/tests/ui/eii/static/mismatch_mut.rs +++ b/tests/ui/eii/static/mismatch_mut.rs @@ -1,6 +1,6 @@ //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether EIIs work on statics #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/mismatch_mut2.rs b/tests/ui/eii/static/mismatch_mut2.rs index ab525e418adeb..ff21af5f0d714 100644 --- a/tests/ui/eii/static/mismatch_mut2.rs +++ b/tests/ui/eii/static/mismatch_mut2.rs @@ -1,6 +1,6 @@ //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether EIIs work on statics #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/mismatch_safety.rs b/tests/ui/eii/static/mismatch_safety.rs index f30326b0755c0..b8d503adc2d21 100644 --- a/tests/ui/eii/static/mismatch_safety.rs +++ b/tests/ui/eii/static/mismatch_safety.rs @@ -1,6 +1,6 @@ //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether EIIs work on statics #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/mismatch_safety2.rs b/tests/ui/eii/static/mismatch_safety2.rs index dea45c26292d8..412e2a694c1fc 100644 --- a/tests/ui/eii/static/mismatch_safety2.rs +++ b/tests/ui/eii/static/mismatch_safety2.rs @@ -1,6 +1,6 @@ //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether EIIs work on statics #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/mismatch_static_fn.rs b/tests/ui/eii/static/mismatch_static_fn.rs index cd9a8109dc339..06e3d95b7cdcb 100644 --- a/tests/ui/eii/static/mismatch_static_fn.rs +++ b/tests/ui/eii/static/mismatch_static_fn.rs @@ -1,6 +1,6 @@ //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether EIIs work on statics #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/multiple_impls.rs b/tests/ui/eii/static/multiple_impls.rs index 8ad7d87040a36..1129417b958ca 100644 --- a/tests/ui/eii/static/multiple_impls.rs +++ b/tests/ui/eii/static/multiple_impls.rs @@ -1,6 +1,6 @@ //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether one function could implement two EIIs. #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/mut.rs b/tests/ui/eii/static/mut.rs index 803ffc2297992..4c9d84061fb25 100644 --- a/tests/ui/eii/static/mut.rs +++ b/tests/ui/eii/static/mut.rs @@ -1,6 +1,6 @@ //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether EIIs work on statics #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/same_address.rs b/tests/ui/eii/static/same_address.rs index 81de19406dc49..de316b9f5f9ff 100644 --- a/tests/ui/eii/static/same_address.rs +++ b/tests/ui/eii/static/same_address.rs @@ -1,8 +1,8 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether EIIs and their declarations share the same address #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/simple.rs b/tests/ui/eii/static/simple.rs index 661ab9b9835f2..a592609e9153a 100644 --- a/tests/ui/eii/static/simple.rs +++ b/tests/ui/eii/static/simple.rs @@ -1,8 +1,8 @@ //@ run-pass //@ check-run-results //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests whether EIIs work on statics #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/subtype.rs b/tests/ui/eii/static/subtype.rs index d98e94fa90322..4553d7b8c59c0 100644 --- a/tests/ui/eii/static/subtype.rs +++ b/tests/ui/eii/static/subtype.rs @@ -1,7 +1,7 @@ //@ check-pass //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests that mismatching types of the declaration and definition are rejected #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/subtype_wrong.rs b/tests/ui/eii/static/subtype_wrong.rs index 964a3d767b197..ac975592a0f08 100644 --- a/tests/ui/eii/static/subtype_wrong.rs +++ b/tests/ui/eii/static/subtype_wrong.rs @@ -1,6 +1,6 @@ //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests that mismatching types of the declaration and definition are rejected #![feature(extern_item_impls)] diff --git a/tests/ui/eii/static/wrong_ty.rs b/tests/ui/eii/static/wrong_ty.rs index beee0a5a0857b..40b7859b06b01 100644 --- a/tests/ui/eii/static/wrong_ty.rs +++ b/tests/ui/eii/static/wrong_ty.rs @@ -1,6 +1,6 @@ //@ ignore-backends: gcc -// FIXME: linking on windows (specifically mingw) not yet supported, see tracking issue #125418 -//@ ignore-windows +// FIXME(#125418): linking on Windows GNU targets is not yet supported. +//@ ignore-windows-gnu // Tests that mismatching types of the declaration and definition are rejected #![feature(extern_item_impls)] From 7074f122d9fe04f242ba709bd1fe5f22e877b819 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 10 Jul 2026 12:15:45 +0200 Subject: [PATCH 015/215] Cleanup lint emitting code --- compiler/rustc_hir_typeck/src/method/probe.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index f09f882168caa..2eb3f6360bbe6 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -465,9 +465,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If we encountered an `_` type or an error type during autoderef, this is // ambiguous. if let Some(bad_ty) = &steps.opt_bad_ty { - // Ended up encountering a type variable when doing autoderef, - // but it may not be a type variable after processing obligations - // in our local `FnCtxt`, so don't call `structurally_resolve_type`. + // We care about the opt_bad_ty given the inference state at the point of computing the auto deref chain, + // so we don't call structurally_resolve_type as it processes obligations in our local FnCtxt, + // potentially making inference progress. let ty = &bad_ty.ty; let ty = self .probe_instantiate_query_response(span, &orig_values, ty) @@ -502,7 +502,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // (see https://github.com/rust-lang/rust/issues/143349) } else if let ty::Infer(ty::TyVar(ty_id)) = *ty.kind() && let ty_id = self.sub_unification_table_root_var(ty_id) - && let root_ty = Ty::new_var(self.tcx, ty_id) && self .diverging_type_vars .borrow() @@ -515,6 +514,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { span, TraitMethodOnCoercedNeverType, ); + let root_ty = Ty::new_var(self.tcx, ty_id); self.demand_eqtype(span, root_ty, self.tcx.types.never); } else { let guar = match *ty.kind() { From 544edb8d29694b578e307a4eb80df10d5c9c615c Mon Sep 17 00:00:00 2001 From: Xuhui Zheng <2529677678@qq.com> Date: Fri, 10 Jul 2026 20:35:45 +0800 Subject: [PATCH 016/215] Fix typo in ambig AST positions description Corrected a typo in the explanation of inferred arguments in ambig AST positions. --- src/doc/rustc-dev-guide/src/ambig-unambig-ty-and-consts.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/ambig-unambig-ty-and-consts.md b/src/doc/rustc-dev-guide/src/ambig-unambig-ty-and-consts.md index 33b685861df37..6cb74fbef560f 100644 --- a/src/doc/rustc-dev-guide/src/ambig-unambig-ty-and-consts.md +++ b/src/doc/rustc-dev-guide/src/ambig-unambig-ty-and-consts.md @@ -57,7 +57,7 @@ fn foo() { The only generic arguments which remain ambiguous after lowering are inferred generic arguments (`_`) in path segments. In the above example it is not clear at parse time whether the `_` argument to `Foo` is an inferred type argument, or an inferred const argument. -In ambig AST positions, inferred argumentsd are parsed as an [`ast::GenericArg::Ty`] wrapping a [`ast::Ty::Infer`]. Then during AST lowering when lowering an `ast::GenericArg::Ty` we check if it is an inferred type and if so lower to a [`hir::GenericArg::Infer`]. +In ambig AST positions, inferred arguments are parsed as an [`ast::GenericArg::Ty`] wrapping a [`ast::Ty::Infer`]. Then during AST lowering when lowering an `ast::GenericArg::Ty` we check if it is an inferred type and if so lower to a [`hir::GenericArg::Infer`]. In unambig AST positions, inferred arguments are parsed as either `ast::Ty::Infer` or [`ast::AnonConst`]. The `AnonConst` case is quite strange, we use [`ast::ExprKind::Underscore`] to represent the "body" of the "anon const" although in reality we do not actually lower this to an anon const in the HIR. From be50f898e1e4d0d9f5182a154cdd9c8bb939edfc Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 10 Jul 2026 12:21:26 +0200 Subject: [PATCH 017/215] Change lint name to METHOD_CALL_ON_DIVERGING_INFER_VAR --- compiler/rustc_hir_typeck/src/method/probe.rs | 10 +++--- compiler/rustc_lint_defs/src/builtin.rs | 30 +++++++++++------ .../question-mark-type-inference-in-chain.rs | 6 ++-- ...estion-mark-type-inference-in-chain.stderr | 14 ++++---- tests/ui/never_type/basic/clone-never.rs | 2 +- tests/ui/never_type/basic/clone-never.stderr | 8 ++--- tests/ui/never_type/basic/method-on-never.rs | 10 +++--- .../never_type/basic/method-on-never.stderr | 32 +++++++++---------- 8 files changed, 61 insertions(+), 51 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/method/probe.rs b/compiler/rustc_hir_typeck/src/method/probe.rs index 2eb3f6360bbe6..8aa4b882c364d 100644 --- a/compiler/rustc_hir_typeck/src/method/probe.rs +++ b/compiler/rustc_hir_typeck/src/method/probe.rs @@ -12,7 +12,7 @@ use rustc_hir_analysis::autoderef::{self, Autoderef}; use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TyCtxtInferExt}; use rustc_infer::traits::{ObligationCauseCode, PredicateObligation, query}; -use rustc_lint::builtin::TRAIT_METHOD_ON_COERCED_NEVER_TYPE; +use rustc_lint::builtin::METHOD_CALL_ON_DIVERGING_INFER_VAR; use rustc_macros::Diagnostic; use rustc_middle::middle::stability; use rustc_middle::ty::elaborate::supertrait_def_ids; @@ -396,9 +396,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { struct MissingTypeAnnot; #[derive(Diagnostic)] - #[diag("trait method call on a coerced never type")] + #[diag("method call on a diverging inference variable")] #[help("consider providing a type annotation")] - struct TraitMethodOnCoercedNeverType; + struct MethodCallOnDivergingInferenceVariable; let mut orig_values = OriginalQueryValues::default(); let predefined_opaques_in_body = if self.next_trait_solver() { @@ -509,10 +509,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { .any(|&candidate_id| self.sub_unification_table_root_var(candidate_id) == ty_id) { self.tcx.emit_node_span_lint( - TRAIT_METHOD_ON_COERCED_NEVER_TYPE, + METHOD_CALL_ON_DIVERGING_INFER_VAR, scope_expr_id, span, - TraitMethodOnCoercedNeverType, + MethodCallOnDivergingInferenceVariable, ); let root_ty = Ty::new_var(self.tcx, ty_id); self.demand_eqtype(span, root_ty, self.tcx.types.never); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 9ca9273cc9ff8..99216026b5a30 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -72,6 +72,7 @@ pub mod hardwired { MALFORMED_DIAGNOSTIC_ATTRIBUTES, MALFORMED_DIAGNOSTIC_FORMAT_LITERALS, META_VARIABLE_MISUSE, + METHOD_CALL_ON_DIVERGING_INFER_VAR, MISPLACED_DIAGNOSTIC_ATTRIBUTES, MISSING_ABI, MISSING_UNSAFE_ON_EXTERN, @@ -112,7 +113,6 @@ pub mod hardwired { TEST_UNSTABLE_LINT, TEXT_DIRECTION_CODEPOINT_IN_COMMENT, TEXT_DIRECTION_CODEPOINT_IN_LITERAL, - TRAIT_METHOD_ON_COERCED_NEVER_TYPE, TRIVIAL_CASTS, TRIVIAL_NUMERIC_CASTS, TYVAR_BEHIND_RAW_POINTER, @@ -5581,8 +5581,8 @@ declare_lint! { } declare_lint! { - /// The `trait_method_on_coerced_never_type` lint detects situations in which a never type, which - /// was coerced to any, has a trait method on it. + /// The `method_call_on_diverging_infer_var` lint detects situations in which a method is called on a value resulting from a never-to-any coercion, + /// without necessary information to infer a type for it. /// /// ### Example /// @@ -5597,15 +5597,25 @@ declare_lint! { /// /// ### Explanation /// - /// Calling trait methods on a coerced `!` was previously disallowed for the never type, - /// but it did work for empty enums such as `Infallible` since these don't coerce. - /// This means that changing the definition of `Infallible` to become a type alias to `!` (a long-term goal), - /// would break code that called a trait method on `Infallible`, in such a way that the `!` would coerce. + /// Rust does not generally allow calling methods on values which do not have a known type, + /// such a result of a never-to-any coercion with no type specified. /// - /// Therefore, to aid in the transition of changing `Infallible` to a type alias, this is temporarily allowed with a FCW. - pub TRAIT_METHOD_ON_COERCED_NEVER_TYPE, + /// To aid with transition of code calling methods on `Infallible` after changing `Infallible` to be an alias for `!`, rustc *temporarily* allows such calls. + /// This will (once again) become an error in the future. + /// + /// Thanks to never-to-any coercion you can replace method calls on `!` with the use of the `!` variable, or an `as` cast to an explicit type: + /// + /// ```diff + /// - x.clone() + /// + x + /// ``` + /// ```diff + /// - result.map(|x| x.convert_error())?; + /// + result.map(|x| x as ErrorType)?; + /// ``` + pub METHOD_CALL_ON_DIVERGING_INFER_VAR, Warn, - "detects trait method calls on an coerced never type", + "detects method calls on a result of never-to-any coercion", @future_incompatible = FutureIncompatibleInfo { reason: fcw!(FutureReleaseError #156047), report_in_deps: true, diff --git a/tests/ui/inference/question-mark-type-inference-in-chain.rs b/tests/ui/inference/question-mark-type-inference-in-chain.rs index e3a613cc21295..68960ec466dd3 100644 --- a/tests/ui/inference/question-mark-type-inference-in-chain.rs +++ b/tests/ui/inference/question-mark-type-inference-in-chain.rs @@ -33,10 +33,10 @@ pub fn error1(lines: &[&str]) -> Result> { let mut tags = lines.iter().map(|e| parse(e)).collect()?; tags.sort(); - //~^ WARN trait method call on a coerced never type + //~^ WARN method call on a diverging inference variable //~| WARN previously accepted //~| NOTE for more information, see issue - //~| NOTE `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + //~| NOTE `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default //~| ERROR no method named `sort` found for type `!` in the current scope [E0599] //~| HELP consider providing a type annotation //~| NOTE method not found in `!` @@ -64,7 +64,7 @@ pub fn error3(lines: &[&str]) -> Result> { //~| NOTE: in this expansion of desugaring of operator `?` //~| NOTE: in this expansion of desugaring of operator `?` tags.sort(); - //~^ WARN trait method call on a coerced never type + //~^ WARN method call on a diverging inference variable //~| WARN previously accepted //~| NOTE for more information, see issue //~| ERROR no method named `sort` found for type `!` in the current scope [E0599] diff --git a/tests/ui/inference/question-mark-type-inference-in-chain.stderr b/tests/ui/inference/question-mark-type-inference-in-chain.stderr index 21ff6df87f9de..3c5ca3b6875fb 100644 --- a/tests/ui/inference/question-mark-type-inference-in-chain.stderr +++ b/tests/ui/inference/question-mark-type-inference-in-chain.stderr @@ -1,4 +1,4 @@ -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/question-mark-type-inference-in-chain.rs:35:10 | LL | tags.sort(); @@ -7,7 +7,7 @@ LL | tags.sort(); = help: consider providing a type annotation = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 - = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default error[E0599]: no method named `sort` found for type `!` in the current scope --> $DIR/question-mark-type-inference-in-chain.rs:35:10 @@ -37,7 +37,7 @@ LL | let mut tags = lines.iter().map(|e| parse(e)).collect::>()?; | = help: the nightly-only, unstable trait `Try` is not implemented for `Vec>` -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/question-mark-type-inference-in-chain.rs:66:10 | LL | tags.sort(); @@ -84,7 +84,7 @@ error: aborting due to 5 previous errors; 2 warnings emitted Some errors have detailed explanations: E0277, E0283, E0599. For more information about an error, try `rustc --explain E0277`. Future incompatibility report: Future breakage diagnostic: -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/question-mark-type-inference-in-chain.rs:35:10 | LL | tags.sort(); @@ -93,10 +93,10 @@ LL | tags.sort(); = help: consider providing a type annotation = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 - = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default Future breakage diagnostic: -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/question-mark-type-inference-in-chain.rs:66:10 | LL | tags.sort(); @@ -105,5 +105,5 @@ LL | tags.sort(); = help: consider providing a type annotation = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 - = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default diff --git a/tests/ui/never_type/basic/clone-never.rs b/tests/ui/never_type/basic/clone-never.rs index 3466a4c78c026..6da3ac76a881f 100644 --- a/tests/ui/never_type/basic/clone-never.rs +++ b/tests/ui/never_type/basic/clone-never.rs @@ -4,6 +4,6 @@ fn main() { let x = panic!(); x.clone(); - //~^ WARN [trait_method_on_coerced_never_type] + //~^ WARN [method_call_on_diverging_infer_var] //~| WARN previously accepted } diff --git a/tests/ui/never_type/basic/clone-never.stderr b/tests/ui/never_type/basic/clone-never.stderr index ed726bd48f11d..2c4229c4d2eff 100644 --- a/tests/ui/never_type/basic/clone-never.stderr +++ b/tests/ui/never_type/basic/clone-never.stderr @@ -1,4 +1,4 @@ -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/clone-never.rs:6:7 | LL | x.clone(); @@ -7,12 +7,12 @@ LL | x.clone(); = help: consider providing a type annotation = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 - = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default warning: 1 warning emitted Future incompatibility report: Future breakage diagnostic: -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/clone-never.rs:6:7 | LL | x.clone(); @@ -21,5 +21,5 @@ LL | x.clone(); = help: consider providing a type annotation = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 - = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default diff --git a/tests/ui/never_type/basic/method-on-never.rs b/tests/ui/never_type/basic/method-on-never.rs index 9e430ed678512..88cd0d7f31ce8 100644 --- a/tests/ui/never_type/basic/method-on-never.rs +++ b/tests/ui/never_type/basic/method-on-never.rs @@ -40,27 +40,27 @@ fn temp() -> Result { todo!() } fn main() -> Result<(), ()> { let x = loop {}; x.method(); - //~^ WARN [trait_method_on_coerced_never_type] + //~^ WARN [method_call_on_diverging_infer_var] //~| WARN previously accepted { loop {} }.method(); - //~^ WARN [trait_method_on_coerced_never_type] + //~^ WARN [method_call_on_diverging_infer_var] //~| WARN previously accepted let e = match loop {} { y => y.method(), - //~^ WARN [trait_method_on_coerced_never_type] + //~^ WARN [method_call_on_diverging_infer_var] //~| WARN previously accepted }; let error = match loop {} { error => (&error).anyhow_kind().new(error), - //~^ WARN [trait_method_on_coerced_never_type] + //~^ WARN [method_call_on_diverging_infer_var] //~| WARN previously accepted }; let res = temp()?; res.method(); - //~^ WARN [trait_method_on_coerced_never_type] + //~^ WARN [method_call_on_diverging_infer_var] //~| WARN previously accepted } diff --git a/tests/ui/never_type/basic/method-on-never.stderr b/tests/ui/never_type/basic/method-on-never.stderr index 977439bed38e9..f5903616be6f4 100644 --- a/tests/ui/never_type/basic/method-on-never.stderr +++ b/tests/ui/never_type/basic/method-on-never.stderr @@ -1,4 +1,4 @@ -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/method-on-never.rs:42:7 | LL | x.method(); @@ -7,9 +7,9 @@ LL | x.method(); = help: consider providing a type annotation = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 - = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/method-on-never.rs:46:17 | LL | { loop {} }.method(); @@ -19,7 +19,7 @@ LL | { loop {} }.method(); = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/method-on-never.rs:51:16 | LL | y => y.method(), @@ -29,7 +29,7 @@ LL | y => y.method(), = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/method-on-never.rs:57:27 | LL | error => (&error).anyhow_kind().new(error), @@ -39,7 +39,7 @@ LL | error => (&error).anyhow_kind().new(error), = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/method-on-never.rs:63:9 | LL | res.method(); @@ -52,7 +52,7 @@ LL | res.method(); warning: 5 warnings emitted Future incompatibility report: Future breakage diagnostic: -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/method-on-never.rs:42:7 | LL | x.method(); @@ -61,10 +61,10 @@ LL | x.method(); = help: consider providing a type annotation = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 - = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default Future breakage diagnostic: -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/method-on-never.rs:46:17 | LL | { loop {} }.method(); @@ -73,10 +73,10 @@ LL | { loop {} }.method(); = help: consider providing a type annotation = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 - = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default Future breakage diagnostic: -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/method-on-never.rs:51:16 | LL | y => y.method(), @@ -85,10 +85,10 @@ LL | y => y.method(), = help: consider providing a type annotation = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 - = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default Future breakage diagnostic: -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/method-on-never.rs:57:27 | LL | error => (&error).anyhow_kind().new(error), @@ -97,10 +97,10 @@ LL | error => (&error).anyhow_kind().new(error), = help: consider providing a type annotation = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 - = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default Future breakage diagnostic: -warning: trait method call on a coerced never type +warning: method call on a diverging inference variable --> $DIR/method-on-never.rs:63:9 | LL | res.method(); @@ -109,5 +109,5 @@ LL | res.method(); = help: consider providing a type annotation = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #156047 - = note: `#[warn(trait_method_on_coerced_never_type)]` (part of `#[warn(future_incompatible)]`) on by default + = note: `#[warn(method_call_on_diverging_infer_var)]` (part of `#[warn(future_incompatible)]`) on by default From cddad246a891890f9281403ba36ca33d82dd83f0 Mon Sep 17 00:00:00 2001 From: Urgau Date: Fri, 26 Jun 2026 19:10:51 +0200 Subject: [PATCH 018/215] Lint against invalid POSIX function definitions --- compiler/rustc_hir/src/lang_items.rs | 8 ++ compiler/rustc_hir/src/weak_lang_items.rs | 8 ++ compiler/rustc_lint/src/runtime_symbols.rs | 20 +++- compiler/rustc_span/src/symbol.rs | 8 ++ library/std/src/lib.rs | 1 + library/std/src/sys/alloc/unix.rs | 19 ++++ library/std/src/sys/exit.rs | 19 ++++ library/std/src/sys/fs/unix.rs | 22 ++++ tests/ui/lint/runtime-symbols-no-std.rs | 27 +++++ tests/ui/lint/runtime-symbols-unix.rs | 57 ++++++++++ tests/ui/lint/runtime-symbols-unix.stderr | 117 +++++++++++++++++++++ 11 files changed, 302 insertions(+), 4 deletions(-) create mode 100644 tests/ui/lint/runtime-symbols-no-std.rs create mode 100644 tests/ui/lint/runtime-symbols-unix.rs create mode 100644 tests/ui/lint/runtime-symbols-unix.stderr diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index c7c37d30dd546..0ab9782c69356 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -461,6 +461,14 @@ language_item_table! { MemCmp, sym::memcmp_fn, memcmp_fn, Target::ForeignFn, GenericRequirement::None; Bcmp, sym::bcmp_fn, bcmp_fn, Target::ForeignFn, GenericRequirement::None; StrLen, sym::strlen_fn, strlen_fn, Target::ForeignFn, GenericRequirement::None; + Open, sym::open_fn, open_fn, Target::ForeignFn, GenericRequirement::None; + Read, sym::read_fn, read_fn, Target::ForeignFn, GenericRequirement::None; + Write, sym::write_fn, write_fn, Target::ForeignFn, GenericRequirement::None; + Close, sym::close_fn, close_fn, Target::ForeignFn, GenericRequirement::None; + Malloc, sym::malloc_fn, malloc_fn, Target::ForeignFn, GenericRequirement::None; + Realloc, sym::realloc_fn, realloc_fn, Target::ForeignFn, GenericRequirement::None; + Free, sym::free_fn, free_fn, Target::ForeignFn, GenericRequirement::None; + Exit, sym::exit_fn, exit_fn, Target::ForeignFn, GenericRequirement::None; } /// The requirement imposed on the generics of a lang item diff --git a/compiler/rustc_hir/src/weak_lang_items.rs b/compiler/rustc_hir/src/weak_lang_items.rs index a2541089a530b..be3e2424d1782 100644 --- a/compiler/rustc_hir/src/weak_lang_items.rs +++ b/compiler/rustc_hir/src/weak_lang_items.rs @@ -45,4 +45,12 @@ weak_only_lang_items! { MemCmp, Bcmp, StrLen, + Open, + Read, + Write, + Close, + Malloc, + Realloc, + Free, + Exit, } diff --git a/compiler/rustc_lint/src/runtime_symbols.rs b/compiler/rustc_lint/src/runtime_symbols.rs index a583ee70074b6..d1a7bec10db12 100644 --- a/compiler/rustc_lint/src/runtime_symbols.rs +++ b/compiler/rustc_lint/src/runtime_symbols.rs @@ -11,7 +11,7 @@ use crate::{LateContext, LateLintPass, LintContext}; declare_lint! { /// The `invalid_runtime_symbol_definitions` lint checks the signature of items whose - /// symbol name is a runtime symbol expected by `core` differs significantly from the + /// symbol name is a runtime symbol expected by `core` or `std` differs significantly from the /// expected signature (like mismatch ABI, mismatch C variadics, mismatch argument count, /// missing return type, ...). /// @@ -32,7 +32,8 @@ declare_lint! { /// standard-library facility or undefined behavior may occur. /// /// The symbols currently checked are `memcpy`, `memmove`, `memset`, `memcmp`, - /// `bcmp` and `strlen`. + /// `bcmp`, `strlen`, as well as the following POSIX symbols: `open`, `read`, `write` + /// `close`, `malloc`, `realloc`, `free` and `exit`. /// /// [^1]: https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library pub INVALID_RUNTIME_SYMBOL_DEFINITIONS, @@ -42,7 +43,7 @@ declare_lint! { declare_lint! { /// The `suspicious_runtime_symbol_definitions` lint checks the signature of items whose - /// symbol name is a runtime symbol expected by `core`. + /// symbol name is a runtime symbol expected by `core` or `std`. /// /// ### Example /// @@ -62,7 +63,8 @@ declare_lint! { /// standard-library facility or undefined behavior may occur. /// /// The symbols currently checked are `memcpy`, `memmove`, `memset`, `memcmp`, - /// `bcmp` and `strlen`. + /// `bcmp`, `strlen`, as well as the following POSIX symbols: `open`, `read`, `write` + /// `close`, `malloc`, `realloc`, `free` and `exit`. /// /// [^1]: https://doc.rust-lang.org/core/index.html#how-to-use-the-core-library pub SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS, @@ -73,12 +75,22 @@ declare_lint! { declare_lint_pass!(RuntimeSymbols => [INVALID_RUNTIME_SYMBOL_DEFINITIONS, SUSPICIOUS_RUNTIME_SYMBOL_DEFINITIONS]); static EXPECTED_SYMBOLS: &[ExpectedSymbol] = &[ + // `core` symbols ExpectedSymbol { symbol: "memcpy", lang: LanguageItems::memcpy_fn }, ExpectedSymbol { symbol: "memmove", lang: LanguageItems::memmove_fn }, ExpectedSymbol { symbol: "memset", lang: LanguageItems::memset_fn }, ExpectedSymbol { symbol: "memcmp", lang: LanguageItems::memcmp_fn }, ExpectedSymbol { symbol: "bcmp", lang: LanguageItems::bcmp_fn }, ExpectedSymbol { symbol: "strlen", lang: LanguageItems::strlen_fn }, + // POSIX symbols + ExpectedSymbol { symbol: "open", lang: LanguageItems::open_fn }, + ExpectedSymbol { symbol: "read", lang: LanguageItems::read_fn }, + ExpectedSymbol { symbol: "write", lang: LanguageItems::write_fn }, + ExpectedSymbol { symbol: "close", lang: LanguageItems::close_fn }, + ExpectedSymbol { symbol: "malloc", lang: LanguageItems::malloc_fn }, + ExpectedSymbol { symbol: "realloc", lang: LanguageItems::realloc_fn }, + ExpectedSymbol { symbol: "free", lang: LanguageItems::free_fn }, + ExpectedSymbol { symbol: "exit", lang: LanguageItems::exit_fn }, ]; #[derive(Copy, Clone, Debug)] diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 9ab27cd1a4918..ab29937f13769 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -616,6 +616,7 @@ symbols! { clone_closures, clone_fn, clone_from, + close_fn, closure, closure_lifetime_binder, closure_to_fn_coercion, @@ -900,6 +901,7 @@ symbols! { exhaustive_integer_patterns, exhaustive_patterns, existential_type, + exit_fn, exp2f16, exp2f32, exp2f64, @@ -1011,6 +1013,7 @@ symbols! { format_unsafe_arg, fp, framework, + free_fn, freeze, freeze_impls, freg, @@ -1252,6 +1255,7 @@ symbols! { macro_vis_matcher, macros_in_extern, main, + malloc_fn, managed_boxes, manually_drop, map, @@ -1477,6 +1481,7 @@ symbols! { on_unmatched_args, opaque, opaque_module_name_placeholder: "", + open_fn, ops, opt_out_copy, optimize, @@ -1661,9 +1666,11 @@ symbols! { raw_identifiers, raw_ref_op, re_rebalance_coherence, + read_fn, read_via_copy, readonly, realloc, + realloc_fn, realtime, reason, reborrow, @@ -2368,6 +2375,7 @@ symbols! { write_box_via_move, write_bytes, write_fmt, + write_fn, write_macro, write_str, write_via_move, diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 9c5b487d1cbf0..66d9c9da27180 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -322,6 +322,7 @@ #![feature(borrowed_buf_init)] #![feature(bstr)] #![feature(bstr_internals)] +#![feature(c_size_t)] #![feature(can_vector)] #![feature(cast_maybe_uninit)] #![feature(char_internals)] diff --git a/library/std/src/sys/alloc/unix.rs b/library/std/src/sys/alloc/unix.rs index d6de8bf28c44c..b9c5dfd24396f 100644 --- a/library/std/src/sys/alloc/unix.rs +++ b/library/std/src/sys/alloc/unix.rs @@ -2,6 +2,25 @@ use super::{MIN_ALIGN, realloc_fallback}; use crate::alloc::Layout; use crate::ptr; +// Used by rustc for checking the definitions of other function with the same symbol names +// +// See the `invalid_runtime_symbols_definitions` lint. +#[cfg(not(test))] +mod runtime_symbols { + use core::ffi::{c_size_t, c_void}; + + unsafe extern "C" { + #[lang = "malloc_fn"] + fn malloc(size: c_size_t) -> *mut c_void; + + #[lang = "realloc_fn"] + fn realloc(ptr: *mut c_void, size: c_size_t) -> *mut c_void; + + #[lang = "free_fn"] + fn free(ptr: *mut c_void); + } +} + #[inline] pub unsafe fn alloc(layout: Layout) -> *mut u8 { // jemalloc provides alignment less than MIN_ALIGN for small allocations. diff --git a/library/std/src/sys/exit.rs b/library/std/src/sys/exit.rs index 53fb92ba077e0..c4fa3af803071 100644 --- a/library/std/src/sys/exit.rs +++ b/library/std/src/sys/exit.rs @@ -68,6 +68,25 @@ cfg_select! { } } +#[cfg(not(test))] +cfg_select! { + any( + target_family = "unix", + target_os = "wasi", + ) => { + // Used by rustc for checking the definitions of other function with the same symbol names + // + // See the `invalid_runtime_symbols_definitions` lint. + mod runtime_symbols { + unsafe extern "C" { + #[lang = "exit_fn"] + fn exit(status: core::ffi::c_int) -> !; + } + } + } + _ => {} +} + pub fn exit(code: i32) -> ! { cfg_select! { target_os = "hermit" => { diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 7283e8f710c55..cadbf1a21aa8f 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -102,6 +102,28 @@ use crate::sys::weak::weak; use crate::sys::{AsInner, AsInnerMut, FromInner, IntoInner, cvt, cvt_r}; use crate::{mem, ptr}; +// Used by rustc for checking the definitions of other function with the same symbol names +// +// See the `invalid_runtime_symbols_definitions` lint. +#[cfg(not(test))] +mod runtime_symbols { + use core::ffi::{c_char, c_int, c_size_t, c_ssize_t, c_void}; + + unsafe extern "C" { + #[lang = "open_fn"] + fn open(pathname: *const c_char, flags: c_int, ...) -> c_int; + + #[lang = "read_fn"] + fn read(fd: c_int, buf: *mut c_void, count: c_size_t) -> c_ssize_t; + + #[lang = "write_fn"] + fn write(fd: c_int, buf: *const c_void, count: c_size_t) -> c_ssize_t; + + #[lang = "close_fn"] + fn close(fd: c_int) -> c_int; + } +} + pub struct File(FileDesc); // FIXME: This should be available on Linux with all `target_env`. diff --git a/tests/ui/lint/runtime-symbols-no-std.rs b/tests/ui/lint/runtime-symbols-no-std.rs new file mode 100644 index 0000000000000..cef75e45b02d3 --- /dev/null +++ b/tests/ui/lint/runtime-symbols-no-std.rs @@ -0,0 +1,27 @@ +// This test makes sure that the following symbols are not linted against +// in a #[no_std] context. + +//@ check-pass + +#![no_std] +#![crate_type = "lib"] + +use core::ffi::{c_char, c_int, c_void}; + +#[no_mangle] +pub fn open() {} + +extern "C" { + pub fn read(); + pub fn write(); +} + +#[no_mangle] +pub static close: () = (); + +extern "C" { + pub fn malloc(); + pub fn realloc(); + pub fn free(); + pub fn exit(); +} diff --git a/tests/ui/lint/runtime-symbols-unix.rs b/tests/ui/lint/runtime-symbols-unix.rs new file mode 100644 index 0000000000000..517cf765195f3 --- /dev/null +++ b/tests/ui/lint/runtime-symbols-unix.rs @@ -0,0 +1,57 @@ +// This test checks the runtime symbols lint with the Unix symbols. + +//@ only-unix +//@ edition: 2021 +//@ normalize-stderr: "\*const [iu]8" -> "*const U8" + +#![feature(c_variadic)] +#![allow(clashing_extern_declarations)] // we are voluntarily testing different definitions + +use core::ffi::{c_char, c_int, c_void}; + +fn invalid() { + #[no_mangle] + pub fn open() {} + //~^ ERROR invalid definition of the runtime `open` symbol + + extern "C" { + pub fn read(); + //~^ ERROR invalid definition of the runtime `read` symbol + + pub fn write(); + //~^ ERROR invalid definition of the runtime `write` symbol + } + + #[no_mangle] + pub static close: () = (); + //~^ ERROR invalid definition of the runtime `close` symbol + + extern "C" { + pub fn malloc(); + //~^ ERROR invalid definition of the runtime `malloc` symbol + + pub fn realloc(); + //~^ ERROR invalid definition of the runtime `realloc` symbol + + pub fn free(); + //~^ ERROR invalid definition of the runtime `free` symbol + + pub fn exit(); + //~^ ERROR invalid definition of the runtime `exit` symbol + } +} + +fn suspicious() { + extern "C" { + pub fn open(path: *const u8, oflag: usize, ...) -> c_int; + //~^ WARN suspicious definition of the runtime `open` symbol + + pub fn free(ptr: *const u8); + //~^ WARN suspicious definition of the runtime `free` symbol + + pub fn exit(code: f32) -> !; + //~^ WARN suspicious definition of the runtime `exit` symbol + } +} + +fn main() {} diff --git a/tests/ui/lint/runtime-symbols-unix.stderr b/tests/ui/lint/runtime-symbols-unix.stderr new file mode 100644 index 0000000000000..7ef765c29684d --- /dev/null +++ b/tests/ui/lint/runtime-symbols-unix.stderr @@ -0,0 +1,117 @@ +error: invalid definition of the runtime `open` symbol used by the standard library + --> $DIR/runtime-symbols-unix.rs:14:5 + | +LL | pub fn open() {} + | ^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const U8, i32, ...) -> i32` + found `fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "open")]`, or `#[link_name = "open"]` + = note: `#[deny(invalid_runtime_symbol_definitions)]` on by default + +error: invalid definition of the runtime `read` symbol used by the standard library + --> $DIR/runtime-symbols-unix.rs:18:9 + | +LL | pub fn read(); + | ^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(i32, *mut c_void, usize) -> isize` + found `unsafe extern "C" fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "read")]`, or `#[link_name = "read"]` + +error: invalid definition of the runtime `write` symbol used by the standard library + --> $DIR/runtime-symbols-unix.rs:21:9 + | +LL | pub fn write(); + | ^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(i32, *const c_void, usize) -> isize` + found `unsafe extern "C" fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "write")]`, or `#[link_name = "write"]` + +error: invalid definition of the runtime `close` symbol used by the standard library + --> $DIR/runtime-symbols-unix.rs:26:5 + | +LL | pub static close: () = (); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(i32) -> i32` + found `static close: ()` + = help: either fix the signature or remove any attributes `#[unsafe(no_mangle)]` or `#[unsafe(export_name = "close")]` + +error: invalid definition of the runtime `malloc` symbol used by the standard library + --> $DIR/runtime-symbols-unix.rs:30:9 + | +LL | pub fn malloc(); + | ^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(usize) -> *mut c_void` + found `unsafe extern "C" fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "malloc")]`, or `#[link_name = "malloc"]` + +error: invalid definition of the runtime `realloc` symbol used by the standard library + --> $DIR/runtime-symbols-unix.rs:33:9 + | +LL | pub fn realloc(); + | ^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void, usize) -> *mut c_void` + found `unsafe extern "C" fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "realloc")]`, or `#[link_name = "realloc"]` + +error: invalid definition of the runtime `free` symbol used by the standard library + --> $DIR/runtime-symbols-unix.rs:36:9 + | +LL | pub fn free(); + | ^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void)` + found `unsafe extern "C" fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "free")]`, or `#[link_name = "free"]` + +error: invalid definition of the runtime `exit` symbol used by the standard library + --> $DIR/runtime-symbols-unix.rs:39:9 + | +LL | pub fn exit(); + | ^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(i32) -> !` + found `unsafe extern "C" fn()` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "exit")]`, or `#[link_name = "exit"]` + +warning: suspicious definition of the runtime `open` symbol used by the standard library + --> $DIR/runtime-symbols-unix.rs:46:9 + | +LL | pub fn open(path: *const U8, oflag: usize, ...) -> c_int; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*const U8, i32, ...) -> i32` + found `unsafe extern "C" fn(*const U8, usize, ...) -> i32` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "open")]`, or `#[link_name = "open"]` + = help: allow this lint if the signature is compatible + = note: `#[warn(suspicious_runtime_symbol_definitions)]` on by default + +warning: suspicious definition of the runtime `free` symbol used by the standard library + --> $DIR/runtime-symbols-unix.rs:49:9 + | +LL | pub fn free(ptr: *const U8); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(*mut c_void)` + found `unsafe extern "C" fn(*const U8)` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "free")]`, or `#[link_name = "free"]` + = help: allow this lint if the signature is compatible + +warning: suspicious definition of the runtime `exit` symbol used by the standard library + --> $DIR/runtime-symbols-unix.rs:52:9 + | +LL | pub fn exit(code: f32) -> !; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: expected `unsafe extern "C" fn(i32) -> !` + found `unsafe extern "C" fn(f32) -> !` + = help: either fix the signature or remove any attributes like `#[unsafe(no_mangle)]`, `#[unsafe(export_name = "exit")]`, or `#[link_name = "exit"]` + = help: allow this lint if the signature is compatible + +error: aborting due to 8 previous errors; 3 warnings emitted + From 86db662d0ebe1b6d8da5da9e9652abb5fff39f59 Mon Sep 17 00:00:00 2001 From: Urgau Date: Sat, 27 Jun 2026 12:43:27 +0200 Subject: [PATCH 019/215] Fix UI tests for invalid POSIX function definitions --- tests/ui/abi/stack-protector.rs | 11 ++++++----- tests/ui/cfg/conditional-compile.rs | 8 ++++---- tests/ui/extern/extern-pub.rs | 2 +- tests/ui/ffi/ffi-struct-size-alignment.rs | 3 +-- tests/ui/lint/dead-code/lint-dead-code-3.rs | 14 +++++++++----- tests/ui/lint/dead-code/lint-dead-code-3.stderr | 14 +++++++------- tests/ui/lint/warn-ctypes-inhibit.rs | 4 ++-- tests/ui/privacy/pub-extern-privacy.rs | 3 +-- 8 files changed, 31 insertions(+), 28 deletions(-) diff --git a/tests/ui/abi/stack-protector.rs b/tests/ui/abi/stack-protector.rs index dd0d0d43182ea..6a2b0d0854c4a 100644 --- a/tests/ui/abi/stack-protector.rs +++ b/tests/ui/abi/stack-protector.rs @@ -9,6 +9,7 @@ #![allow(function_casts_as_integer)] use std::env; +use std::ffi::c_void; use std::process::{Command, ExitStatus}; fn main() { @@ -42,7 +43,9 @@ fn vulnerable_function() { let bad_code_ptr = malicious_code as usize; // Overwrite the on-stack return address with the address of `malicious_code()`, // thereby jumping to that function when returning from `vulnerable_function()`. - unsafe { fill(stackaddr, bad_code_ptr, 20); } + unsafe { + fill(stackaddr, bad_code_ptr, 20); + } // Capture the address, so the write is not optimized away. std::hint::black_box(stackaddr); } @@ -68,16 +71,15 @@ unsafe fn fill(addr: *mut usize, val: usize, count: usize) { fn malicious_code() { let msg = [112u8, 119u8, 110u8, 101u8, 100u8, 33u8, 0u8]; // "pwned!\0" ascii unsafe { - write(1, &msg as *const u8, msg.len()); + write(1, &msg as *const u8 as *const c_void, msg.len()); _exit(0); } } extern "C" { - fn write(fd: i32, buf: *const u8, count: usize) -> isize; + fn write(fd: i32, buf: *const c_void, count: usize) -> isize; fn _exit(status: i32) -> !; } - fn assert_stack_smash_prevented(cmd: &mut Command) { let (status, stdout, stderr) = run(cmd); assert!(!status.success()); @@ -92,7 +94,6 @@ fn assert_stack_smashed(cmd: &mut Command) { assert!(stderr.is_empty()); } - fn run(cmd: &mut Command) -> (ExitStatus, String, String) { let output = cmd.output().unwrap(); let stdout = String::from_utf8_lossy(&output.stdout); diff --git a/tests/ui/cfg/conditional-compile.rs b/tests/ui/cfg/conditional-compile.rs index 637bd0e12e8be..3cdd1d52f8881 100644 --- a/tests/ui/cfg/conditional-compile.rs +++ b/tests/ui/cfg/conditional-compile.rs @@ -47,7 +47,7 @@ struct r { #[cfg(false)] fn r(i: isize) -> r { - r { i: i } + r { i } } struct r { @@ -55,7 +55,7 @@ struct r { } fn r(i: isize) -> r { - r { i: i } + r { i } } #[cfg(false)] @@ -111,8 +111,8 @@ mod test_foreign_items { pub mod rustrt { extern "C" { #[cfg(false)] - pub fn write() -> String; - pub fn write() -> String; + pub fn foo() -> String; + pub fn foo() -> String; } } } diff --git a/tests/ui/extern/extern-pub.rs b/tests/ui/extern/extern-pub.rs index b272bc5359fd0..9e91ca7f3324f 100644 --- a/tests/ui/extern/extern-pub.rs +++ b/tests/ui/extern/extern-pub.rs @@ -1,7 +1,7 @@ //@ run-pass extern "C" { - pub fn free(p: *const u8); + pub fn free(p: *mut std::ffi::c_void); } pub fn main() {} diff --git a/tests/ui/ffi/ffi-struct-size-alignment.rs b/tests/ui/ffi/ffi-struct-size-alignment.rs index 15ad3232555bb..287ae7cad2b6d 100644 --- a/tests/ui/ffi/ffi-struct-size-alignment.rs +++ b/tests/ui/ffi/ffi-struct-size-alignment.rs @@ -5,7 +5,6 @@ // Incorrect struct size computation in the FFI, because of not taking // the alignment of elements into account. - use std::ffi::{c_uint, c_void}; pub struct KEYGEN { @@ -17,7 +16,7 @@ pub struct KEYGEN { extern "C" { // Bogus signature, just need to test if it compiles. - pub fn malloc(data: KEYGEN); + pub fn foo(data: KEYGEN); } pub fn main() {} diff --git a/tests/ui/lint/dead-code/lint-dead-code-3.rs b/tests/ui/lint/dead-code/lint-dead-code-3.rs index 20b568054dfb7..7e733fc11c1db 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-3.rs +++ b/tests/ui/lint/dead-code/lint-dead-code-3.rs @@ -48,24 +48,28 @@ mod blah { enum c_void {} extern "C" { - fn free(p: *const c_void); - fn malloc(size: usize) -> *const c_void; + fn my_free(p: *const c_void); + fn my_malloc(size: usize) -> *const c_void; } pub fn baz() { - unsafe { free(malloc(4)); } + unsafe { + my_free(my_malloc(4)); + } } } enum c_void {} //~ ERROR: enum `c_void` is never used extern "C" { - fn free(p: *const c_void); //~ ERROR: function `free` is never used + fn my_free(p: *const c_void); //~ ERROR: function `my_free` is never used } // Check provided method mod inner { pub trait Trait { - fn f(&self) { f(); } + fn f(&self) { + f(); + } } impl Trait for isize {} diff --git a/tests/ui/lint/dead-code/lint-dead-code-3.stderr b/tests/ui/lint/dead-code/lint-dead-code-3.stderr index 5c68cf0e18b67..acc5de4a93786 100644 --- a/tests/ui/lint/dead-code/lint-dead-code-3.stderr +++ b/tests/ui/lint/dead-code/lint-dead-code-3.stderr @@ -25,28 +25,28 @@ LL | fn bar() { | ^^^ error: enum `c_void` is never used - --> $DIR/lint-dead-code-3.rs:60:6 + --> $DIR/lint-dead-code-3.rs:62:6 | LL | enum c_void {} | ^^^^^^ error: function `blah` is never used - --> $DIR/lint-dead-code-3.rs:77:8 + --> $DIR/lint-dead-code-3.rs:81:8 | LL | fn blah() {} | ^^^^ error: function `blah` is never used - --> $DIR/lint-dead-code-3.rs:81:12 + --> $DIR/lint-dead-code-3.rs:85:12 | LL | fn blah() {} | ^^^^ -error: function `free` is never used - --> $DIR/lint-dead-code-3.rs:62:8 +error: function `my_free` is never used + --> $DIR/lint-dead-code-3.rs:64:8 | -LL | fn free(p: *const c_void); - | ^^^^ +LL | fn my_free(p: *const c_void); + | ^^^^^^^ error: aborting due to 7 previous errors diff --git a/tests/ui/lint/warn-ctypes-inhibit.rs b/tests/ui/lint/warn-ctypes-inhibit.rs index 0f401150adf64..1de661a6994e6 100644 --- a/tests/ui/lint/warn-ctypes-inhibit.rs +++ b/tests/ui/lint/warn-ctypes-inhibit.rs @@ -1,9 +1,9 @@ //@ run-pass - -#![allow(dead_code)] //@ compile-flags:-D improper-ctypes +#![allow(dead_code)] #![allow(improper_ctypes)] +#![allow(suspicious_runtime_symbol_definitions)] mod libc { extern "C" { diff --git a/tests/ui/privacy/pub-extern-privacy.rs b/tests/ui/privacy/pub-extern-privacy.rs index 0f9131685b0e2..e01d86acf6547 100644 --- a/tests/ui/privacy/pub-extern-privacy.rs +++ b/tests/ui/privacy/pub-extern-privacy.rs @@ -1,11 +1,10 @@ //@ run-pass - use std::mem::transmute; mod a { extern "C" { - pub fn free(x: *const u8); + pub fn free(x: *mut std::ffi::c_void); } } From 676a82f223cc4d7f841c15486f8586238b6ae9ce Mon Sep 17 00:00:00 2001 From: Urgau Date: Sun, 28 Jun 2026 16:57:07 +0200 Subject: [PATCH 020/215] Fix Miri fail tests by allowing the lints --- src/tools/miri/tests/fail/function_calls/check_arg_abi.rs | 2 ++ .../tests/fail/function_calls/check_arg_count_too_few_args.rs | 2 ++ .../tests/fail/function_calls/check_arg_count_too_many_args.rs | 2 ++ .../tests/fail/function_calls/exported_symbol_shim_clashing.rs | 2 ++ src/tools/miri/tests/fail/shims/input_arg_mismatch.rs | 3 +++ .../miri/tests/fail/shims/non_vararg_signature_mismatch.rs | 3 +++ src/tools/miri/tests/fail/shims/return_type_mismatch.rs | 3 +++ src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs | 3 +++ 8 files changed, 20 insertions(+) diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs b/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs index 0e91636416903..e36b516887962 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs +++ b/src/tools/miri/tests/fail/function_calls/check_arg_abi.rs @@ -1,3 +1,5 @@ +#![allow(invalid_runtime_symbol_definitions)] + fn main() { extern "Rust" { fn malloc(size: usize) -> *mut std::ffi::c_void; diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.rs b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.rs index 41aebea2d8add..ecdda9e509d4e 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.rs +++ b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_few_args.rs @@ -1,3 +1,5 @@ +#![allow(invalid_runtime_symbol_definitions)] + fn main() { extern "C" { fn malloc() -> *mut std::ffi::c_void; diff --git a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.rs b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.rs index 1f5c509c6666f..1d3fec0fe32f8 100644 --- a/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.rs +++ b/src/tools/miri/tests/fail/function_calls/check_arg_count_too_many_args.rs @@ -1,3 +1,5 @@ +#![allow(invalid_runtime_symbol_definitions)] + fn main() { extern "C" { fn malloc(_: i32, _: i32) -> *mut std::ffi::c_void; diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_shim_clashing.rs b/src/tools/miri/tests/fail/function_calls/exported_symbol_shim_clashing.rs index dffae7adbb972..6260d55b8f553 100644 --- a/src/tools/miri/tests/fail/function_calls/exported_symbol_shim_clashing.rs +++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_shim_clashing.rs @@ -1,3 +1,5 @@ +#![allow(suspicious_runtime_symbol_definitions)] + #[no_mangle] extern "C" fn malloc(_: usize) -> *mut std::ffi::c_void { //~^ HELP: the `malloc` symbol is defined here diff --git a/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs b/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs index 77699776aea66..d355da73174e9 100644 --- a/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs +++ b/src/tools/miri/tests/fail/shims/input_arg_mismatch.rs @@ -1,5 +1,8 @@ //@ignore-target: windows # File handling is not implemented yet //@compile-flags: -Zmiri-disable-isolation + +#![allow(suspicious_runtime_symbol_definitions)] + use std::ffi::{CString, OsStr, c_char, c_int}; use std::os::unix::ffi::OsStrExt; diff --git a/src/tools/miri/tests/fail/shims/non_vararg_signature_mismatch.rs b/src/tools/miri/tests/fail/shims/non_vararg_signature_mismatch.rs index b920e6795f90d..ebbf0cc2bf331 100644 --- a/src/tools/miri/tests/fail/shims/non_vararg_signature_mismatch.rs +++ b/src/tools/miri/tests/fail/shims/non_vararg_signature_mismatch.rs @@ -1,5 +1,8 @@ //@ignore-target: windows # File handling is not implemented yet //@compile-flags: -Zmiri-disable-isolation + +#![allow(invalid_runtime_symbol_definitions)] + use std::ffi::{CString, OsStr, c_char, c_int}; use std::os::unix::ffi::OsStrExt; diff --git a/src/tools/miri/tests/fail/shims/return_type_mismatch.rs b/src/tools/miri/tests/fail/shims/return_type_mismatch.rs index 6dbdd3f539b38..bcde5d8040f59 100644 --- a/src/tools/miri/tests/fail/shims/return_type_mismatch.rs +++ b/src/tools/miri/tests/fail/shims/return_type_mismatch.rs @@ -1,5 +1,8 @@ //@ignore-target: windows # File handling is not implemented yet //@compile-flags: -Zmiri-disable-isolation + +#![allow(suspicious_runtime_symbol_definitions)] + use std::ffi::{CString, OsStr, c_char, c_int, c_short}; use std::os::unix::ffi::OsStrExt; diff --git a/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs b/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs index e9cb69418d22f..5453db57bbd8f 100644 --- a/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs +++ b/src/tools/miri/tests/fail/shims/wrong_fixed_arg_count.rs @@ -1,5 +1,8 @@ //@ignore-target: windows # File handling is not implemented yet //@compile-flags: -Zmiri-disable-isolation + +#![allow(invalid_runtime_symbol_definitions)] + use std::ffi::{CString, OsStr, c_char, c_int}; use std::os::unix::ffi::OsStrExt; From ec8c18fdb83bbda486ad0e175dfc407727076341 Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Sat, 2 May 2026 19:35:54 -0700 Subject: [PATCH 021/215] Un-copy-paste Dropper in VecDeque code --- .../alloc/src/collections/vec_deque/mod.rs | 48 +++++-------------- 1 file changed, 12 insertions(+), 36 deletions(-) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 44211354f520e..80b69b1dba557 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -133,21 +133,21 @@ impl Clone for VecDeque { } } -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T, A: Allocator> Drop for VecDeque { - fn drop(&mut self) { - /// Runs the destructor for all items in the slice when it gets dropped (normally or - /// during unwinding). - struct Dropper<'a, T>(&'a mut [T]); +/// Runs the destructor for all items in the slice when it gets dropped (normally or +/// during unwinding). +struct Dropper<'a, T>(&'a mut [T]); - impl<'a, T> Drop for Dropper<'a, T> { - fn drop(&mut self) { - unsafe { - ptr::drop_in_place(self.0); - } - } +impl Drop for Dropper<'_, T> { + fn drop(&mut self) { + unsafe { + ptr::drop_in_place(self.0); } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T, A: Allocator> Drop for VecDeque { + fn drop(&mut self) { let (front, back) = self.as_mut_slices(); unsafe { let _back_dropper = Dropper(back); @@ -1433,18 +1433,6 @@ impl VecDeque { #[doc(alias = "retain_front")] #[stable(feature = "deque_extras", since = "1.16.0")] pub fn truncate(&mut self, len: usize) { - /// Runs the destructor for all items in the slice when it gets dropped (normally or - /// during unwinding). - struct Dropper<'a, T>(&'a mut [T]); - - impl<'a, T> Drop for Dropper<'a, T> { - fn drop(&mut self) { - unsafe { - ptr::drop_in_place(self.0); - } - } - } - // Safe because: // // * Any slice passed to `drop_in_place` is valid; the second case has @@ -1499,18 +1487,6 @@ impl VecDeque { #[doc(alias = "truncate_front")] #[stable(feature = "vec_deque_truncate_front", since = "CURRENT_RUSTC_VERSION")] pub fn retain_back(&mut self, len: usize) { - /// Runs the destructor for all items in the slice when it gets dropped (normally or - /// during unwinding). - struct Dropper<'a, T>(&'a mut [T]); - - impl<'a, T> Drop for Dropper<'a, T> { - fn drop(&mut self) { - unsafe { - ptr::drop_in_place(self.0); - } - } - } - unsafe { if len >= self.len { // No action is taken From ba313ddd1db0c8264be2f20473f561ce1942fe53 Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Sat, 2 May 2026 19:36:08 -0700 Subject: [PATCH 022/215] Implement `VecDeque::retain_range` --- .../alloc/src/collections/vec_deque/mod.rs | 86 +++++++++++ library/alloctests/tests/lib.rs | 1 + library/alloctests/tests/vec_deque.rs | 135 ++++++++++++++++++ 3 files changed, 222 insertions(+) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 80b69b1dba557..9eac8bcde1d1c 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -1519,6 +1519,92 @@ impl VecDeque { } } + /// Shortens the deque to the elements within `range`, dropping the rest. + /// + /// # Panics + /// + /// Panics if the starting point is greater than the end point or if + /// the end point is greater than the length of the deque. + /// + /// # Examples + /// + /// ``` + /// # #![feature(vec_deque_retain_range)] + /// use std::collections::VecDeque; + /// + /// let mut buf: VecDeque<_> = (0..6).collect(); + /// buf.truncate_to_range(2..5); + /// assert_eq!(buf, [2, 3, 4]); + /// ``` + #[unstable(feature = "vec_deque_retain_range", issue = "156215")] + pub fn truncate_to_range(&mut self, range: R) + where + R: RangeBounds, + { + let Range { start, end } = slice::range(range, ..self.len); + + if start == 0 && end == self.len { + return; + } else if start == end { + self.clear(); + return; + } else if start == 0 { + self.truncate(end); + return; + } else if end == self.len { + self.retain_back(self.len - start); + return; + } + + // Both the dropped prefix [0..start) and the dropped suffix [end..self.len) are + // non-empty. Plan up to three physical slices to drop, then update head/len, then + // drop. Only one of the dropped prefix or dropped suffix can cross between slices. + let (front, back) = self.as_mut_slices(); + let flen = front.len(); + let blen = back.len(); + let fptr = front.as_mut_ptr(); + let bptr = back.as_mut_ptr(); + + unsafe { + let (drop_a, drop_b, drop_c) = if end <= flen { + // Kept range lies in `front`. The dropped suffix is the rest of `front` + // plus all of `back`. + let pre = ptr::slice_from_raw_parts_mut(fptr, start); + let mid = ptr::slice_from_raw_parts_mut(fptr.add(end), flen - end); + (pre, mid, Some(back as *mut [T])) + } else if start >= flen { + // Kept range lies in `back`. The dropped prefix is all of `front` plus the + // start of `back`. + let mid = ptr::slice_from_raw_parts_mut(bptr, start - flen); + let suf = ptr::slice_from_raw_parts_mut(bptr.add(end - flen), blen - (end - flen)); + (front as *mut [T], mid, Some(suf)) + } else { + // Kept range straddles the boundary. The dropped prefix is in `front`, the + // dropped suffix is in `back`. Only two regions to drop. + let pre = ptr::slice_from_raw_parts_mut(fptr, start); + let suf = ptr::slice_from_raw_parts_mut(bptr.add(end - flen), blen - (end - flen)); + (pre, suf, None) + }; + + // Set these once only, then drop. If we called truncate + retain_back, a panic in + // a destructor could leave this truncation in a half completed state. + self.head = self.to_wrapped_index(start); + self.len = end - start; + + match drop_c { + Some(c) => { + let _g_a = Dropper(&mut *drop_a); + let _g_b = Dropper(&mut *drop_b); + ptr::drop_in_place(c); + } + None => { + let _g_a = Dropper(&mut *drop_a); + ptr::drop_in_place(drop_b); + } + } + } + } + /// Returns a reference to the underlying allocator. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index e5b3646f14a58..c06a22cbfb5d8 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -40,6 +40,7 @@ #![feature(vec_peek_mut)] #![feature(vec_try_remove)] #![feature(ptr_cast_slice)] +#![feature(vec_deque_retain_range)] #![allow(internal_features)] #![deny(implicit_provenance_casts)] #![deny(unsafe_op_in_unsafe_fn)] diff --git a/library/alloctests/tests/vec_deque.rs b/library/alloctests/tests/vec_deque.rs index 2e75b7b07e63f..15cc156d6988f 100644 --- a/library/alloctests/tests/vec_deque.rs +++ b/library/alloctests/tests/vec_deque.rs @@ -7,6 +7,7 @@ use std::collections::vec_deque::Drain; use std::fmt::Debug; use std::ops::Bound::*; use std::panic::{AssertUnwindSafe, catch_unwind}; +use std::sync::atomic::{AtomicUsize, Ordering}; use Taggy::*; use Taggypar::*; @@ -2360,3 +2361,137 @@ fn test_splice_wrapping_and_resize() { assert_eq!(Vec::from(vec), [1, 2, 3, 4, 1, 1, 1, 1, 1]) } + +#[test] +fn truncate_to_range_basic() { + // no-op + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(..); + assert_eq!(v, [0, 1, 2, 3, 4, 5]); + + // clear + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(3..3); + assert_eq!(v, [] as [i32; 0]); + + // truncate + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(..3); + assert_eq!(v, [0, 1, 2]); + + // truncate front + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(2..); + assert_eq!(v, [2, 3, 4, 5]); + + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(2..5); + assert_eq!(v, [2, 3, 4]); + + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(3..=5); + assert_eq!(v, [3, 4, 5]); + + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(..=3); + assert_eq!(v, [0, 1, 2, 3]); +} + +fn make_wrapped() -> VecDeque { + let mut v = VecDeque::new(); + v.extend(0..5); + v.push_front(-1); + v.push_front(-2); + v.push_front(-3); + assert_eq!(v.as_slices(), ([-3, -2, -1].as_slice(), [0, 1, 2, 3, 4].as_slice())); + v +} + +#[test] +fn truncate_to_range_kept_in_front() { + let mut v = make_wrapped(); + v.truncate_to_range(1..3); + assert_eq!(v, [-2, -1]); +} + +#[test] +fn truncate_to_range_kept_in_back() { + let mut v = make_wrapped(); + v.truncate_to_range(4..7); + assert_eq!(v, [1, 2, 3]); +} + +#[test] +fn truncate_to_range_kept_straddles() { + let mut v = make_wrapped(); + v.truncate_to_range(1..6); + assert_eq!(v, [-2, -1, 0, 1, 2]); +} + +#[test] +#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")] +fn truncate_to_range_leak() { + struct_with_counted_drop!(D(bool), DROPS => |this: &D| if this.0 { panic!("panic in `drop`"); } ); + + let mut q = VecDeque::new(); + q.push_back(D(true)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_back(D(false)); + q.push_front(D(false)); + q.push_front(D(false)); + q.push_front(D(false)); + + catch_unwind(AssertUnwindSafe(|| q.truncate_to_range(4..7))).ok(); + + assert_eq!(DROPS.get(), 5); +} + +#[test] +fn truncate_to_range_calls_drop() { + static DROPPED: AtomicUsize = AtomicUsize::new(0); + + #[derive(Debug)] + struct Foo(u8); + + impl Drop for Foo { + fn drop(&mut self) { + DROPPED.fetch_add(1, Ordering::Relaxed); + } + } + + let mut deque: VecDeque<_> = (0..12).map(Foo).collect(); + deque.truncate_to_range(1..5); + assert!(deque.iter().map(|x| x.0).eq([1, 2, 3, 4])); + assert_eq!(8, DROPPED.load(Ordering::Relaxed)); +} + +#[test] +#[should_panic] +fn truncate_to_range_start_greater_than_end() { + let mut v: VecDeque<_> = (0..6).collect(); + #[allow(clippy::reversed_empty_ranges)] + v.truncate_to_range(4..2); +} + +#[test] +#[should_panic] +fn truncate_to_range_end_past_len() { + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(2..7); +} + +#[test] +#[should_panic] +fn truncate_to_range_start_past_len() { + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(7..8); +} + +#[test] +#[should_panic] +fn truncate_to_range_inclusive_end_overflow() { + let mut v: VecDeque<_> = (0..6).collect(); + v.truncate_to_range(0..=usize::MAX); +} From f371a62bdf3bdf868dc29a5d9b19140ded6d35f7 Mon Sep 17 00:00:00 2001 From: Sidney Cammeresi Date: Wed, 13 May 2026 07:42:32 -0700 Subject: [PATCH 023/215] Tidy up library/alloctests/tests/lib.rs --- library/alloctests/tests/lib.rs | 58 +++++++++++++++++---------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/library/alloctests/tests/lib.rs b/library/alloctests/tests/lib.rs index c06a22cbfb5d8..96dcde71fc071 100644 --- a/library/alloctests/tests/lib.rs +++ b/library/alloctests/tests/lib.rs @@ -1,49 +1,51 @@ +// tidy-alphabetical-start +#![allow(internal_features)] +#![deny(implicit_provenance_casts)] +#![deny(unsafe_op_in_unsafe_fn)] #![feature(allocator_api)] +#![feature(binary_heap_drain_sorted)] +#![feature(binary_heap_into_iter_sorted)] #![feature(binary_heap_pop_if)] -#![feature(const_heap)] -#![feature(deque_extend_front)] -#![feature(iter_array_chunks)] #![feature(casefold)] -#![feature(cow_is_borrowed)] +#![feature(const_btree_len)] +#![feature(const_heap)] +#![feature(const_trait_impl)] #![feature(core_intrinsics)] +#![feature(cow_is_borrowed)] +#![feature(deque_extend_front)] #![feature(downcast_unchecked)] +#![feature(drain_keep_rest)] #![feature(exact_size_is_empty)] #![feature(hashmap_internals)] +#![feature(inplace_iteration)] +#![feature(iter_advance_by)] +#![feature(iter_array_chunks)] +#![feature(iter_next_chunk)] #![feature(linked_list_cursors)] +#![feature(local_waker)] +#![feature(macro_metavar_expr_concat)] #![feature(map_try_insert)] #![feature(pattern)] -#![feature(trusted_len)] -#![feature(try_reserve_kind)] -#![feature(try_with_capacity)] -#![feature(unboxed_closures)] -#![feature(binary_heap_into_iter_sorted)] -#![feature(binary_heap_drain_sorted)] -#![feature(slice_ptr_get)] -#![feature(slice_range)] +#![feature(ptr_cast_slice)] #![feature(slice_partial_sort_unstable)] -#![feature(inplace_iteration)] -#![feature(iter_advance_by)] -#![feature(iter_next_chunk)] #![feature(slice_partition_dedup)] -#![feature(string_remove_matches)] -#![feature(const_btree_len)] -#![feature(const_trait_impl)] -#![feature(test)] -#![feature(thin_box)] -#![feature(drain_keep_rest)] -#![feature(local_waker)] +#![feature(slice_ptr_get)] +#![feature(slice_range)] #![feature(str_as_str)] #![feature(strict_provenance_lints)] +#![feature(string_remove_matches)] #![feature(string_replace_in_place)] +#![feature(test)] +#![feature(thin_box)] +#![feature(trusted_len)] +#![feature(try_reserve_kind)] +#![feature(try_with_capacity)] +#![feature(unboxed_closures)] #![feature(unique_rc_arc)] -#![feature(macro_metavar_expr_concat)] +#![feature(vec_deque_retain_range)] #![feature(vec_peek_mut)] #![feature(vec_try_remove)] -#![feature(ptr_cast_slice)] -#![feature(vec_deque_retain_range)] -#![allow(internal_features)] -#![deny(implicit_provenance_casts)] -#![deny(unsafe_op_in_unsafe_fn)] +// tidy-alphabetical-end extern crate alloc; From 4d0b5936d4162277809de769f4ca8b9724f549e8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 12 Jul 2026 10:57:34 +1000 Subject: [PATCH 024/215] Make `HasTokens` a sub-trait of `HasAttrs`. Because that's what it is: any type with tokens must also have attrs. This simplifies some trait bounds. --- compiler/rustc_ast/src/ast_traits.rs | 4 ++-- compiler/rustc_ast/src/tokenstream.rs | 4 ++-- compiler/rustc_builtin_macros/src/cfg_eval.rs | 4 ++-- compiler/rustc_expand/src/config.rs | 2 +- compiler/rustc_parse/src/parser/attr_wrapper.rs | 4 ++-- compiler/rustc_parse/src/parser/mod.rs | 7 +++---- 6 files changed, 12 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 9ee8ece414b7c..14b317199996f 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -62,7 +62,7 @@ impl HasNodeId for Box { } /// A trait for AST nodes having (or not having) collected tokens. -pub trait HasTokens { +pub trait HasTokens: HasAttrs { fn tokens(&self) -> Option<&LazyAttrTokenStream>; fn tokens_mut(&mut self) -> Option<&mut Option>; } @@ -109,7 +109,7 @@ impl_has_tokens_none!( WherePredicate ); -impl HasTokens for WithTokens { +impl HasTokens for WithTokens { fn tokens(&self) -> Option<&LazyAttrTokenStream> { self.tokens.as_ref() } diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index b37d5fd9dc9c1..19ac92aa66fe7 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -18,7 +18,7 @@ use rustc_span::{DUMMY_SP, Span, SpanDecoder, SpanEncoder, Symbol, sym}; use thin_vec::ThinVec; use crate::ast::AttrStyle; -use crate::ast_traits::{HasAttrs, HasTokens}; +use crate::ast_traits::HasTokens; use crate::token::{self, Delimiter, Token, TokenKind}; use crate::{AttrVec, Attribute}; @@ -653,7 +653,7 @@ impl TokenStream { TokenStream::new(vec![TokenTree::token_alone(kind, span)]) } - pub fn from_ast(node: &(impl HasAttrs + HasTokens + fmt::Debug)) -> TokenStream { + pub fn from_ast(node: &(impl HasTokens + fmt::Debug)) -> TokenStream { let tokens = node.tokens().unwrap_or_else(|| panic!("missing tokens for node: {:?}", node)); let mut tts = vec![]; attrs_and_tokens_to_token_trees(node.attrs(), tokens, &mut tts); diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs index 9f032fa588195..60854d67ec946 100644 --- a/compiler/rustc_builtin_macros/src/cfg_eval.rs +++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs @@ -3,7 +3,7 @@ use core::ops::ControlFlow; use rustc_ast as ast; use rustc_ast::mut_visit::MutVisitor; use rustc_ast::visit::{AssocCtxt, Visitor}; -use rustc_ast::{Attribute, HasAttrs, HasTokens, NodeId, mut_visit, visit}; +use rustc_ast::{Attribute, HasTokens, NodeId, mut_visit, visit}; use rustc_errors::PResult; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_expand::config::StripUnconfigured; @@ -67,7 +67,7 @@ fn has_cfg_or_cfg_attr(annotatable: &Annotatable) -> bool { } impl CfgEval<'_> { - fn configure(&mut self, node: T) -> Option { + fn configure(&mut self, node: T) -> Option { self.0.configure(node) } diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index b3582494f226e..01b786152e6e9 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -167,7 +167,7 @@ macro_rules! configure { } impl<'a> StripUnconfigured<'a> { - pub fn configure(&self, mut node: T) -> Option { + pub fn configure(&self, mut node: T) -> Option { self.process_cfg_attrs(&mut node); self.in_cfg(node.attrs()).then(|| { self.try_configure_tokens(&mut node); diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs index e04178645fdd8..6397fc855b86c 100644 --- a/compiler/rustc_parse/src/parser/attr_wrapper.rs +++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs @@ -5,7 +5,7 @@ use rustc_ast::token::Token; use rustc_ast::tokenstream::{ AttrsTarget, LazyAttrTokenStream, NodeRange, ParserRange, Spacing, TokenCursor, }; -use rustc_ast::{self as ast, AttrVec, Attribute, HasAttrs, HasTokens}; +use rustc_ast::{self as ast, AttrVec, Attribute, HasTokens}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::PResult; use rustc_session::parse::ParseSess; @@ -137,7 +137,7 @@ impl<'a> Parser<'a> { /// } // 32..33 /// } // 33..34 /// ``` - pub(super) fn collect_tokens( + pub(super) fn collect_tokens( &mut self, pre_attr_pos: Option, attrs: AttrWrapper, diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 8a8d1bd2c2e3f..a20e07b756cff 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -36,9 +36,8 @@ use rustc_ast::util::case::Case; use rustc_ast::util::classify; use rustc_ast::{ self as ast, AnonConst, AttrArgs, AttrId, BinOpKind, ByRef, Const, CoroutineKind, - DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, ImplRestriction, - MutRestriction, Mutability, Recovered, RestrictionKind, Safety, StrLit, Visibility, - VisibilityKind, + DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasTokens, ImplRestriction, MutRestriction, + Mutability, Recovered, RestrictionKind, Safety, StrLit, Visibility, VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; @@ -1662,7 +1661,7 @@ impl<'a> Parser<'a> { } } - fn collect_tokens_no_attrs( + fn collect_tokens_no_attrs( &mut self, f: impl FnOnce(&mut Self) -> PResult<'a, R>, ) -> PResult<'a, R> { From 36487c75956bfe2476d1f813dff6903b104066b9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 3 Jul 2026 14:58:41 +1000 Subject: [PATCH 025/215] Shrink `ast::Expr` From 72 bytes to 64 bytes, by boxing `ForLoop`, which is the biggest variant. There are now 11 `ExprKind` variants that are 31 bytes, so future improvements here will be very difficult. --- compiler/rustc_ast/src/ast.rs | 21 +++++++++-------- compiler/rustc_ast/src/util/classify.rs | 5 +++- compiler/rustc_ast/src/visit.rs | 2 +- compiler/rustc_ast_lowering/src/expr.rs | 2 +- .../rustc_ast_pretty/src/pprust/state/expr.rs | 2 +- compiler/rustc_lint/src/unused.rs | 6 ++--- compiler/rustc_parse/src/parser/expr.rs | 22 ++++++++++++++---- compiler/rustc_resolve/src/late.rs | 2 +- .../clippy/clippy_utils/src/ast_utils/mod.rs | 23 ++++++------------- src/tools/rustfmt/src/expr.rs | 10 ++------ tests/ui/stats/input-stats.stderr | 16 ++++++------- 11 files changed, 57 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 3293da27d2912..deaafa8ab2e36 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -1655,6 +1655,15 @@ impl From> for Expr { } } +#[derive(Clone, Encodable, Decodable, Debug, Walkable)] +pub struct ForLoop { + pub pat: Box, + pub iter: Box, + pub body: Box, + pub label: Option