From 2f44f6c6d038f818c6e8e874227b7639ae4f11ba Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 1 May 2026 16:21:52 +0200 Subject: [PATCH 01/71] 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 02/71] 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 03/71] 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 67fc269618b40c5a492caadbf7cfd994bb3b69f7 Mon Sep 17 00:00:00 2001 From: sgasho Date: Thu, 9 Jul 2026 14:58:36 +0900 Subject: [PATCH 04/71] 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 05/71] 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 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 06/71] 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 07/71] 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 08/71] 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 09/71] 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 4d0b5936d4162277809de769f4ca8b9724f549e8 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Sun, 12 Jul 2026 10:57:34 +1000 Subject: [PATCH 10/71] 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 36ac828a2b23f3b6214563b8465c07a0f290f8bd Mon Sep 17 00:00:00 2001 From: Andrew Barnes Date: Sun, 12 Jul 2026 22:57:18 -0400 Subject: [PATCH 11/71] Refresh diagnostic items date check --- src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md index 9360427d660e2..7396f0aba9e89 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md @@ -48,7 +48,7 @@ A new diagnostic item can be added with these two steps: For the naming conventions of diagnostic items, please refer to [*Naming Conventions*](#naming-conventions). -2. +2. Diagnostic items in code are accessed via symbols in [`rustc_span::symbol::sym`]. To add your newly-created diagnostic item, From 7abc53785aad373ddd9eed830466e9bb97569d37 Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Wed, 8 Apr 2026 21:08:27 +0100 Subject: [PATCH 12/71] Suggest the `[const] Destruct` bound for type parameters in const functions When a const function drops a value whose type is a type parameter, suggest adding a `[const] Destruct` bound so the destructor can be evaluated at compile-time. --- .../rustc_const_eval/src/check_consts/ops.rs | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 1f4b2c7879cc7..06e55c575a35c 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -594,7 +594,7 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> { } fn build_error(&self, ccx: &ConstCx<'_, 'tcx>, span: Span) -> Diag<'tcx> { - if self.needs_non_const_drop { + let mut err = if self.needs_non_const_drop { ccx.dcx().create_err(diagnostics::LiveDrop { span, dropped_ty: self.dropped_ty, @@ -611,7 +611,27 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> { }, sym::const_destruct, ) + }; + + // If the dropped type is a type parameter, suggest adding a `[const] Destruct` bound. + if let Param(param_ty) = self.dropped_ty.kind() { + let tcx = ccx.tcx; + let caller = ccx.def_id(); + if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() { + let destruct_def_id = tcx.lang_items().destruct_trait(); + suggest_constraining_type_param( + tcx, + generics, + &mut err, + param_ty.name.as_str(), + "[const] Destruct", + destruct_def_id, + None, + ); + } } + + err } } From 4fca6bd4ba655a20108027fca6c85b387e79085b Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Wed, 8 Apr 2026 21:38:07 +0100 Subject: [PATCH 13/71] Bless tests affected by the `[const] Destruct` suggestion --- tests/ui/consts/min_const_fn/min_const_fn.stderr | 5 +++++ tests/ui/consts/unstable-const-fn-in-libcore.stderr | 5 +++++ tests/ui/self/arbitrary-self-from-method-substs-ice.stderr | 5 +++++ tests/ui/static/static-drop-scope.stderr | 5 +++++ tests/ui/traits/const-traits/minicore-drop-fail.stderr | 5 +++++ 5 files changed, 25 insertions(+) diff --git a/tests/ui/consts/min_const_fn/min_const_fn.stderr b/tests/ui/consts/min_const_fn/min_const_fn.stderr index 0e939e5121aa9..0904de2af0d46 100644 --- a/tests/ui/consts/min_const_fn/min_const_fn.stderr +++ b/tests/ui/consts/min_const_fn/min_const_fn.stderr @@ -73,6 +73,11 @@ LL | const fn no_apit(_x: impl std::fmt::Debug) {} | ^^ - value is dropped here | | | the destructor for this type cannot be evaluated in constant functions + | +help: consider restricting opaque type `impl std::fmt::Debug` with unstable trait `Destruct` + | +LL | const fn no_apit(_x: impl std::fmt::Debug + [const] Destruct) {} + | ++++++++++++++++++ error: aborting due to 9 previous errors diff --git a/tests/ui/consts/unstable-const-fn-in-libcore.stderr b/tests/ui/consts/unstable-const-fn-in-libcore.stderr index 16db7791cd849..9dcd2b0ac0326 100644 --- a/tests/ui/consts/unstable-const-fn-in-libcore.stderr +++ b/tests/ui/consts/unstable-const-fn-in-libcore.stderr @@ -6,6 +6,11 @@ LL | const fn unwrap_or_else T>(self, f: F) -> T { ... LL | } | - value is dropped here + | +help: consider further restricting type parameter `F` with unstable trait `Destruct` + | +LL | const fn unwrap_or_else T + [const] Destruct>(self, f: F) -> T { + | ++++++++++++++++++ error[E0493]: destructor of `Opt` cannot be evaluated at compile-time --> $DIR/unstable-const-fn-in-libcore.rs:19:55 diff --git a/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr b/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr index f217370b024b5..1b410322aacbe 100644 --- a/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr +++ b/tests/ui/self/arbitrary-self-from-method-substs-ice.stderr @@ -15,6 +15,11 @@ LL | const fn get>(self: R) -> u32 { ... LL | } | - value is dropped here + | +help: consider further restricting type parameter `R` with unstable trait `Destruct` + | +LL | const fn get + [const] Destruct>(self: R) -> u32 { + | ++++++++++++++++++ error[E0801]: invalid generic `self` parameter type: `R` --> $DIR/arbitrary-self-from-method-substs-ice.rs:10:49 diff --git a/tests/ui/static/static-drop-scope.stderr b/tests/ui/static/static-drop-scope.stderr index 0fdf081e23432..84e99a3988121 100644 --- a/tests/ui/static/static-drop-scope.stderr +++ b/tests/ui/static/static-drop-scope.stderr @@ -37,6 +37,11 @@ LL | const fn const_drop(_: T) {} | ^ - value is dropped here | | | the destructor for this type cannot be evaluated in constant functions + | +help: consider restricting type parameter `T` with unstable trait `Destruct` + | +LL | const fn const_drop(_: T) {} + | ++++++++++++++++++ error[E0493]: destructor of `(T, ())` cannot be evaluated at compile-time --> $DIR/static-drop-scope.rs:17:5 diff --git a/tests/ui/traits/const-traits/minicore-drop-fail.stderr b/tests/ui/traits/const-traits/minicore-drop-fail.stderr index 12d1877a18aba..2cb1256e37667 100644 --- a/tests/ui/traits/const-traits/minicore-drop-fail.stderr +++ b/tests/ui/traits/const-traits/minicore-drop-fail.stderr @@ -30,6 +30,11 @@ LL | const fn drop_arbitrary(_: T) { LL | LL | } | - value is dropped here + | +help: consider restricting type parameter `T` with trait `Destruct` + | +LL | const fn drop_arbitrary(_: T) { + | ++++++++++++++++++ error: aborting due to 4 previous errors From 2f961b8f2098b565da95ebca1f1f08487f8bf7ee Mon Sep 17 00:00:00 2001 From: Jacob Adam Date: Sun, 12 Jul 2026 16:09:24 +0100 Subject: [PATCH 14/71] Only offer the `[const] Destruct` suggestion on a nightly compiler The suggested bound requires the unstable `const_destruct` feature, so it is not actionable on a stable compiler. Add a run-make test that verifies the suggestion is only emitted on nightly. --- .../rustc_const_eval/src/check_consts/ops.rs | 5 +++- .../const-drop-nightly.stderr | 16 ++++++++++++ .../const-drop-stable.stderr | 11 ++++++++ .../const-drop.rs | 3 +++ .../const-destruct-stable-toolchain/rmake.rs | 26 +++++++++++++++++++ 5 files changed, 60 insertions(+), 1 deletion(-) create mode 100644 tests/run-make/const-destruct-stable-toolchain/const-drop-nightly.stderr create mode 100644 tests/run-make/const-destruct-stable-toolchain/const-drop-stable.stderr create mode 100644 tests/run-make/const-destruct-stable-toolchain/const-drop.rs create mode 100644 tests/run-make/const-destruct-stable-toolchain/rmake.rs diff --git a/compiler/rustc_const_eval/src/check_consts/ops.rs b/compiler/rustc_const_eval/src/check_consts/ops.rs index 06e55c575a35c..e980667c5e468 100644 --- a/compiler/rustc_const_eval/src/check_consts/ops.rs +++ b/compiler/rustc_const_eval/src/check_consts/ops.rs @@ -614,7 +614,10 @@ impl<'tcx> NonConstOp<'tcx> for LiveDrop<'tcx> { }; // If the dropped type is a type parameter, suggest adding a `[const] Destruct` bound. - if let Param(param_ty) = self.dropped_ty.kind() { + // The suggestion is only offered on nightly, since `[const]` bounds are unstable. + if let Param(param_ty) = self.dropped_ty.kind() + && ccx.tcx.sess.is_nightly_build() + { let tcx = ccx.tcx; let caller = ccx.def_id(); if let Some(generics) = tcx.hir_node_by_def_id(caller).generics() { diff --git a/tests/run-make/const-destruct-stable-toolchain/const-drop-nightly.stderr b/tests/run-make/const-destruct-stable-toolchain/const-drop-nightly.stderr new file mode 100644 index 0000000000000..52f68743b6275 --- /dev/null +++ b/tests/run-make/const-destruct-stable-toolchain/const-drop-nightly.stderr @@ -0,0 +1,16 @@ +error[E0493]: destructor of `T` cannot be evaluated at compile-time + --> const-drop.rs:1:24 + | +LL | const fn const_drop(_: T) {} + | ^ - value is dropped here + | | + | the destructor for this type cannot be evaluated in constant functions + | +help: consider restricting type parameter `T` with unstable trait `Destruct` + | +LL | const fn const_drop(_: T) {} + | ++++++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0493`. diff --git a/tests/run-make/const-destruct-stable-toolchain/const-drop-stable.stderr b/tests/run-make/const-destruct-stable-toolchain/const-drop-stable.stderr new file mode 100644 index 0000000000000..dc37d840658fd --- /dev/null +++ b/tests/run-make/const-destruct-stable-toolchain/const-drop-stable.stderr @@ -0,0 +1,11 @@ +error[E0493]: destructor of `T` cannot be evaluated at compile-time + --> const-drop.rs:1:24 + | +1 | const fn const_drop(_: T) {} + | ^ - value is dropped here + | | + | the destructor for this type cannot be evaluated in constant functions + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0493`. diff --git a/tests/run-make/const-destruct-stable-toolchain/const-drop.rs b/tests/run-make/const-destruct-stable-toolchain/const-drop.rs new file mode 100644 index 0000000000000..6479498281fe5 --- /dev/null +++ b/tests/run-make/const-destruct-stable-toolchain/const-drop.rs @@ -0,0 +1,3 @@ +const fn const_drop(_: T) {} + +fn main() {} diff --git a/tests/run-make/const-destruct-stable-toolchain/rmake.rs b/tests/run-make/const-destruct-stable-toolchain/rmake.rs new file mode 100644 index 0000000000000..c1ff48b3214f0 --- /dev/null +++ b/tests/run-make/const-destruct-stable-toolchain/rmake.rs @@ -0,0 +1,26 @@ +//@ needs-target-std +// +// Test that the suggestion to constrain a type parameter that is dropped in a const +// function with a `[const] Destruct` bound is only offered on nightly, since the bound +// requires an unstable feature. + +use run_make_support::{diff, rustc}; + +fn main() { + let out = rustc() + .input("const-drop.rs") + .env("RUSTC_BOOTSTRAP", "-1") + .run_fail() + .assert_stderr_not_contains("consider restricting type parameter `T`") + .stderr_utf8(); + diff().expected_file("const-drop-stable.stderr").actual_text("(rustc)", &out).run(); + let out = rustc() + .input("const-drop.rs") + .ui_testing() + .run_fail() + .assert_stderr_contains( + "consider restricting type parameter `T` with unstable trait `Destruct`", + ) + .stderr_utf8(); + diff().expected_file("const-drop-nightly.stderr").actual_text("(rustc)", &out).run(); +} From 8c2ed2f9a15bce185dbbcaf261d4dd9d3aa9180a Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Thu, 8 Jan 2026 10:45:01 +0000 Subject: [PATCH 15/71] Introduce InstanceKind::LlvmIntrinsic This way codegen backends don't have to check for a magic symbol name prefix to detect LLVM intrinsic calls and thus can more easily handle them separately as necessary. --- .../rustc_codegen_cranelift/src/abi/mod.rs | 23 +-- compiler/rustc_codegen_ssa/src/base.rs | 9 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 2 +- .../src/const_eval/dummy_machine.rs | 11 + .../src/const_eval/machine.rs | 13 ++ .../rustc_const_eval/src/interpret/call.rs | 11 + .../rustc_const_eval/src/interpret/machine.rs | 11 + compiler/rustc_middle/src/mir/visit.rs | 1 + compiler/rustc_middle/src/mono.rs | 1 + compiler/rustc_middle/src/ty/instance.rs | 29 ++- compiler/rustc_middle/src/ty/mod.rs | 4 +- compiler/rustc_middle/src/ty/print/mod.rs | 1 + compiler/rustc_mir_transform/src/inline.rs | 2 +- .../rustc_mir_transform/src/inline/cycle.rs | 4 +- compiler/rustc_monomorphize/src/collector.rs | 4 +- .../rustc_monomorphize/src/partitioning.rs | 2 + compiler/rustc_public/src/mir/mono.rs | 7 +- .../src/unstable/convert/stable/ty.rs | 1 + compiler/rustc_ty_utils/src/instance.rs | 7 + src/tools/miri/src/intrinsics/mod.rs | 2 +- src/tools/miri/src/machine.rs | 12 ++ src/tools/miri/src/shims/foreign_items.rs | 189 +++++++++++------- 22 files changed, 236 insertions(+), 110 deletions(-) diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index ec17e72900dfb..ce780647662b8 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -440,18 +440,6 @@ pub(crate) fn codegen_terminator_call<'tcx>( } } - if fx.tcx.symbol_name(instance).name.starts_with("llvm.") { - crate::intrinsics::codegen_llvm_intrinsic_call( - fx, - fx.tcx.symbol_name(instance).name, - args, - ret_place, - target, - source_info.span, - ); - return; - } - match instance.def { InstanceKind::Intrinsic(_) => { match crate::intrinsics::codegen_intrinsic_call( @@ -466,6 +454,17 @@ pub(crate) fn codegen_terminator_call<'tcx>( Err(instance) => Some(instance), } } + InstanceKind::LlvmIntrinsic(_) => { + crate::intrinsics::codegen_llvm_intrinsic_call( + fx, + fx.tcx.symbol_name(instance).name, + args, + ret_place, + target, + source_info.span, + ); + return; + } // We don't need AsyncDropGlueCtorShim here because it is not `noop func`, // it is `func returning noop future` InstanceKind::Shim(ShimKind::DropGlue(_, None)) => { diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 0b86c421d16b5..01c132fd09ee5 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -892,12 +892,8 @@ pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( tcx: TyCtxt<'tcx>, instance: Instance<'tcx>, ) -> bool { - fn is_llvm_intrinsic(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - if let Some(name) = tcx.codegen_fn_attrs(def_id).symbol_name { - name.as_str().starts_with("llvm.") - } else { - false - } + if let ty::InstanceKind::LlvmIntrinsic(_) = instance.def { + return false; } fn is_extern_call_to_local_crate<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) -> bool { @@ -910,7 +906,6 @@ pub fn is_call_from_compiler_builtins_to_upstream_monomorphization<'tcx>( let def_id = instance.def_id(); !def_id.is_local() && tcx.is_compiler_builtins(LOCAL_CRATE) - && !is_llvm_intrinsic(tcx, def_id) && !tcx.should_codegen_locally(instance) && !is_extern_call_to_local_crate(tcx, instance) } diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index fad93de43b272..c05b89a552631 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -1112,8 +1112,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { }; if let Some(instance) = instance + && let ty::InstanceKind::LlvmIntrinsic(_) = instance.def && let Some(name) = bx.tcx().codegen_fn_attrs(instance.def_id()).symbol_name - && name.as_str().starts_with("llvm.") // This is the only LLVM intrinsic we use that unwinds // FIXME either add unwind support to codegen_llvm_intrinsic_call or replace usage of // this intrinsic with something else diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs index 7c5f867929f04..adf4069300e07 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -105,6 +105,17 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine { unimplemented!() } + fn call_llvm_intrinsic( + _ecx: &mut InterpCx<'tcx, Self>, + _instance: ty::Instance<'tcx>, + _args: &[interpret::OpTy<'tcx, Self::Provenance>], + _destination: &interpret::PlaceTy<'tcx, Self::Provenance>, + _target: Option, + _unwind: UnwindAction, + ) -> interpret::InterpResult<'tcx> { + unimplemented!() + } + fn assert_panic( _ecx: &mut InterpCx<'tcx, Self>, _msg: &rustc_middle::mir::AssertMessage<'tcx>, diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index c5da3253ef2c7..fa394ab627b32 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -745,6 +745,19 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { interp_ok(None) } + fn call_llvm_intrinsic( + ecx: &mut InterpCx<'tcx, Self>, + instance: ty::Instance<'tcx>, + _args: &[OpTy<'tcx>], + _dest: &PlaceTy<'tcx, Self::Provenance>, + _target: Option, + _unwind: mir::UnwindAction, + ) -> InterpResult<'tcx> { + let intrinsic_name = ecx.tcx.codegen_fn_attrs(instance.def_id()).symbol_name.unwrap(); + + throw_unsup_format!("LLVM intrinsic `{intrinsic_name}` is not supported at compile-time"); + } + fn assert_panic( ecx: &mut InterpCx<'tcx, Self>, msg: &AssertMessage<'tcx>, diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index e2fe8c3a79690..fad4c5081d833 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -652,6 +652,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { interp_ok(()) } } + ty::InstanceKind::LlvmIntrinsic(_) => { + // FIXME: Should `InPlace` arguments be reset to uninit? + M::call_llvm_intrinsic( + self, + instance, + &Self::copy_fn_args(args), + destination, + target, + unwind, + ) + } ty::InstanceKind::Shim(ty::ShimKind::VTable(..)) | ty::InstanceKind::Shim(ty::ShimKind::Reify(..)) | ty::InstanceKind::Shim(ty::ShimKind::ClosureOnce { .. }) diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index debcc2ac338b5..1df40dcf21dcb 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -247,6 +247,17 @@ pub trait Machine<'tcx>: Sized { unwind: mir::UnwindAction, ) -> InterpResult<'tcx, Option>>; + /// Directly process an LLVM intrinsic without pushing a stack frame. It is the hook's + /// responsibility to advance the instruction pointer as appropriate. + fn call_llvm_intrinsic( + ecx: &mut InterpCx<'tcx, Self>, + instance: ty::Instance<'tcx>, + args: &[OpTy<'tcx, Self::Provenance>], + destination: &PlaceTy<'tcx, Self::Provenance>, + target: Option, + unwind: mir::UnwindAction, + ) -> InterpResult<'tcx>; + /// Check whether the given function may be executed on the current machine, in terms of the /// target features is requires. fn check_fn_target_features( diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index 22f2d5c1afcac..56f6ac4368d5f 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -347,6 +347,7 @@ macro_rules! make_mir_visitor { ty::InstanceKind::Item(_def_id) => {} ty::InstanceKind::Intrinsic(_def_id) + | ty::InstanceKind::LlvmIntrinsic(_def_id) | ty::InstanceKind::Shim(ty::ShimKind::VTable(_def_id)) | ty::InstanceKind::Shim(ty::ShimKind::Reify(_def_id, _)) | ty::InstanceKind::Virtual(_def_id, _) diff --git a/compiler/rustc_middle/src/mono.rs b/compiler/rustc_middle/src/mono.rs index 6df67257a2ea7..bad6986a2630c 100644 --- a/compiler/rustc_middle/src/mono.rs +++ b/compiler/rustc_middle/src/mono.rs @@ -524,6 +524,7 @@ impl<'tcx> CodegenUnit<'tcx> { MonoItem::Fn(ref instance) => match instance.def { InstanceKind::Item(def) => def.as_local().map(|_| def), InstanceKind::Intrinsic(..) + | InstanceKind::LlvmIntrinsic(..) | InstanceKind::Virtual(..) | InstanceKind::Shim(ShimKind::VTable(..)) | InstanceKind::Shim(ShimKind::Reify(..)) diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index c59e71b19be78..6a04357827360 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -70,11 +70,18 @@ pub enum InstanceKind<'tcx> { /// An intrinsic `fn` item (with`#[rustc_intrinsic]`). /// - /// Alongside `Virtual`, this is the only `InstanceKind` that does not have its own callable MIR. - /// Instead, codegen and const eval "magically" evaluate calls to intrinsics purely in the - /// caller. + /// Alongside `LlvmIntrinsic` and `Virtual`, this is the only `InstanceKind` + /// that does not have its own callable MIR. Instead, codegen and const eval + /// "magically" evaluate calls to intrinsics purely in the caller. Intrinsic(DefId), + /// An LLVM intrinsic `fn` item (with `extern "unadjusted"`). + /// + /// Alongside `Intrinsic` and `Virtual`, this is the only `InstanceKind` + /// that does not have its own callable MIR. Instead, codegen and const eval + /// "magically" evaluate calls to LLVM intrinsics purely in the caller. + LlvmIntrinsic(DefId), + /// Dynamic dispatch to `::fn`. /// /// This `InstanceKind` may have a callable MIR as the default implementation. @@ -253,7 +260,8 @@ impl<'tcx> InstanceKind<'tcx> { match self { InstanceKind::Item(def_id) | InstanceKind::Virtual(def_id, _) - | InstanceKind::Intrinsic(def_id) => def_id, + | InstanceKind::Intrinsic(def_id) + | InstanceKind::LlvmIntrinsic(def_id) => def_id, InstanceKind::Shim(shim) => shim.def_id(), } } @@ -262,7 +270,9 @@ impl<'tcx> InstanceKind<'tcx> { pub fn def_id_if_not_guaranteed_local_codegen(self) -> Option { match self { InstanceKind::Item(def) => Some(def), - InstanceKind::Virtual(..) | InstanceKind::Intrinsic(..) => None, + InstanceKind::Virtual(..) + | InstanceKind::Intrinsic(..) + | InstanceKind::LlvmIntrinsic(..) => None, InstanceKind::Shim(shim) => shim.def_id_if_not_guaranteed_local_codegen(), } } @@ -280,7 +290,9 @@ impl<'tcx> InstanceKind<'tcx> { DefPathData::Ctor | DefPathData::Closure ), InstanceKind::Shim(shim) => shim.requires_inline(), - InstanceKind::Virtual(..) | InstanceKind::Intrinsic(..) => true, + InstanceKind::Virtual(..) + | InstanceKind::Intrinsic(..) + | InstanceKind::LlvmIntrinsic(..) => true, } } @@ -308,7 +320,10 @@ impl<'tcx> InstanceKind<'tcx> { /// body should perform necessary instantiations. pub fn has_polymorphic_mir_body(&self) -> bool { match *self { - InstanceKind::Item(_) | InstanceKind::Intrinsic(..) | InstanceKind::Virtual(..) => true, + InstanceKind::Item(_) + | InstanceKind::Intrinsic(..) + | InstanceKind::LlvmIntrinsic(..) + | InstanceKind::Virtual(..) => true, InstanceKind::Shim(shim) => shim.has_polymorphic_mir_body(), } } diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index c482e6bb87345..4c14858101bf1 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -1833,7 +1833,9 @@ impl<'tcx> TyCtxt<'tcx> { _ => self.optimized_mir(def), } } - ty::InstanceKind::Intrinsic(..) => bug!("intrinsics have no instance MIR"), + ty::InstanceKind::Intrinsic(..) | ty::InstanceKind::LlvmIntrinsic(..) => { + bug!("intrinsics have no instance MIR") + } ty::InstanceKind::Virtual(..) => bug!("virtual dispatches have no instance MIR"), ty::InstanceKind::Shim(shim) => self.mir_shims(shim), }; diff --git a/compiler/rustc_middle/src/ty/print/mod.rs b/compiler/rustc_middle/src/ty/print/mod.rs index bec1ffc642769..ccdac57cc8dcd 100644 --- a/compiler/rustc_middle/src/ty/print/mod.rs +++ b/compiler/rustc_middle/src/ty/print/mod.rs @@ -368,6 +368,7 @@ impl<'tcx, P: Printer<'tcx> + std::fmt::Write> Print

for ty::Instance<'tcx> { match self.def { ty::InstanceKind::Item(_) => {} ty::InstanceKind::Intrinsic(_) => cx.write_str(" - intrinsic")?, + ty::InstanceKind::LlvmIntrinsic(_) => cx.write_str(" - LLVM intrinsic")?, ty::InstanceKind::Virtual(_, num) => cx.write_str(&format!(" - virtual#{num}"))?, ty::InstanceKind::Shim(shim) => { cx.write_str(" - ")?; diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index c36b687111c01..df47eb9c3f9a2 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -730,7 +730,7 @@ fn check_mir_is_available<'tcx, I: Inliner<'tcx>>( } } // These have no own callable MIR. - InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => { + InstanceKind::Intrinsic(_) | InstanceKind::LlvmIntrinsic(_) | InstanceKind::Virtual(..) => { debug!("instance without MIR (intrinsic / virtual)"); return Err("implementation limitation -- cannot inline intrinsic"); } diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index b1a9258d48bb3..c3bdef3e43cf8 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -21,7 +21,9 @@ fn should_recurse<'tcx>(tcx: TyCtxt<'tcx>, callee: ty::Instance<'tcx>) -> bool { } // These have no own callable MIR. - InstanceKind::Intrinsic(_) | InstanceKind::Virtual(..) => return false, + InstanceKind::Intrinsic(_) | InstanceKind::LlvmIntrinsic(_) | InstanceKind::Virtual(..) => { + return false; + } // These have MIR and if that MIR is inlined, instantiated and then inlining is run // again, a function item can end up getting inlined. Thus we'll be able to cause diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 9b18b22399ae8..6fb789939f416 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1018,7 +1018,9 @@ fn visit_instance_use<'tcx>( } match instance.def { - ty::InstanceKind::Virtual(..) | ty::InstanceKind::Intrinsic(_) => { + ty::InstanceKind::Virtual(..) + | ty::InstanceKind::Intrinsic(_) + | ty::InstanceKind::LlvmIntrinsic(_) => { if !is_direct_call { bug!("{:?} being reified", instance); } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index bf4a2bdd15107..1e8e1486f107f 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -631,6 +631,7 @@ fn characteristic_def_id_of_mono_item<'tcx>( let def_id = match instance.def { ty::InstanceKind::Item(def) => def, ty::InstanceKind::Intrinsic(..) + | ty::InstanceKind::LlvmIntrinsic(..) | ty::InstanceKind::Virtual(..) | ty::InstanceKind::Shim(ty::ShimKind::VTable(..)) | ty::InstanceKind::Shim(ty::ShimKind::Reify(..)) @@ -810,6 +811,7 @@ fn mono_item_visibility<'tcx>( | InstanceKind::Shim(ShimKind::FnPtr(..)) | InstanceKind::Virtual(..) | InstanceKind::Intrinsic(..) + | InstanceKind::LlvmIntrinsic(..) | InstanceKind::Shim(ShimKind::ClosureOnce { .. }) | InstanceKind::Shim(ShimKind::ConstructCoroutineInClosure { .. }) | InstanceKind::Shim(ShimKind::DropGlue(..)) diff --git a/compiler/rustc_public/src/mir/mono.rs b/compiler/rustc_public/src/mir/mono.rs index ab939a5535149..b3caee5095733 100644 --- a/compiler/rustc_public/src/mir/mono.rs +++ b/compiler/rustc_public/src/mir/mono.rs @@ -32,6 +32,8 @@ pub enum InstanceKind { Item, /// A compiler intrinsic function. Intrinsic, + /// An LLVM intrinsic function. + LlvmIntrinsic, /// A virtual function definition stored in a VTable. /// The `idx` field indicates the position in the VTable for this instance. Virtual { idx: usize }, @@ -113,7 +115,10 @@ impl Instance { InstanceKind::Intrinsic => { Some(with(|context| context.intrinsic(self.def.def_id()).unwrap().fn_name())) } - InstanceKind::Item | InstanceKind::Virtual { .. } | InstanceKind::Shim => None, + InstanceKind::LlvmIntrinsic + | InstanceKind::Item + | InstanceKind::Virtual { .. } + | InstanceKind::Shim => None, } } diff --git a/compiler/rustc_public/src/unstable/convert/stable/ty.rs b/compiler/rustc_public/src/unstable/convert/stable/ty.rs index e0e212ee47d59..436fdd766899f 100644 --- a/compiler/rustc_public/src/unstable/convert/stable/ty.rs +++ b/compiler/rustc_public/src/unstable/convert/stable/ty.rs @@ -970,6 +970,7 @@ impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> { let kind = match self.def { ty::InstanceKind::Item(..) => crate::mir::mono::InstanceKind::Item, ty::InstanceKind::Intrinsic(..) => crate::mir::mono::InstanceKind::Intrinsic, + ty::InstanceKind::LlvmIntrinsic(..) => crate::mir::mono::InstanceKind::LlvmIntrinsic, ty::InstanceKind::Virtual(_def_id, idx) => { crate::mir::mono::InstanceKind::Virtual { idx } } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 34a3a96f89c40..d9069ac127729 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -1,5 +1,6 @@ use rustc_errors::ErrorGuaranteed; use rustc_hir::LangItem; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::bug; @@ -91,6 +92,12 @@ fn resolve_instance_raw<'tcx>( } else if tcx.is_async_drop_in_place_coroutine(def_id) { let ty = args.type_at(0); ty::InstanceKind::Shim(ty::ShimKind::AsyncDropGlue(def_id, ty)) + } else if tcx.def_kind(def_id) == DefKind::Fn + && let Some(name) = tcx.codegen_fn_attrs(def_id).symbol_name + && name.as_str().starts_with("llvm.") + { + debug!(" => LLVM intrinsic"); + ty::InstanceKind::LlvmIntrinsic(def_id) } else { debug!(" => free item"); ty::InstanceKind::Item(def_id) diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs index bc171f8fdb87c..7d7f4e6be7fc3 100644 --- a/src/tools/miri/src/intrinsics/mod.rs +++ b/src/tools/miri/src/intrinsics/mod.rs @@ -10,7 +10,7 @@ pub use self::atomic::AtomicRmwOp; use rand::RngExt; use rustc_abi::Size; use rustc_middle::{mir, ty}; -use rustc_span::Symbol; +use rustc_span::{Symbol, sym}; use self::atomic::EvalContextExt as _; use self::math::EvalContextExt as _; diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index cc6a15d3f7115..1654e1040310a 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1300,6 +1300,18 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { ecx.call_intrinsic(instance, args, dest, ret, unwind) } + #[inline(always)] + fn call_llvm_intrinsic( + ecx: &mut MiriInterpCx<'tcx>, + instance: ty::Instance<'tcx>, + args: &[OpTy<'tcx>], + dest: &PlaceTy<'tcx>, + ret: Option, + unwind: mir::UnwindAction, + ) -> InterpResult<'tcx, ()> { + ecx.call_llvm_intrinsic(instance, args, dest, ret, unwind) + } + #[inline(always)] fn assert_panic( ecx: &mut MiriInterpCx<'tcx>, diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index a1a341ffff3e0..462007855f23a 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -241,6 +241,118 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { Some(instance) => interp_ok(Some((this.load_mir(instance.def, None)?, instance))), } } + + // FIXME move this and the LLVM intrinsic impls to the intrinsics module + fn call_llvm_intrinsic( + &mut self, + instance: ty::Instance<'tcx>, + args: &[OpTy<'tcx>], + dest: &PlaceTy<'tcx>, + ret: Option, + unwind: mir::UnwindAction, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let link_name = this.tcx.codegen_fn_attrs(instance.def_id()).symbol_name.unwrap(); + + // FIXME: avoid allocating memory + let dest = this.force_allocation(dest)?; + + let res = match link_name.as_str() { + // LLVM intrinsics + "llvm.prefetch.p0" => { + let [p, rw, loc, ty] = this.check_shim_sig_unadjusted(link_name, args)?; + + let _ = this.read_pointer(p)?; + let rw = this.read_scalar(rw)?.to_i32()?; + let loc = this.read_scalar(loc)?.to_i32()?; + let ty = this.read_scalar(ty)?.to_i32()?; + + if ty == 1 { + // Data cache prefetch. + // Notably, we do not have to check the pointer, this operation is never UB! + + if !matches!(rw, 0 | 1) { + throw_unsup_format!("invalid `rw` value passed to `llvm.prefetch`: {}", rw); + } + if !matches!(loc, 0..=3) { + throw_unsup_format!( + "invalid `loc` value passed to `llvm.prefetch`: {}", + loc + ); + } + } else { + throw_unsup_format!("unsupported `llvm.prefetch` type argument: {}", ty); + } + + EmulateItemResult::NeedsReturn + } + // Used to implement the x86 `_mm{,256,512}_popcnt_epi{8,16,32,64}` and wasm + // `{i,u}8x16_popcnt` functions. + name if name.starts_with("llvm.ctpop.v") + && this.tcx.sess.target.endian == Endian::Little => + { + let [op] = this.check_shim_sig_unadjusted(link_name, args)?; + + let (op, op_len) = this.project_to_simd(op)?; + let (dest, dest_len) = this.project_to_simd(&dest)?; + + assert_eq!(dest_len, op_len); + + for i in 0..dest_len { + let op = this.read_immediate(&this.project_index(&op, i)?)?; + // Use `to_uint` to get a zero-extended `u128`. Those + // extra zeros will not affect `count_ones`. + let res = op.to_scalar().to_uint(op.layout.size)?.count_ones(); + + this.write_scalar( + Scalar::from_uint(res, op.layout.size), + &this.project_index(&dest, i)?, + )?; + } + + EmulateItemResult::NeedsReturn + } + + // Target-specific shims + name if name.starts_with("llvm.x86.") + && matches!(this.tcx.sess.target.arch, Arch::X86 | Arch::X86_64) + && this.tcx.sess.target.endian == Endian::Little => + shims::x86::EvalContextExt::emulate_x86_intrinsic(this, link_name, args, &dest)?, + name if name.starts_with("llvm.aarch64.") + && this.tcx.sess.target.arch == Arch::AArch64 + && this.tcx.sess.target.endian == Endian::Little => + shims::aarch64::EvalContextExt::emulate_aarch64_intrinsic( + this, link_name, args, &dest, + )?, + name if name.starts_with("llvm.loongarch.") + && matches!(this.tcx.sess.target.arch, Arch::LoongArch32 | Arch::LoongArch64) + && this.tcx.sess.target.endian == Endian::Little => + shims::loongarch::EvalContextExt::emulate_loongarch_intrinsic( + this, link_name, args, &dest, + )?, + _ => EmulateItemResult::NotSupported, + }; + + // The rest either implements the logic, or falls back to `lookup_exported_symbol`. + match res { + EmulateItemResult::NeedsReturn => { + trace!("{:?}", this.dump_place(&dest.clone().into())); + this.return_to_block(ret) + } + EmulateItemResult::NeedsUnwind => { + // Jump to the unwind block to begin unwinding. + this.unwind_to_block(unwind) + } + EmulateItemResult::AlreadyJumped => interp_ok(()), + EmulateItemResult::NotSupported => { + throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(format!( + "can't call LLVM intrinsic `{link_name}` on architecture `{arch}`", + arch = this.tcx.sess.target.arch, + ))); + } + } + } } impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} @@ -803,83 +915,6 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_pointer(ptr_dest, dest)?; } - // LLVM intrinsics - "llvm.prefetch.p0" => { - let [p, rw, loc, ty] = this.check_shim_sig_unadjusted(link_name, args)?; - - let _ = this.read_pointer(p)?; - let rw = this.read_scalar(rw)?.to_i32()?; - let loc = this.read_scalar(loc)?.to_i32()?; - let ty = this.read_scalar(ty)?.to_i32()?; - - if ty == 1 { - // Data cache prefetch. - // Notably, we do not have to check the pointer, this operation is never UB! - - if !matches!(rw, 0 | 1) { - throw_unsup_format!("invalid `rw` value passed to `llvm.prefetch`: {}", rw); - } - if !matches!(loc, 0..=3) { - throw_unsup_format!( - "invalid `loc` value passed to `llvm.prefetch`: {}", - loc - ); - } - } else { - throw_unsup_format!("unsupported `llvm.prefetch` type argument: {}", ty); - } - } - // Used to implement the x86 `_mm{,256,512}_popcnt_epi{8,16,32,64}` and wasm - // `{i,u}8x16_popcnt` functions. - name if name.starts_with("llvm.ctpop.v") - && this.tcx.sess.target.endian == Endian::Little => - { - let [op] = this.check_shim_sig_unadjusted(link_name, args)?; - - let (op, op_len) = this.project_to_simd(op)?; - let (dest, dest_len) = this.project_to_simd(dest)?; - - assert_eq!(dest_len, op_len); - - for i in 0..dest_len { - let op = this.read_immediate(&this.project_index(&op, i)?)?; - // Use `to_uint` to get a zero-extended `u128`. Those - // extra zeros will not affect `count_ones`. - let res = op.to_scalar().to_uint(op.layout.size)?.count_ones(); - - this.write_scalar( - Scalar::from_uint(res, op.layout.size), - &this.project_index(&dest, i)?, - )?; - } - } - - // Target-specific shims - name if name.starts_with("llvm.x86.") - && matches!(this.tcx.sess.target.arch, Arch::X86 | Arch::X86_64) - && this.tcx.sess.target.endian == Endian::Little => - { - return shims::x86::EvalContextExt::emulate_x86_intrinsic( - this, link_name, args, dest, - ); - } - name if name.starts_with("llvm.aarch64.") - && this.tcx.sess.target.arch == Arch::AArch64 - && this.tcx.sess.target.endian == Endian::Little => - { - return shims::aarch64::EvalContextExt::emulate_aarch64_intrinsic( - this, link_name, args, dest, - ); - } - name if name.starts_with("llvm.loongarch.") - && matches!(this.tcx.sess.target.arch, Arch::LoongArch32 | Arch::LoongArch64) - && this.tcx.sess.target.endian == Endian::Little => - { - return shims::loongarch::EvalContextExt::emulate_loongarch_intrinsic( - this, link_name, args, dest, - ); - } - // Fallback to shims in submodules. _ => { // Math shims From aec727fab4d6ffe0a8b4b561dff392446c99d0d9 Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:16:35 +0000 Subject: [PATCH 16/71] Stop using EmulateItemResult for LLVM intrinsics in miri --- .../src/const_eval/dummy_machine.rs | 1 - .../src/const_eval/machine.rs | 1 - .../rustc_const_eval/src/interpret/call.rs | 1 - .../rustc_const_eval/src/interpret/machine.rs | 1 - src/tools/miri/src/machine.rs | 3 +- src/tools/miri/src/shims/aarch64.rs | 6 ++-- src/tools/miri/src/shims/foreign_items.rs | 33 +++++++------------ src/tools/miri/src/shims/loongarch.rs | 6 ++-- src/tools/miri/src/shims/x86/aesni.rs | 6 ++-- src/tools/miri/src/shims/x86/avx.rs | 6 ++-- src/tools/miri/src/shims/x86/avx2.rs | 6 ++-- src/tools/miri/src/shims/x86/avx512.rs | 6 ++-- src/tools/miri/src/shims/x86/bmi.rs | 8 ++--- src/tools/miri/src/shims/x86/gfni.rs | 6 ++-- src/tools/miri/src/shims/x86/mod.rs | 8 ++--- src/tools/miri/src/shims/x86/sha.rs | 6 ++-- src/tools/miri/src/shims/x86/sse.rs | 6 ++-- src/tools/miri/src/shims/x86/sse2.rs | 6 ++-- src/tools/miri/src/shims/x86/sse3.rs | 6 ++-- src/tools/miri/src/shims/x86/sse41.rs | 6 ++-- src/tools/miri/src/shims/x86/sse42.rs | 8 ++--- src/tools/miri/src/shims/x86/ssse3.rs | 6 ++-- 22 files changed, 64 insertions(+), 78 deletions(-) diff --git a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs index adf4069300e07..b4f54f4c79f51 100644 --- a/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/dummy_machine.rs @@ -111,7 +111,6 @@ impl<'tcx> interpret::Machine<'tcx> for DummyMachine { _args: &[interpret::OpTy<'tcx, Self::Provenance>], _destination: &interpret::PlaceTy<'tcx, Self::Provenance>, _target: Option, - _unwind: UnwindAction, ) -> interpret::InterpResult<'tcx> { unimplemented!() } diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index fa394ab627b32..545a3a3ff523a 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -751,7 +751,6 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { _args: &[OpTy<'tcx>], _dest: &PlaceTy<'tcx, Self::Provenance>, _target: Option, - _unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { let intrinsic_name = ecx.tcx.codegen_fn_attrs(instance.def_id()).symbol_name.unwrap(); diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index fad4c5081d833..80cd892c799cd 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -660,7 +660,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { &Self::copy_fn_args(args), destination, target, - unwind, ) } ty::InstanceKind::Shim(ty::ShimKind::VTable(..)) diff --git a/compiler/rustc_const_eval/src/interpret/machine.rs b/compiler/rustc_const_eval/src/interpret/machine.rs index 1df40dcf21dcb..40251cbc8155b 100644 --- a/compiler/rustc_const_eval/src/interpret/machine.rs +++ b/compiler/rustc_const_eval/src/interpret/machine.rs @@ -255,7 +255,6 @@ pub trait Machine<'tcx>: Sized { args: &[OpTy<'tcx, Self::Provenance>], destination: &PlaceTy<'tcx, Self::Provenance>, target: Option, - unwind: mir::UnwindAction, ) -> InterpResult<'tcx>; /// Check whether the given function may be executed on the current machine, in terms of the diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs index 1654e1040310a..cdf7dcda2444f 100644 --- a/src/tools/miri/src/machine.rs +++ b/src/tools/miri/src/machine.rs @@ -1307,9 +1307,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> { args: &[OpTy<'tcx>], dest: &PlaceTy<'tcx>, ret: Option, - unwind: mir::UnwindAction, ) -> InterpResult<'tcx, ()> { - ecx.call_llvm_intrinsic(instance, args, dest, ret, unwind) + ecx.call_llvm_intrinsic(instance, args, dest, ret) } #[inline(always)] diff --git a/src/tools/miri/src/shims/aarch64.rs b/src/tools/miri/src/shims/aarch64.rs index 290b8e108f3e6..afaace872b52b 100644 --- a/src/tools/miri/src/shims/aarch64.rs +++ b/src/tools/miri/src/shims/aarch64.rs @@ -13,7 +13,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.aarch64.").unwrap(); @@ -273,8 +273,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_u128(result), &dest)?; } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs index 462007855f23a..cef23e6648964 100644 --- a/src/tools/miri/src/shims/foreign_items.rs +++ b/src/tools/miri/src/shims/foreign_items.rs @@ -249,7 +249,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { args: &[OpTy<'tcx>], dest: &PlaceTy<'tcx>, ret: Option, - unwind: mir::UnwindAction, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -258,7 +257,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // FIXME: avoid allocating memory let dest = this.force_allocation(dest)?; - let res = match link_name.as_str() { + let handled = match link_name.as_str() { // LLVM intrinsics "llvm.prefetch.p0" => { let [p, rw, loc, ty] = this.check_shim_sig_unadjusted(link_name, args)?; @@ -285,7 +284,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { throw_unsup_format!("unsupported `llvm.prefetch` type argument: {}", ty); } - EmulateItemResult::NeedsReturn + true } // Used to implement the x86 `_mm{,256,512}_popcnt_epi{8,16,32,64}` and wasm // `{i,u}8x16_popcnt` functions. @@ -311,7 +310,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { )?; } - EmulateItemResult::NeedsReturn + true } // Target-specific shims @@ -331,26 +330,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { shims::loongarch::EvalContextExt::emulate_loongarch_intrinsic( this, link_name, args, &dest, )?, - _ => EmulateItemResult::NotSupported, + _ => false, }; // The rest either implements the logic, or falls back to `lookup_exported_symbol`. - match res { - EmulateItemResult::NeedsReturn => { - trace!("{:?}", this.dump_place(&dest.clone().into())); - this.return_to_block(ret) - } - EmulateItemResult::NeedsUnwind => { - // Jump to the unwind block to begin unwinding. - this.unwind_to_block(unwind) - } - EmulateItemResult::AlreadyJumped => interp_ok(()), - EmulateItemResult::NotSupported => { - throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(format!( - "can't call LLVM intrinsic `{link_name}` on architecture `{arch}`", - arch = this.tcx.sess.target.arch, - ))); - } + if handled { + trace!("{:?}", this.dump_place(&dest.clone().into())); + this.return_to_block(ret) + } else { + throw_machine_stop!(TerminationInfo::UnsupportedForeignItem(format!( + "can't call LLVM intrinsic `{link_name}` on architecture `{arch}`", + arch = this.tcx.sess.target.arch, + ))); } } } diff --git a/src/tools/miri/src/shims/loongarch.rs b/src/tools/miri/src/shims/loongarch.rs index 461dd08dd2157..d45581c4c09f9 100644 --- a/src/tools/miri/src/shims/loongarch.rs +++ b/src/tools/miri/src/shims/loongarch.rs @@ -11,7 +11,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.loongarch.").unwrap(); @@ -67,8 +67,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = compute_crc32(crc, data, bit_size, polynomial); this.write_scalar(Scalar::from_u32(result), dest)?; } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/x86/aesni.rs b/src/tools/miri/src/shims/x86/aesni.rs index 0ae76b62d2624..b706639c75ead 100644 --- a/src/tools/miri/src/shims/x86/aesni.rs +++ b/src/tools/miri/src/shims/x86/aesni.rs @@ -10,7 +10,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "aes")?; // Prefix should have already been checked. @@ -112,9 +112,9 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } // TODO: Implement the `llvm.x86.aesni.aeskeygenassist` when possible // with an external crate. - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/x86/avx.rs b/src/tools/miri/src/shims/x86/avx.rs index 25fe93a20f9db..aad278948338f 100644 --- a/src/tools/miri/src/shims/x86/avx.rs +++ b/src/tools/miri/src/shims/x86/avx.rs @@ -14,7 +14,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "avx")?; // Prefix should have already been checked. @@ -243,8 +243,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The only thing that needs to be ensured is the correct calling convention. let [] = this.check_shim_sig_unadjusted(link_name, args)?; } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/x86/avx2.rs b/src/tools/miri/src/shims/x86/avx2.rs index 160bce2dec98b..3ee66862cc5cf 100644 --- a/src/tools/miri/src/shims/x86/avx2.rs +++ b/src/tools/miri/src/shims/x86/avx2.rs @@ -14,7 +14,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "avx2")?; // Prefix should have already been checked. @@ -216,8 +216,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { pmaddwd(this, left, right, dest)?; } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/x86/avx512.rs b/src/tools/miri/src/shims/x86/avx512.rs index 6dde7558d829c..d69b1ff112753 100644 --- a/src/tools/miri/src/shims/x86/avx512.rs +++ b/src/tools/miri/src/shims/x86/avx512.rs @@ -12,7 +12,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.avx512.").unwrap(); @@ -178,9 +178,9 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { packusdw(this, a, b, dest)?; } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/x86/bmi.rs b/src/tools/miri/src/shims/x86/bmi.rs index b14d69199ab6b..4e82d7a6693fa 100644 --- a/src/tools/miri/src/shims/x86/bmi.rs +++ b/src/tools/miri/src/shims/x86/bmi.rs @@ -10,7 +10,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); // Prefix should have already been checked. @@ -29,7 +29,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.expect_target_feature_for_intrinsic(link_name, target_feature)?; if is_64_bit && this.tcx.sess.target.arch != Arch::X86_64 { - return interp_ok(EmulateItemResult::NotSupported); + return interp_ok(false); } let [left, right] = this.check_shim_sig_unadjusted(link_name, args)?; @@ -92,7 +92,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } result } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), }; let result = if is_64_bit { @@ -102,6 +102,6 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; this.write_scalar(result, dest)?; - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/x86/gfni.rs b/src/tools/miri/src/shims/x86/gfni.rs index 562d9f76ffc6e..71ba283992c62 100644 --- a/src/tools/miri/src/shims/x86/gfni.rs +++ b/src/tools/miri/src/shims/x86/gfni.rs @@ -9,7 +9,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); // Prefix should have already been checked. @@ -58,9 +58,9 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_u8(gf2p8_mul(left, right)), &dest)?; } } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs index d76d35cb722bc..c320d5af9b976 100644 --- a/src/tools/miri/src/shims/x86/mod.rs +++ b/src/tools/miri/src/shims/x86/mod.rs @@ -30,7 +30,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); // Prefix should have already been checked. let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.").unwrap(); @@ -42,7 +42,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // https://www.intel.com/content/www/us/en/docs/cpp-compiler/developer-guide-reference/2021-8/subborrow-u32-subborrow-u64.html "addcarry.32" | "addcarry.64" | "subborrow.32" | "subborrow.64" => { if unprefixed_name.ends_with("64") && this.tcx.sess.target.arch != Arch::X86_64 { - return interp_ok(EmulateItemResult::NotSupported); + return interp_ok(false); } let [cb_in, a, b] = this.check_shim_sig_unadjusted(link_name, args)?; @@ -147,9 +147,9 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ); } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/x86/sha.rs b/src/tools/miri/src/shims/x86/sha.rs index 982e3a08e4826..3809e23ce8bb4 100644 --- a/src/tools/miri/src/shims/x86/sha.rs +++ b/src/tools/miri/src/shims/x86/sha.rs @@ -15,7 +15,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "sha")?; // Prefix should have already been checked. @@ -104,9 +104,9 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let result = sha256msg2(a, b); write(this, &dest, result)?; } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/x86/sse.rs b/src/tools/miri/src/shims/x86/sse.rs index 81078bda99c8c..c6e0c7d9a7b59 100644 --- a/src/tools/miri/src/shims/x86/sse.rs +++ b/src/tools/miri/src/shims/x86/sse.rs @@ -14,7 +14,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "sse")?; // Prefix should have already been checked. @@ -171,8 +171,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_immediate(*res, dest)?; } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs index 2ee4d276a3af6..f0017b443d0a3 100644 --- a/src/tools/miri/src/shims/x86/sse2.rs +++ b/src/tools/miri/src/shims/x86/sse2.rs @@ -14,7 +14,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "sse2")?; // Prefix should have already been checked. @@ -272,8 +272,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { pmaddwd(this, left, right, dest)?; } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/x86/sse3.rs b/src/tools/miri/src/shims/x86/sse3.rs index 5890af49088df..c470ad54b20a4 100644 --- a/src/tools/miri/src/shims/x86/sse3.rs +++ b/src/tools/miri/src/shims/x86/sse3.rs @@ -9,7 +9,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "sse3")?; // Prefix should have already been checked. @@ -28,8 +28,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.mem_copy(src_ptr, dest.ptr(), dest.layout.size, /*nonoverlapping*/ true)?; } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/x86/sse41.rs b/src/tools/miri/src/shims/x86/sse41.rs index aee2e115a0ac5..68aa0f71160e2 100644 --- a/src/tools/miri/src/shims/x86/sse41.rs +++ b/src/tools/miri/src/shims/x86/sse41.rs @@ -10,7 +10,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "sse4.1")?; // Prefix should have already been checked. @@ -155,8 +155,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(Scalar::from_i32(res.into()), dest)?; } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/x86/sse42.rs b/src/tools/miri/src/shims/x86/sse42.rs index f6152c60f8370..af28897305bb9 100644 --- a/src/tools/miri/src/shims/x86/sse42.rs +++ b/src/tools/miri/src/shims/x86/sse42.rs @@ -279,7 +279,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "sse4.2")?; // Prefix should have already been checked. @@ -428,7 +428,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; if bit_size == 64 && this.tcx.sess.target.arch != Arch::X86_64 { - return interp_ok(EmulateItemResult::NotSupported); + return interp_ok(false); } let [left, right] = this.check_shim_sig_unadjusted(link_name, args)?; @@ -460,8 +460,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.write_scalar(result, dest)?; } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } diff --git a/src/tools/miri/src/shims/x86/ssse3.rs b/src/tools/miri/src/shims/x86/ssse3.rs index 5b4746e5b1a04..dadd0cc7315a8 100644 --- a/src/tools/miri/src/shims/x86/ssse3.rs +++ b/src/tools/miri/src/shims/x86/ssse3.rs @@ -11,7 +11,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { link_name: Symbol, args: &[OpTy<'tcx>], dest: &MPlaceTy<'tcx>, - ) -> InterpResult<'tcx, EmulateItemResult> { + ) -> InterpResult<'tcx, bool> { let this = self.eval_context_mut(); this.expect_target_feature_for_intrinsic(link_name, "ssse3")?; // Prefix should have already been checked. @@ -67,8 +67,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { psign(this, left, right, dest)?; } - _ => return interp_ok(EmulateItemResult::NotSupported), + _ => return interp_ok(false), } - interp_ok(EmulateItemResult::NeedsReturn) + interp_ok(true) } } From 8583c9abebbe1f188e2795567145f79333108b30 Mon Sep 17 00:00:00 2001 From: Joel-Wwalker Date: Mon, 13 Jul 2026 09:28:04 -0400 Subject: [PATCH 17/71] Add regression test for issue 95719 --- .../send-bound-nested-async-95719.rs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/ui/type-alias-impl-trait/send-bound-nested-async-95719.rs diff --git a/tests/ui/type-alias-impl-trait/send-bound-nested-async-95719.rs b/tests/ui/type-alias-impl-trait/send-bound-nested-async-95719.rs new file mode 100644 index 0000000000000..9f73dea641197 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/send-bound-nested-async-95719.rs @@ -0,0 +1,53 @@ +// Regression test for . +// The `Send` bound of a GAT `impl Future` was lost when the future was +// wrapped in another `async fn`, failing with "the parameter type `G` may +// not live long enough". +//@ check-pass +//@ edition: 2021 + +#![feature(impl_trait_in_assoc_type)] + +use std::future::Future; + +pub trait Get: Send + Sync { + type Ret<'a>: Future + Send + 'a + where + Self: 'a; + fn get<'a>(&'a self) -> Self::Ret<'a> + where + Self: 'a; +} + +impl Get for usize { + type Ret<'a> + = impl Future + Send + 'a + where + Self: 'a; + + fn get<'a>(&'a self) -> Self::Ret<'a> + where + Self: 'a, + { + async move { *self } + } +} + +fn is_send + Send>(_f: &F) -> bool { + true +} + +async fn wrap(g: &G) -> usize { + let fut = g.get(); + assert!(is_send(&fut)); + fut.await +} + +async fn wrap_wrap(g: &G) -> usize { + let fut = wrap(g); + assert!(is_send(&fut)); + fut.await +} + +fn main() { + let _ = wrap_wrap(&0usize); +} From 09882ef10bb0083619f7dda61b9f5c08c47ac419 Mon Sep 17 00:00:00 2001 From: Tim Vilgot Mikael Fredenberg Date: Mon, 13 Jul 2026 19:41:25 +0200 Subject: [PATCH 18/71] inline Once wait and wait_force --- library/std/src/sync/once.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/std/src/sync/once.rs b/library/std/src/sync/once.rs index 2556d1897b642..d37e57e399660 100644 --- a/library/std/src/sync/once.rs +++ b/library/std/src/sync/once.rs @@ -296,6 +296,7 @@ impl Once { /// If this [`Once`] has been poisoned because an initialization closure has /// panicked, this method will also panic. Use [`wait_force`](Self::wait_force) /// if this behavior is not desired. + #[inline] #[stable(feature = "once_wait", since = "1.86.0")] #[rustc_should_not_be_called_on_const_items] pub fn wait(&self) { @@ -309,6 +310,7 @@ impl Once { /// /// If this [`Once`] has been poisoned, this function blocks until it /// becomes completed, unlike [`Once::wait()`], which panics in this case. + #[inline] #[stable(feature = "once_wait", since = "1.86.0")] #[rustc_should_not_be_called_on_const_items] pub fn wait_force(&self) { From 9561a5236924cf3ebd13129be2f767f58ffc1195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 13 Jul 2026 22:43:16 +0200 Subject: [PATCH 19/71] Bump rustc-perf submodule --- src/tools/rustc-perf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rustc-perf b/src/tools/rustc-perf index dec492af8eb74..a134d7f67336c 160000 --- a/src/tools/rustc-perf +++ b/src/tools/rustc-perf @@ -1 +1 @@ -Subproject commit dec492af8eb74903879bd356648fc42d7aaf8555 +Subproject commit a134d7f67336c1178aa28b09063f86e28bb42cd6 From 5f35fc8b8f466c0112b833e6dc375289f986cc4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Mon, 13 Jul 2026 22:48:25 +0000 Subject: [PATCH 20/71] Replace shortened type with `_` instead of `...` as placeholder --- compiler/rustc_middle/src/ty/print/pretty.rs | 2 +- tests/ui/codegen/overflow-during-mono.stderr | 4 ++-- ...hr_alias_normalization_leaking_vars.stderr | 2 +- tests/ui/diagnostic-width/E0271.ascii.stderr | 6 +++--- .../ui/diagnostic-width/E0271.unicode.stderr | 6 +++--- tests/ui/diagnostic-width/binop.rs | 4 ++-- tests/ui/diagnostic-width/binop.stderr | 8 ++++---- .../diagnostic-width/long-E0308.ascii.stderr | 20 +++++++++---------- .../long-E0308.unicode.stderr | 20 +++++++++---------- tests/ui/diagnostic-width/long-E0529.rs | 4 ++-- tests/ui/diagnostic-width/long-E0529.stderr | 4 ++-- tests/ui/diagnostic-width/long-E0609.rs | 2 +- tests/ui/diagnostic-width/long-E0609.stderr | 2 +- tests/ui/diagnostic-width/long-E0614.rs | 2 +- tests/ui/diagnostic-width/long-E0614.stderr | 2 +- tests/ui/diagnostic-width/long-E0618.rs | 4 ++-- tests/ui/diagnostic-width/long-E0618.stderr | 4 ++-- tests/ui/diagnostic-width/long-e0277.rs | 2 +- tests/ui/diagnostic-width/long-e0277.stderr | 4 ++-- .../non-copy-type-moved.stderr | 2 +- .../secondary-label-with-long-type.rs | 4 ++-- .../secondary-label-with-long-type.stderr | 6 +++--- .../dropck_no_diverge_on_nonregular_1.stderr | 2 +- tests/ui/error-codes/E0275.stderr | 4 ++-- .../hang-on-deeply-nested-dyn.stderr | 2 +- ...inding-without-sufficient-type-info.stderr | 2 +- ...te-instantiation-struct-tail-ice-114484.rs | 2 +- ...nstantiation-struct-tail-ice-114484.stderr | 8 ++++---- .../ui/infinite/infinite-instantiation.stderr | 2 +- .../issue-37311.stderr | 2 +- .../inherent-impls-overflow.current.stderr | 6 +++--- .../inherent-impls-overflow.rs | 6 +++--- .../type-length-limit-enforcement.stderr | 2 +- .../ui/methods/inherent-bound-in-probe.stderr | 2 +- .../probe-error-on-infinite-deref.stderr | 2 +- .../mismatch-sugg-for-shorthand-field.stderr | 2 +- .../dyn-trait-ice-153366.stderr | 2 +- ...inite-function-recursion-error-8727.stderr | 2 +- tests/ui/recursion/issue-23122-2.stderr | 4 ++-- ...-38591-non-regular-dropck-recursion.stderr | 2 +- tests/ui/recursion/issue-83150.stderr | 2 +- tests/ui/recursion/recursion.stderr | 2 +- ...ve-impl-trait-iterator-by-ref-67552.stderr | 2 +- .../box-future-wrong-output.stderr | 2 +- .../expected-boxed-future-isnt-pinned.stderr | 6 +++--- tests/ui/suggestions/issue-107860.stderr | 2 +- ...associated-error-bound-issue-145586.stderr | 2 +- tests/ui/traits/issue-68295.stderr | 2 +- .../issue-91949-hangs-on-recursion.stderr | 2 +- .../next-solver/overflow/global-cache.stderr | 2 +- .../traits/on_unimplemented_long_types.stderr | 6 +++--- ...pl-overflow-with-where-clause-20413.stderr | 6 +++--- tests/ui/trimmed-paths/doc-hidden.rs | 4 ++-- tests/ui/trimmed-paths/doc-hidden.stderr | 4 ++-- tests/ui/typeck/issue-107775.stderr | 2 +- 55 files changed, 107 insertions(+), 107 deletions(-) diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 2d45b6feb59f0..e26c448cb75ee 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -2357,7 +2357,7 @@ impl<'tcx> Printer<'tcx> for FmtPrinter<'_, 'tcx> { { // We only truncate types that we know are likely to be much longer than 3 chars. // There's no point in replacing `i32` or `!`. - write!(self, "...")?; + write!(self, "_")?; Ok(()) } _ => { diff --git a/tests/ui/codegen/overflow-during-mono.stderr b/tests/ui/codegen/overflow-during-mono.stderr index 1559de757e7ba..4d631e255afe5 100644 --- a/tests/ui/codegen/overflow-during-mono.stderr +++ b/tests/ui/codegen/overflow-during-mono.stderr @@ -3,8 +3,8 @@ error[E0275]: overflow evaluating the requirement `for<'a> {closure@$DIR/overflo = help: consider increasing the recursion limit by adding a `#![recursion_limit = "64"]` attribute to your crate (`overflow_during_mono`) = note: required for `Filter, {closure@overflow-during-mono.rs:14:41}>` to implement `Iterator` = note: 31 redundant requirements hidden - = note: required for `Filter, ...>, ...>, ...>, ...>` to implement `Iterator` - = note: required for `Filter, ...>, ...>, ...>, ...>` to implement `IntoIterator` + = note: required for `Filter, _>, _>, _>, _>, _>` to implement `Iterator` + = note: required for `Filter, _>, _>, _>, _>, _>` to implement `IntoIterator` = note: the full name for the type has been written to '$TEST_BUILD_DIR/overflow-during-mono.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/coercion/hr_alias_normalization_leaking_vars.stderr b/tests/ui/coercion/hr_alias_normalization_leaking_vars.stderr index 54da352c65039..c549748f2d0d1 100644 --- a/tests/ui/coercion/hr_alias_normalization_leaking_vars.stderr +++ b/tests/ui/coercion/hr_alias_normalization_leaking_vars.stderr @@ -16,7 +16,7 @@ error[E0308]: mismatched types --> $DIR/hr_alias_normalization_leaking_vars.rs:34:36 | LL | LendingIterator::for_each(&(), f); - | ------------------------- ^ expected `Box`, found fn item + | ------------------------- ^ expected `Box`, found fn item | | | arguments to this function are incorrect | diff --git a/tests/ui/diagnostic-width/E0271.ascii.stderr b/tests/ui/diagnostic-width/E0271.ascii.stderr index ad5f53e44beab..f1e9a9366925a 100644 --- a/tests/ui/diagnostic-width/E0271.ascii.stderr +++ b/tests/ui/diagnostic-width/E0271.ascii.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving ` as Future>::Error == Foo` +error[E0271]: type mismatch resolving ` as Future>::Error == Foo` --> $DIR/E0271.rs:19:5 | LL | / Box::new( @@ -7,14 +7,14 @@ LL | | Err::<(), _>( LL | | Ok::<_, ()>( ... | LL | | ) - | |_____^ type mismatch resolving ` as Future>::Error == Foo` + | |_____^ type mismatch resolving ` as Future>::Error == Foo` | note: expected this to be `Foo` --> $DIR/E0271.rs:9:18 | LL | type Error = E; | ^ - = note: required for the cast from `Box>` to `Box<...>` + = note: required for the cast from `Box>` to `Box<_>` = note: the full name for the type has been written to '$TEST_BUILD_DIR/E0271.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/diagnostic-width/E0271.unicode.stderr b/tests/ui/diagnostic-width/E0271.unicode.stderr index 91adf83410163..ecb09c53a76c6 100644 --- a/tests/ui/diagnostic-width/E0271.unicode.stderr +++ b/tests/ui/diagnostic-width/E0271.unicode.stderr @@ -1,4 +1,4 @@ -error[E0271]: type mismatch resolving ` as Future>::Error == Foo` +error[E0271]: type mismatch resolving ` as Future>::Error == Foo` ╭▸ $DIR/E0271.rs:19:5 │ LL │ ┏ Box::new( @@ -7,14 +7,14 @@ LL │ ┃ Err::<(), _>( LL │ ┃ Ok::<_, ()>( ‡ ┃ LL │ ┃ ) - │ ┗━━━━━┛ type mismatch resolving ` as Future>::Error == Foo` + │ ┗━━━━━┛ type mismatch resolving ` as Future>::Error == Foo` ╰╴ note: expected this to be `Foo` ╭▸ $DIR/E0271.rs:9:18 │ LL │ type Error = E; │ ━ - ├ note: required for the cast from `Box>` to `Box<...>` + ├ note: required for the cast from `Box>` to `Box<_>` ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/E0271.long-type-$LONG_TYPE_HASH.txt' ╰ note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/diagnostic-width/binop.rs b/tests/ui/diagnostic-width/binop.rs index 9e4e837f38698..fc6e47b0fdecc 100644 --- a/tests/ui/diagnostic-width/binop.rs +++ b/tests/ui/diagnostic-width/binop.rs @@ -5,11 +5,11 @@ type C = (B, B, B, B); type D = (C, C, C, C); fn foo(x: D) { - x + x; //~ ERROR cannot add `(... + x + x; //~ ERROR cannot add `((_ } fn bar(x: D) { - !x; //~ ERROR cannot apply unary operator `!` to type `(... + !x; //~ ERROR cannot apply unary operator `!` to type `((_ } fn main() {} diff --git a/tests/ui/diagnostic-width/binop.stderr b/tests/ui/diagnostic-width/binop.stderr index 92723df5a9b35..adf168ffb06f1 100644 --- a/tests/ui/diagnostic-width/binop.stderr +++ b/tests/ui/diagnostic-width/binop.stderr @@ -1,15 +1,15 @@ -error[E0369]: cannot add `(..., ..., ..., ...)` to `(..., ..., ..., ...)` +error[E0369]: cannot add `((_, _, _, _), _, _, _)` to `((_, _, _, _), _, _, _)` --> $DIR/binop.rs:8:7 | LL | x + x; - | - ^ - (..., ..., ..., ...) + | - ^ - ((_, _, _, _), _, _, _) | | - | (..., ..., ..., ...) + | ((_, _, _, _), _, _, _) | = note: the full name for the type has been written to '$TEST_BUILD_DIR/binop.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console -error[E0600]: cannot apply unary operator `!` to type `(..., ..., ..., ...)` +error[E0600]: cannot apply unary operator `!` to type `((_, _, _, _), _, _, _)` --> $DIR/binop.rs:12:5 | LL | !x; diff --git a/tests/ui/diagnostic-width/long-E0308.ascii.stderr b/tests/ui/diagnostic-width/long-E0308.ascii.stderr index d1fdd6c443352..3acf3829c352e 100644 --- a/tests/ui/diagnostic-width/long-E0308.ascii.stderr +++ b/tests/ui/diagnostic-width/long-E0308.ascii.stderr @@ -16,10 +16,10 @@ LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok... LL | | Ok("") LL | | )))))))))))))))))))))))))))))) LL | | )))))))))))))))))))))))))))))); - | |__________________________________^ expected `Atype, i32>, i32>`, found `Result, _>, _>` + | |__________________________________^ expected `Atype, i32>, i32>`, found `Result, _>, _>` | - = note: expected struct `Atype, i32>` - found enum `Result, _>` + = note: expected struct `Atype, i32>` + found enum `Result, _>` = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console @@ -32,10 +32,10 @@ LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(... LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(""))))))) LL | | )))))))))))))))))))))))))))))) LL | | )))))))))))))))))))))))); - | |____________________________^ expected `Option>, _>>`, found `Result, _>, _>` + | |____________________________^ expected `Option>, _>>`, found `Result, _>, _>` | - = note: expected enum `Option, _>>` - found enum `Result, _>` + = note: expected enum `Option, _>>` + found enum `Result, _>` = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console @@ -50,11 +50,11 @@ LL | | Atype< ... | LL | | i32 LL | | > = (); - | | - ^^ expected `Atype, i32>, i32>`, found `()` + | | - ^^ expected `Atype, i32>, i32>`, found `()` | |_____| | expected due to this | - = note: expected struct `Atype, i32>` + = note: expected struct `Atype, i32>` found unit type `()` = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console @@ -70,10 +70,10 @@ LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(... LL | | Ok(Ok(Ok(Ok(Ok(Ok(Ok(""))))))) LL | | )))))))))))))))))))))))))))))) LL | | )))))))))))))))))))))))); - | |____________________________^ expected `()`, found `Result, _>, _>` + | |____________________________^ expected `()`, found `Result, _>, _>` | = note: expected unit type `()` - found enum `Result, _>` + found enum `Result, _>` = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/diagnostic-width/long-E0308.unicode.stderr b/tests/ui/diagnostic-width/long-E0308.unicode.stderr index 69e5ca100671e..4cd09b66eee90 100644 --- a/tests/ui/diagnostic-width/long-E0308.unicode.stderr +++ b/tests/ui/diagnostic-width/long-E0308.unicode.stderr @@ -16,10 +16,10 @@ LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(O… LL │ ┃ Ok("") LL │ ┃ )))))))))))))))))))))))))))))) LL │ ┃ )))))))))))))))))))))))))))))); - │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `Atype, i32>, i32>`, found `Result, _>, _>` + │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `Atype, i32>, i32>`, found `Result, _>, _>` │ - ├ note: expected struct `Atype, i32>` - │ found enum `Result, _>` + ├ note: expected struct `Atype, i32>` + │ found enum `Result, _>` ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' ╰ note: consider using `--verbose` to print the full type name to the console @@ -32,10 +32,10 @@ LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok… LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(""))))))) LL │ ┃ )))))))))))))))))))))))))))))) LL │ ┃ )))))))))))))))))))))))); - │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `Option>, _>>`, found `Result, _>, _>` + │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `Option>, _>>`, found `Result, _>, _>` │ - ├ note: expected enum `Option, _>>` - │ found enum `Result, _>` + ├ note: expected enum `Option, _>>` + │ found enum `Result, _>` ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' ╰ note: consider using `--verbose` to print the full type name to the console @@ -50,11 +50,11 @@ LL │ │ Atype< ‡ │ LL │ │ i32 LL │ │ > = (); - │ │ │ ━━ expected `Atype, i32>, i32>`, found `()` + │ │ │ ━━ expected `Atype, i32>, i32>`, found `()` │ └─────┤ │ expected due to this │ - ├ note: expected struct `Atype, i32>` + ├ note: expected struct `Atype, i32>` │ found unit type `()` ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' ╰ note: consider using `--verbose` to print the full type name to the console @@ -70,10 +70,10 @@ LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok(Ok… LL │ ┃ Ok(Ok(Ok(Ok(Ok(Ok(Ok(""))))))) LL │ ┃ )))))))))))))))))))))))))))))) LL │ ┃ )))))))))))))))))))))))); - │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `()`, found `Result, _>, _>` + │ ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛ expected `()`, found `Result, _>, _>` │ ├ note: expected unit type `()` - │ found enum `Result, _>` + │ found enum `Result, _>` ├ note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0308.long-type-$LONG_TYPE_HASH.txt' ╰ note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/diagnostic-width/long-E0529.rs b/tests/ui/diagnostic-width/long-E0529.rs index 4146d3be40fe0..3930c1aa303d8 100644 --- a/tests/ui/diagnostic-width/long-E0529.rs +++ b/tests/ui/diagnostic-width/long-E0529.rs @@ -7,8 +7,8 @@ type C = (B, B, B, B); type D = (C, C, C, C); fn foo(x: D) { - let [] = x; //~ ERROR expected an array or slice, found `(... - //~^ NOTE pattern cannot match with input type `(... + let [] = x; //~ ERROR expected an array or slice, found `((_ + //~^ NOTE pattern cannot match with input type `((_ } fn main() {} diff --git a/tests/ui/diagnostic-width/long-E0529.stderr b/tests/ui/diagnostic-width/long-E0529.stderr index e5b82b592712f..bd0ba90fa930e 100644 --- a/tests/ui/diagnostic-width/long-E0529.stderr +++ b/tests/ui/diagnostic-width/long-E0529.stderr @@ -1,8 +1,8 @@ -error[E0529]: expected an array or slice, found `(..., ..., ..., ...)` +error[E0529]: expected an array or slice, found `((_, _, _, _), _, _, _)` --> $DIR/long-E0529.rs:10:9 | LL | let [] = x; - | ^^ pattern cannot match with input type `(..., ..., ..., ...)` + | ^^ pattern cannot match with input type `((_, _, _, _), _, _, _)` | = note: the full name for the type has been written to '$TEST_BUILD_DIR/long-E0529.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/diagnostic-width/long-E0609.rs b/tests/ui/diagnostic-width/long-E0609.rs index a26d16ad12e78..d9c4417ad93cc 100644 --- a/tests/ui/diagnostic-width/long-E0609.rs +++ b/tests/ui/diagnostic-width/long-E0609.rs @@ -6,7 +6,7 @@ type C = (B, B, B, B); type D = (C, C, C, C); fn foo(x: D) { - x.field; //~ ERROR no field `field` on type `(... + x.field; //~ ERROR no field `field` on type `((_ } fn main() {} diff --git a/tests/ui/diagnostic-width/long-E0609.stderr b/tests/ui/diagnostic-width/long-E0609.stderr index 70092ea34bc13..894f7ef131eef 100644 --- a/tests/ui/diagnostic-width/long-E0609.stderr +++ b/tests/ui/diagnostic-width/long-E0609.stderr @@ -1,4 +1,4 @@ -error[E0609]: no field `field` on type `(..., ..., ..., ...)` +error[E0609]: no field `field` on type `((_, _, _, _), _, _, _)` --> $DIR/long-E0609.rs:9:7 | LL | x.field; diff --git a/tests/ui/diagnostic-width/long-E0614.rs b/tests/ui/diagnostic-width/long-E0614.rs index 5e8b3324dd17d..81e1a5cb4a4c3 100644 --- a/tests/ui/diagnostic-width/long-E0614.rs +++ b/tests/ui/diagnostic-width/long-E0614.rs @@ -6,7 +6,7 @@ type C = (B, B, B, B); type D = (C, C, C, C); fn foo(x: D) { - *x; //~ ERROR type `(... + *x; //~ ERROR type `((_ } fn main() {} diff --git a/tests/ui/diagnostic-width/long-E0614.stderr b/tests/ui/diagnostic-width/long-E0614.stderr index 18da20da9453e..16c2f0f567a32 100644 --- a/tests/ui/diagnostic-width/long-E0614.stderr +++ b/tests/ui/diagnostic-width/long-E0614.stderr @@ -1,4 +1,4 @@ -error[E0614]: type `(..., ..., ..., ...)` cannot be dereferenced +error[E0614]: type `((_, _, _, _), _, _, _)` cannot be dereferenced --> $DIR/long-E0614.rs:9:5 | LL | *x; diff --git a/tests/ui/diagnostic-width/long-E0618.rs b/tests/ui/diagnostic-width/long-E0618.rs index 247061d17f836..c6ee331ba2387 100644 --- a/tests/ui/diagnostic-width/long-E0618.rs +++ b/tests/ui/diagnostic-width/long-E0618.rs @@ -6,8 +6,8 @@ type B = (A, A, A, A); type C = (B, B, B, B); type D = (C, C, C, C); -fn foo(x: D) { //~ NOTE `x` has type `(... - x(); //~ ERROR expected function, found `(... +fn foo(x: D) { //~ NOTE `x` has type `((_ + x(); //~ ERROR expected function, found `((_ } fn main() {} diff --git a/tests/ui/diagnostic-width/long-E0618.stderr b/tests/ui/diagnostic-width/long-E0618.stderr index 7d92b94faf8fe..2a2d13db35332 100644 --- a/tests/ui/diagnostic-width/long-E0618.stderr +++ b/tests/ui/diagnostic-width/long-E0618.stderr @@ -1,8 +1,8 @@ -error[E0618]: expected function, found `(..., ..., ..., ...)` +error[E0618]: expected function, found `((_, _, _, _), _, _, _)` --> $DIR/long-E0618.rs:10:5 | LL | fn foo(x: D) { - | - `x` has type `(..., ..., ..., ...)` + | - `x` has type `((_, _, _, _), _, _, _)` LL | x(); | ^-- | | diff --git a/tests/ui/diagnostic-width/long-e0277.rs b/tests/ui/diagnostic-width/long-e0277.rs index 369fd8daea78c..7c6a3f56c313f 100644 --- a/tests/ui/diagnostic-width/long-e0277.rs +++ b/tests/ui/diagnostic-width/long-e0277.rs @@ -9,5 +9,5 @@ trait Trait {} fn require_trait() {} fn main() { - require_trait::(); //~ ERROR the trait bound `(... + require_trait::(); //~ ERROR the trait bound `((_ } diff --git a/tests/ui/diagnostic-width/long-e0277.stderr b/tests/ui/diagnostic-width/long-e0277.stderr index ff8971511653c..130ff1228aa7d 100644 --- a/tests/ui/diagnostic-width/long-e0277.stderr +++ b/tests/ui/diagnostic-width/long-e0277.stderr @@ -1,10 +1,10 @@ -error[E0277]: the trait bound `(..., ..., ..., ...): Trait` is not satisfied +error[E0277]: the trait bound `((_, _, _, _), _, _, _): Trait` is not satisfied --> $DIR/long-e0277.rs:12:21 | LL | require_trait::(); | ^ unsatisfied trait bound | - = help: the trait `Trait` is not implemented for `(..., ..., ..., ...)` + = help: the trait `Trait` is not implemented for `((_, _, _, _), _, _, _)` help: this trait has no implementations, consider adding one --> $DIR/long-e0277.rs:7:1 | diff --git a/tests/ui/diagnostic-width/non-copy-type-moved.stderr b/tests/ui/diagnostic-width/non-copy-type-moved.stderr index 16c01c858b7d0..a9b7685299e8e 100644 --- a/tests/ui/diagnostic-width/non-copy-type-moved.stderr +++ b/tests/ui/diagnostic-width/non-copy-type-moved.stderr @@ -2,7 +2,7 @@ error[E0382]: use of moved value: `x` --> $DIR/non-copy-type-moved.rs:14:14 | LL | fn foo(x: D) { - | - move occurs because `x` has type `(..., ..., ..., ...)`, which does not implement the `Copy` trait + | - move occurs because `x` has type `((_, _, _, _), _, _, _)`, which does not implement the `Copy` trait LL | let _a = x; | - value moved here LL | let _b = x; diff --git a/tests/ui/diagnostic-width/secondary-label-with-long-type.rs b/tests/ui/diagnostic-width/secondary-label-with-long-type.rs index 13fe967ba5f8c..c72ab77dfffd4 100644 --- a/tests/ui/diagnostic-width/secondary-label-with-long-type.rs +++ b/tests/ui/diagnostic-width/secondary-label-with-long-type.rs @@ -7,8 +7,8 @@ type D = (C, C, C, C); fn foo(x: D) { let () = x; //~ ERROR mismatched types - //~^ NOTE this expression has type `((..., - //~| NOTE expected `((..., + //~^ NOTE this expression has type `(((_, + //~| NOTE expected `(((_, //~| NOTE expected tuple //~| NOTE the full name for the type has been written to //~| NOTE consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr b/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr index a99657ca113ff..58974cec57564 100644 --- a/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr +++ b/tests/ui/diagnostic-width/secondary-label-with-long-type.stderr @@ -2,11 +2,11 @@ error[E0308]: mismatched types --> $DIR/secondary-label-with-long-type.rs:9:9 | LL | let () = x; - | ^^ - this expression has type `((..., ..., ..., ...), ..., ..., ...)` + | ^^ - this expression has type `(((_, _, _, _), _, _, _), _, _, _)` | | - | expected `((..., ..., ..., ...), ..., ..., ...)`, found `()` + | expected `(((_, _, _, _), _, _, _), _, _, _)`, found `()` | - = note: expected tuple `((..., ..., ..., ...), ..., ..., ...)` + = note: expected tuple `(((_, _, _, _), _, _, _), _, _, _)` found unit type `()` = note: the full name for the type has been written to '$TEST_BUILD_DIR/secondary-label-with-long-type.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/dropck/dropck_no_diverge_on_nonregular_1.stderr b/tests/ui/dropck/dropck_no_diverge_on_nonregular_1.stderr index 330a40d925bb1..e8e3bfd1dd816 100644 --- a/tests/ui/dropck/dropck_no_diverge_on_nonregular_1.stderr +++ b/tests/ui/dropck/dropck_no_diverge_on_nonregular_1.stderr @@ -4,7 +4,7 @@ error[E0320]: overflow while adding drop-check rules for `FingerTree` LL | let ft = | ^^ | - = note: overflowed on `FingerTree>>>>>>>>>` + = note: overflowed on `FingerTree>>>>>>>>>` = note: the full name for the type has been written to '$TEST_BUILD_DIR/dropck_no_diverge_on_nonregular_1.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/error-codes/E0275.stderr b/tests/ui/error-codes/E0275.stderr index 755404c1e1698..36175f636d6fb 100644 --- a/tests/ui/error-codes/E0275.stderr +++ b/tests/ui/error-codes/E0275.stderr @@ -1,11 +1,11 @@ -error[E0275]: overflow evaluating the requirement `Bar>>>>>>: Foo` +error[E0275]: overflow evaluating the requirement `Bar>>>>>>: Foo` --> $DIR/E0275.rs:6:33 | LL | impl Foo for T where Bar: Foo {} | ^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`E0275`) -note: required for `Bar>>>>>>>>>>>>` to implement `Foo` +note: required for `Bar>>>>>>>>>>>>` to implement `Foo` --> $DIR/E0275.rs:6:9 | LL | impl Foo for T where Bar: Foo {} diff --git a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr index 00a5948bdd472..716278f4036e3 100644 --- a/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr +++ b/tests/ui/higher-ranked/trait-bounds/hang-on-deeply-nested-dyn.stderr @@ -11,7 +11,7 @@ LL | | ), LL | | ) { | |_- expected `&dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn Fn(u32) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a))` because of return type LL | f - | ^ expected `&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&...))))`, found `&dyn Fn(u32)` + | ^ expected `&dyn Fn(&dyn Fn(&dyn Fn(&dyn Fn(&_))))`, found `&dyn Fn(u32)` | = note: expected reference `&dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn for<'a> Fn(&'a (dyn Fn(u32) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a)) + 'a))` found reference `&dyn Fn(u32)` diff --git a/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.stderr b/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.stderr index 5c4a1a7582931..d697829c55c95 100644 --- a/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.stderr +++ b/tests/ui/inference/really-long-type-in-let-binding-without-sufficient-type-info.stderr @@ -1,4 +1,4 @@ -error[E0282]: type annotations needed for `Result<_, (((..., ..., ..., ...), ..., ..., ...), ..., ..., ...)>` +error[E0282]: type annotations needed for `Result<_, ((((i32, i32, i32, i32), _, _, _), _, _, _), _, _, _)>` --> $DIR/really-long-type-in-let-binding-without-sufficient-type-info.rs:8:9 | LL | let y = Err(x); diff --git a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs index b8ea353df9385..1abfd88b6bf41 100644 --- a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs +++ b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.rs @@ -10,7 +10,7 @@ //~| ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` //~| ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` //~| ERROR reached the recursion limit finding the struct tail for `VirtualWrapper, 0>` -//~| ERROR reached the recursion limit while instantiating ` as MyTrait>::virtualize` +//~| ERROR reached the recursion limit while instantiating ` as MyTrait>::virtualize` //@ build-fail //@ compile-flags: --diagnostic-width=100 -Zwrite-long-types-to-disk=yes diff --git a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr index deccc88e64fa5..0167841cce689 100644 --- a/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr +++ b/tests/ui/infinite/infinite-instantiation-struct-tail-ice-114484.stderr @@ -17,7 +17,7 @@ error: reached the recursion limit finding the struct tail for `[u8; 256]` = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -note: the above error was encountered while instantiating `fn virtualize_my_trait::>` +note: the above error was encountered while instantiating `fn virtualize_my_trait::>` --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:38:18 | LL | unsafe { virtualize_my_trait(L, self) } @@ -45,7 +45,7 @@ error: reached the recursion limit finding the struct tail for `SomeData<256>` = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -note: the above error was encountered while instantiating `fn virtualize_my_trait::>` +note: the above error was encountered while instantiating `fn virtualize_my_trait::>` --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:38:18 | LL | unsafe { virtualize_my_trait(L, self) } @@ -73,7 +73,7 @@ error: reached the recursion limit finding the struct tail for `VirtualWrapper>` +note: the above error was encountered while instantiating `fn virtualize_my_trait::>` --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:38:18 | LL | unsafe { virtualize_my_trait(L, self) } @@ -82,7 +82,7 @@ LL | unsafe { virtualize_my_trait(L, self) } = note: the full name for the type has been written to '$TEST_BUILD_DIR/infinite-instantiation-struct-tail-ice-114484.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console -error: reached the recursion limit while instantiating ` as MyTrait>::virtualize` +error: reached the recursion limit while instantiating ` as MyTrait>::virtualize` | note: ` as MyTrait>::virtualize` defined here --> $DIR/infinite-instantiation-struct-tail-ice-114484.rs:37:5 diff --git a/tests/ui/infinite/infinite-instantiation.stderr b/tests/ui/infinite/infinite-instantiation.stderr index 3218584441290..c41d0b245b9e7 100644 --- a/tests/ui/infinite/infinite-instantiation.stderr +++ b/tests/ui/infinite/infinite-instantiation.stderr @@ -1,4 +1,4 @@ -error: reached the recursion limit while instantiating `function::>>>>` +error: reached the recursion limit while instantiating `function::>>>>` --> $DIR/infinite-instantiation.rs:22:9 | LL | function(counter - 1, t.to_option()); diff --git a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr index 835f1c6442a78..ddb4d1645af5e 100644 --- a/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr +++ b/tests/ui/issues/issue-37311-type-length-limit/issue-37311.stderr @@ -1,4 +1,4 @@ -error: reached the recursion limit while instantiating `<(&(&(&..., ...), ...), ...) as Foo>::recurse` +error: reached the recursion limit while instantiating `<(&(&(&(&(&_, _), _), _), _), _) as Foo>::recurse` --> $DIR/issue-37311.rs:17:9 | LL | (self, self).recurse(); diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.current.stderr b/tests/ui/lazy-type-alias/inherent-impls-overflow.current.stderr index dee809ebf7e81..a4d208a8fc24c 100644 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.current.stderr +++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.current.stderr @@ -14,7 +14,7 @@ LL | impl Loop {} | = note: in case this is a recursive type alias, consider using a struct, enum, or union instead -error[E0275]: overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>` +error[E0275]: overflow normalizing the type alias `Poly0<(((((((_,),),),),),),)>` --> $DIR/inherent-impls-overflow.rs:17:1 | LL | type Poly0 = Poly1<(T,)>; @@ -22,7 +22,7 @@ LL | type Poly0 = Poly1<(T,)>; | = note: in case this is a recursive type alias, consider using a struct, enum, or union instead -error[E0275]: overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` +error[E0275]: overflow normalizing the type alias `Poly1<(((((((_,),),),),),),)>` --> $DIR/inherent-impls-overflow.rs:20:1 | LL | type Poly1 = Poly0<(T,)>; @@ -30,7 +30,7 @@ LL | type Poly1 = Poly0<(T,)>; | = note: in case this is a recursive type alias, consider using a struct, enum, or union instead -error[E0275]: overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` +error[E0275]: overflow normalizing the type alias `Poly1<(((((((_,),),),),),),)>` --> $DIR/inherent-impls-overflow.rs:24:1 | LL | impl Poly0<()> {} diff --git a/tests/ui/lazy-type-alias/inherent-impls-overflow.rs b/tests/ui/lazy-type-alias/inherent-impls-overflow.rs index 66a81321624b0..6ee4f6b943a22 100644 --- a/tests/ui/lazy-type-alias/inherent-impls-overflow.rs +++ b/tests/ui/lazy-type-alias/inherent-impls-overflow.rs @@ -15,14 +15,14 @@ impl Loop {} //[next]~| ERROR overflow evaluating the requirement `Loop == _` type Poly0 = Poly1<(T,)>; -//[current]~^ ERROR overflow normalizing the type alias `Poly0<(((((((...,),),),),),),)>` +//[current]~^ ERROR overflow normalizing the type alias `Poly0<(((((((_,),),),),),),)>` //[next]~^^ ERROR overflow evaluating the requirement type Poly1 = Poly0<(T,)>; -//[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` +//[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((_,),),),),),),)>` //[next]~^^ ERROR overflow evaluating the requirement impl Poly0<()> {} -//[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((...,),),),),),),)>` +//[current]~^ ERROR overflow normalizing the type alias `Poly1<(((((((_,),),),),),),)>` //[next]~^^ ERROR overflow evaluating the requirement `Poly0<()> == _` //[next]~| ERROR overflow evaluating the requirement diff --git a/tests/ui/limits/type-length-limit-enforcement.stderr b/tests/ui/limits/type-length-limit-enforcement.stderr index 82855bd755285..0313f212d3f7e 100644 --- a/tests/ui/limits/type-length-limit-enforcement.stderr +++ b/tests/ui/limits/type-length-limit-enforcement.stderr @@ -1,4 +1,4 @@ -error: reached the type-length limit while instantiating `drop::>` +error: reached the type-length limit while instantiating `drop::>` --> $DIR/type-length-limit-enforcement.rs:34:5 | LL | drop::>(None); diff --git a/tests/ui/methods/inherent-bound-in-probe.stderr b/tests/ui/methods/inherent-bound-in-probe.stderr index 6502752bcb455..8564fcaf8eac2 100644 --- a/tests/ui/methods/inherent-bound-in-probe.stderr +++ b/tests/ui/methods/inherent-bound-in-probe.stderr @@ -28,7 +28,7 @@ LL | where LL | &'a T: IntoIterator, | ------------- unsatisfied trait bound introduced here = note: 126 redundant requirements hidden - = note: required for `&BitReaderWrapper>>` to implement `IntoIterator` + = note: required for `&BitReaderWrapper>>` to implement `IntoIterator` note: required by a bound in `Helper` --> $DIR/inherent-bound-in-probe.rs:17:12 | diff --git a/tests/ui/methods/probe-error-on-infinite-deref.stderr b/tests/ui/methods/probe-error-on-infinite-deref.stderr index 6148b00116302..4d2f31499173b 100644 --- a/tests/ui/methods/probe-error-on-infinite-deref.stderr +++ b/tests/ui/methods/probe-error-on-infinite-deref.stderr @@ -1,4 +1,4 @@ -error[E0055]: reached the recursion limit while auto-dereferencing `Wrap>>>>>>>>>>` +error[E0055]: reached the recursion limit while auto-dereferencing `Wrap>>>>>>>>>>` --> $DIR/probe-error-on-infinite-deref.rs:14:13 | LL | Wrap(1).lmao(); diff --git a/tests/ui/mismatched_types/mismatch-sugg-for-shorthand-field.stderr b/tests/ui/mismatched_types/mismatch-sugg-for-shorthand-field.stderr index 225d7503a02a4..899cb3cda8591 100644 --- a/tests/ui/mismatched_types/mismatch-sugg-for-shorthand-field.stderr +++ b/tests/ui/mismatched_types/mismatch-sugg-for-shorthand-field.stderr @@ -55,7 +55,7 @@ LL | let a = async { 42 }; | ----- the found `async` block ... LL | let s = Demo { a }; - | ^ expected `Pin>`, found `async` block + | ^ expected `Pin>`, found `async` block | = note: expected struct `Pin + Send + 'static)>>` found `async` block `{async block@$DIR/mismatch-sugg-for-shorthand-field.rs:53:13: 53:18}` diff --git a/tests/ui/parallel-rustc/dyn-trait-ice-153366.stderr b/tests/ui/parallel-rustc/dyn-trait-ice-153366.stderr index ba21ace81c7eb..a9ca104c00d36 100644 --- a/tests/ui/parallel-rustc/dyn-trait-ice-153366.stderr +++ b/tests/ui/parallel-rustc/dyn-trait-ice-153366.stderr @@ -52,7 +52,7 @@ LL | fn iso(a: Fn) -> Option<_> | --------- expected `Option<_>` because of return type ... LL | Box::new(iso_un_option) - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Option<_>`, found `Box ... {iso_un_option::<_>}>` + | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Option<_>`, found `Box _ {iso_un_option::<_>}>` | = note: expected enum `Option<_>` found struct `Box {type error} {iso_un_option::<_>}>` diff --git a/tests/ui/recursion/infinite-function-recursion-error-8727.stderr b/tests/ui/recursion/infinite-function-recursion-error-8727.stderr index 13d57ecb3b2f0..28eb26595ead5 100644 --- a/tests/ui/recursion/infinite-function-recursion-error-8727.stderr +++ b/tests/ui/recursion/infinite-function-recursion-error-8727.stderr @@ -9,7 +9,7 @@ LL | generic::>(); = help: a `loop` may express intention better if this is on purpose = note: `#[warn(unconditional_recursion)]` on by default -error: reached the recursion limit while instantiating `generic::>>>>` +error: reached the recursion limit while instantiating `generic::>>>>` --> $DIR/infinite-function-recursion-error-8727.rs:9:5 | LL | generic::>(); diff --git a/tests/ui/recursion/issue-23122-2.stderr b/tests/ui/recursion/issue-23122-2.stderr index 39cd0eb35a630..2fda276d03729 100644 --- a/tests/ui/recursion/issue-23122-2.stderr +++ b/tests/ui/recursion/issue-23122-2.stderr @@ -1,11 +1,11 @@ -error[E0275]: overflow evaluating the requirement `<<<<<<<... as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: Sized` +error[E0275]: overflow evaluating the requirement `<<<<<<<_ as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next as Next>::Next: Sized` --> $DIR/issue-23122-2.rs:11:17 | LL | type Next = as Next>::Next; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_23122_2`) -note: required for `GetNext<<<<... as Next>::Next as Next>::Next as Next>::Next>` to implement `Next` +note: required for `GetNext<<<<_ as Next>::Next as Next>::Next as Next>::Next>` to implement `Next` --> $DIR/issue-23122-2.rs:10:15 | LL | impl Next for GetNext { diff --git a/tests/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr b/tests/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr index cf3bc4578a727..8eb49c49ad58e 100644 --- a/tests/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr +++ b/tests/ui/recursion/issue-38591-non-regular-dropck-recursion.stderr @@ -4,7 +4,7 @@ error[E0320]: overflow while adding drop-check rules for `S` LL | fn f(x: S) {} | ^ | - = note: overflowed on `S` + = note: overflowed on `S` = note: the full name for the type has been written to '$TEST_BUILD_DIR/issue-38591-non-regular-dropck-recursion.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/recursion/issue-83150.stderr b/tests/ui/recursion/issue-83150.stderr index a245b001badef..9ec187f055018 100644 --- a/tests/ui/recursion/issue-83150.stderr +++ b/tests/ui/recursion/issue-83150.stderr @@ -15,7 +15,7 @@ error[E0275]: overflow evaluating the requirement `Map<&mut std::ops::Range, = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`issue_83150`) = note: required for `&mut Map<&mut Range, {closure@issue-83150.rs:12:24}>` to implement `Iterator` = note: 65 redundant requirements hidden - = note: required for `&mut Map<&mut Map<&mut Map<&mut Map<&mut ..., ...>, ...>, ...>, ...>` to implement `Iterator` + = note: required for `&mut Map<&mut Map<&mut Map<&mut Map<&mut Map<_, _>, _>, _>, _>, _>` to implement `Iterator` = note: the full name for the type has been written to '$TEST_BUILD_DIR/issue-83150.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/recursion/recursion.stderr b/tests/ui/recursion/recursion.stderr index 974f18ed103d1..987a69767c14b 100644 --- a/tests/ui/recursion/recursion.stderr +++ b/tests/ui/recursion/recursion.stderr @@ -1,4 +1,4 @@ -error: reached the recursion limit while instantiating `test::>>>>>>` +error: reached the recursion limit while instantiating `test::>>>>>>` --> $DIR/recursion.rs:17:11 | LL | _ => {test (n-1, i+1, Cons {head:2*i+1, tail:first}, Cons{head:i*i, tail:second})} diff --git a/tests/ui/recursion/recursive-impl-trait-iterator-by-ref-67552.stderr b/tests/ui/recursion/recursive-impl-trait-iterator-by-ref-67552.stderr index 8d6d44dcbe2fb..6a01b5d8f5b79 100644 --- a/tests/ui/recursion/recursive-impl-trait-iterator-by-ref-67552.stderr +++ b/tests/ui/recursion/recursive-impl-trait-iterator-by-ref-67552.stderr @@ -1,4 +1,4 @@ -error: reached the recursion limit while instantiating `rec::<&mut &mut &mut &mut &mut &mut &mut &mut ...>` +error: reached the recursion limit while instantiating `rec::<&mut &mut &mut &mut &mut &mut &mut &mut _>` --> $DIR/recursive-impl-trait-iterator-by-ref-67552.rs:28:9 | LL | rec(identity(&mut it)) diff --git a/tests/ui/suggestions/box-future-wrong-output.stderr b/tests/ui/suggestions/box-future-wrong-output.stderr index bac26ae8fb5dd..39e0f3c45009b 100644 --- a/tests/ui/suggestions/box-future-wrong-output.stderr +++ b/tests/ui/suggestions/box-future-wrong-output.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/box-future-wrong-output.rs:20:39 | LL | let _: BoxFuture<'static, bool> = async {}.boxed(); - | ------------------------ ^^^^^^^^^^^^^^^^ expected `Pin>`, found `Pin + Send>>` + | ------------------------ ^^^^^^^^^^^^^^^^ expected `Pin>`, found `Pin + Send>>` | | | expected due to this | diff --git a/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr b/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr index 61a1d30d31d4a..a38aa47932b56 100644 --- a/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr +++ b/tests/ui/suggestions/expected-boxed-future-isnt-pinned.stderr @@ -5,7 +5,7 @@ LL | fn foo + Send + 'static>(x: F) -> BoxFuture<'static, | - found this type parameter ----------------------- expected `Pin + Send + 'static)>>` because of return type LL | // We could instead use an `async` block, but this way we have no std spans. LL | x - | ^ expected `Pin>`, found type parameter `F` + | ^ expected `Pin>`, found type parameter `F` | = note: expected struct `Pin + Send + 'static)>>` found type parameter `F` @@ -20,7 +20,7 @@ error[E0308]: mismatched types LL | fn bar + Send + 'static>(x: F) -> BoxFuture<'static, i32> { | ----------------------- expected `Pin + Send + 'static)>>` because of return type LL | Box::new(x) - | ^^^^^^^^^^^ expected `Pin>`, found `Box` + | ^^^^^^^^^^^ expected `Pin>`, found `Box` | = note: expected struct `Pin + Send + 'static)>>` found struct `Box` @@ -76,7 +76,7 @@ LL | fn zap() -> BoxFuture<'static, i32> { LL | / async { LL | | 42 LL | | } - | |_____^ expected `Pin>`, found `async` block + | |_____^ expected `Pin>`, found `async` block | = note: expected struct `Pin + Send + 'static)>>` found `async` block `{async block@$DIR/expected-boxed-future-isnt-pinned.rs:28:5: 28:10}` diff --git a/tests/ui/suggestions/issue-107860.stderr b/tests/ui/suggestions/issue-107860.stderr index 2bfd219398141..ab35d639c92d3 100644 --- a/tests/ui/suggestions/issue-107860.stderr +++ b/tests/ui/suggestions/issue-107860.stderr @@ -2,7 +2,7 @@ error[E0308]: mismatched types --> $DIR/issue-107860.rs:3:36 | LL | async fn str(T: &str) -> &str { &str } - | ^^^^ expected `&str`, found `&fn(&str) -> ... {str::<_>}` + | ^^^^ expected `&str`, found `&fn(&str) -> _ {str::<_>}` | = note: expected reference `&str` found reference `&for<'a> fn(&'a str) -> impl Future {str::<_>}` diff --git a/tests/ui/trait-bounds/associated-error-bound-issue-145586.stderr b/tests/ui/trait-bounds/associated-error-bound-issue-145586.stderr index 18cfece99ffa6..4c3b1ec6c928d 100644 --- a/tests/ui/trait-bounds/associated-error-bound-issue-145586.stderr +++ b/tests/ui/trait-bounds/associated-error-bound-issue-145586.stderr @@ -8,7 +8,7 @@ LL | fn deserialize_ignored_any(self, visitor: V) -> Result>::Value, E>` because of return type ... LL | Ok(deserializer) => deserializer.deserialize_ignored_any(visitor), - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Result<>::Value, E>`, found `Result<>::Value, ...>` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `Result<>::Value, E>`, found `Result<>::Value, _>` | = note: expected enum `Result<_, E>` found enum `Result<_, >::Error>` diff --git a/tests/ui/traits/issue-68295.stderr b/tests/ui/traits/issue-68295.stderr index 8bc315302417b..aeda5e2bea4b0 100644 --- a/tests/ui/traits/issue-68295.stderr +++ b/tests/ui/traits/issue-68295.stderr @@ -5,7 +5,7 @@ LL | fn crash(input: Matrix) -> Matrix | ----------------- expected `Matrix` because of return type ... LL | input.into_owned() - | ^^^^^^^^^^^^^^^^^^ expected `Matrix`, found `Matrix` + | ^^^^^^^^^^^^^^^^^^ expected `Matrix`, found `Matrix` | = note: expected struct `Matrix<_, _, u32>` found struct `Matrix<_, _, <() as Allocator>::Buffer>` diff --git a/tests/ui/traits/issue-91949-hangs-on-recursion.stderr b/tests/ui/traits/issue-91949-hangs-on-recursion.stderr index a179107885ab2..3eff2944b472f 100644 --- a/tests/ui/traits/issue-91949-hangs-on-recursion.stderr +++ b/tests/ui/traits/issue-91949-hangs-on-recursion.stderr @@ -24,7 +24,7 @@ LL | impl> Iterator for IteratorOfWrapped { | | | unsatisfied trait bound introduced here = note: 256 redundant requirements hidden - = note: required for `IteratorOfWrapped<(), Map>, ...>>` to implement `Iterator` + = note: required for `IteratorOfWrapped<(), Map>, _>>` to implement `Iterator` = note: the full name for the type has been written to '$TEST_BUILD_DIR/issue-91949-hangs-on-recursion.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/traits/next-solver/overflow/global-cache.stderr b/tests/ui/traits/next-solver/overflow/global-cache.stderr index 67616619384c6..de743dc00b891 100644 --- a/tests/ui/traits/next-solver/overflow/global-cache.stderr +++ b/tests/ui/traits/next-solver/overflow/global-cache.stderr @@ -1,4 +1,4 @@ -error[E0275]: overflow evaluating the requirement `Inc>>>>>>: Trait` +error[E0275]: overflow evaluating the requirement `Inc>>>>>>: Trait` --> $DIR/global-cache.rs:21:19 | LL | impls_trait::>>>>(); diff --git a/tests/ui/traits/on_unimplemented_long_types.stderr b/tests/ui/traits/on_unimplemented_long_types.stderr index f32d99a42b12b..0bd8591257b26 100644 --- a/tests/ui/traits/on_unimplemented_long_types.stderr +++ b/tests/ui/traits/on_unimplemented_long_types.stderr @@ -1,4 +1,4 @@ -error[E0277]: `Option>>` doesn't implement `std::fmt::Display` +error[E0277]: `Option>>` doesn't implement `std::fmt::Display` --> $DIR/on_unimplemented_long_types.rs:3:17 | LL | pub fn foo() -> impl std::fmt::Display { @@ -11,9 +11,9 @@ LL | | Some(Some(Some(Some(Some(Some(Some... ... | LL | | ))))))))))), LL | | ))))))))))) - | |_______________- return type was inferred to be `Option>>` here + | |_______________- return type was inferred to be `Option>>` here | - = help: the trait `std::fmt::Display` is not implemented for `Option>>` + = help: the trait `std::fmt::Display` is not implemented for `Option>>` = note: the full name for the type has been written to '$TEST_BUILD_DIR/on_unimplemented_long_types.long-type-$LONG_TYPE_HASH.txt' = note: consider using `--verbose` to print the full type name to the console diff --git a/tests/ui/traits/trait-impl-overflow-with-where-clause-20413.stderr b/tests/ui/traits/trait-impl-overflow-with-where-clause-20413.stderr index 72aff1b9ee8b7..b5a7e36518364 100644 --- a/tests/ui/traits/trait-impl-overflow-with-where-clause-20413.stderr +++ b/tests/ui/traits/trait-impl-overflow-with-where-clause-20413.stderr @@ -7,7 +7,7 @@ LL | struct NoData; = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead -error[E0275]: overflow evaluating the requirement `NoData>>>>>>: Foo` +error[E0275]: overflow evaluating the requirement `NoData>>>>>>: Foo` --> $DIR/trait-impl-overflow-with-where-clause-20413.rs:9:36 | LL | impl Foo for T where NoData: Foo { @@ -22,7 +22,7 @@ LL | impl Foo for T where NoData: Foo { = note: 126 redundant requirements hidden = note: required for `NoData` to implement `Foo` -error[E0275]: overflow evaluating the requirement `AlmostNoData>>>>>>: Bar` +error[E0275]: overflow evaluating the requirement `AlmostNoData>>>>>>: Bar` --> $DIR/trait-impl-overflow-with-where-clause-20413.rs:28:42 | LL | impl Bar for T where EvenLessData: Baz { @@ -42,7 +42,7 @@ LL | impl Bar for T where EvenLessData: Baz { = note: 125 redundant requirements hidden = note: required for `EvenLessData` to implement `Baz` -error[E0275]: overflow evaluating the requirement `EvenLessData>>>>>>: Baz` +error[E0275]: overflow evaluating the requirement `EvenLessData>>>>>>: Baz` --> $DIR/trait-impl-overflow-with-where-clause-20413.rs:35:42 | LL | impl Baz for T where AlmostNoData: Bar { diff --git a/tests/ui/trimmed-paths/doc-hidden.rs b/tests/ui/trimmed-paths/doc-hidden.rs index c3125385c7e41..9aca8f0cd8f9e 100644 --- a/tests/ui/trimmed-paths/doc-hidden.rs +++ b/tests/ui/trimmed-paths/doc-hidden.rs @@ -44,7 +44,7 @@ fn uses_local() { DocHiddenInHiddenMod, ) = 3u32; //~^ ERROR mismatched types [E0308] - //~| NOTE expected `(ActuallyPub, ..., ..., ..., ..., ...)`, found `u32` + //~| NOTE expected `(ActuallyPub, DocHidden, _, _, _, _)`, found `u32` //~| NOTE expected tuple `(local::ActuallyPub, DocHidden, local::pub_mod::ActuallyPubInPubMod, DocHiddenInPubMod, ActuallyPubInHiddenMod, DocHiddenInHiddenMod)` } @@ -63,6 +63,6 @@ fn uses_helper() { DocHiddenInHiddenMod, ) = 3u32; //~^ ERROR mismatched types [E0308] - //~| NOTE expected `(ActuallyPub, ..., ..., ..., ..., ...)`, found `u32` + //~| NOTE expected `(ActuallyPub, DocHidden, _, _, _, _)`, found `u32` //~| NOTE expected tuple `(doc_hidden_helper::ActuallyPub, doc_hidden_helper::DocHidden, doc_hidden_helper::pub_mod::ActuallyPubInPubMod, doc_hidden_helper::pub_mod::DocHiddenInPubMod, doc_hidden_helper::hidden_mod::ActuallyPubInHiddenMod, doc_hidden_helper::hidden_mod::DocHiddenInHiddenMod)` } diff --git a/tests/ui/trimmed-paths/doc-hidden.stderr b/tests/ui/trimmed-paths/doc-hidden.stderr index 167c92c50a357..3cf8d99422704 100644 --- a/tests/ui/trimmed-paths/doc-hidden.stderr +++ b/tests/ui/trimmed-paths/doc-hidden.stderr @@ -9,7 +9,7 @@ LL | | DocHidden, ... | LL | | DocHiddenInHiddenMod, LL | | ) = 3u32; - | | - ^^^^ expected `(ActuallyPub, ..., ..., ..., ..., ...)`, found `u32` + | | - ^^^^ expected `(ActuallyPub, DocHidden, _, _, _, _)`, found `u32` | |_____| | expected due to this | @@ -27,7 +27,7 @@ LL | | DocHidden, ... | LL | | DocHiddenInHiddenMod, LL | | ) = 3u32; - | | - ^^^^ expected `(ActuallyPub, ..., ..., ..., ..., ...)`, found `u32` + | | - ^^^^ expected `(ActuallyPub, DocHidden, _, _, _, _)`, found `u32` | |_____| | expected due to this | diff --git a/tests/ui/typeck/issue-107775.stderr b/tests/ui/typeck/issue-107775.stderr index 1be2689746941..b2878a779de93 100644 --- a/tests/ui/typeck/issue-107775.stderr +++ b/tests/ui/typeck/issue-107775.stderr @@ -6,7 +6,7 @@ LL | map.insert(1, Struct::do_something); | | | ... which causes `map` to have type `HashMap<{integer}, fn(u8) -> Pin + Send>> {::do_something::<'_>}>` LL | Self { map } - | ^^^ expected `HashMap Pin>>`, found `HashMap<{integer}, ...>` + | ^^^ expected `HashMap Pin>>`, found `HashMap<{integer}, _>` | = note: expected struct `HashMap Pin + Send + 'static)>>>` found struct `HashMap<{integer}, fn(_) -> Pin + Send>> {::do_something::<'_>}>` From 8e12609f40af76b8620d21079f2dca1eed60f9cc Mon Sep 17 00:00:00 2001 From: Joel-Wwalker Date: Mon, 13 Jul 2026 22:50:54 -0400 Subject: [PATCH 21/71] Add regression test for issue 144033 --- .../relate-bound-region-ice-144033.rs | 33 +++++++ .../relate-bound-region-ice-144033.stderr | 98 +++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 tests/ui/higher-ranked/relate-bound-region-ice-144033.rs create mode 100644 tests/ui/higher-ranked/relate-bound-region-ice-144033.stderr diff --git a/tests/ui/higher-ranked/relate-bound-region-ice-144033.rs b/tests/ui/higher-ranked/relate-bound-region-ice-144033.rs new file mode 100644 index 0000000000000..7e0a684bb2ade --- /dev/null +++ b/tests/ui/higher-ranked/relate-bound-region-ice-144033.rs @@ -0,0 +1,33 @@ +// Regression test for . +// A delegating impl whose method drops the trait's HRTB where-clause used to +// ICE with "cannot relate bound region" instead of emitting normal errors. + +trait FooMut { + type Baz: 'static; + fn bar<'a, I>(self, iterator: &'a I) + where + for<'b> &'b I: IntoIterator; +} +struct DelegatingFooMut {} +//~^ ERROR type parameter `T` is never used + +impl FooMut for DelegatingFooMut +where + T: FooMut, +{ + type Baz = DelegatingBaz; + fn bar<'a, I>(self, collection: &'a I) + //~^ ERROR lifetime parameters do not match the trait definition + where + for<'b> &'b I: IntoIterator, + { + let collection = collection.into_iter().map(|b| &b); + self.bar(collection) + //~^ ERROR type mismatch resolving + //~| ERROR mismatched types + } +} +struct DelegatingBaz; +//~^ ERROR type parameter `T` is never used + +fn main() {} diff --git a/tests/ui/higher-ranked/relate-bound-region-ice-144033.stderr b/tests/ui/higher-ranked/relate-bound-region-ice-144033.stderr new file mode 100644 index 0000000000000..4518587661292 --- /dev/null +++ b/tests/ui/higher-ranked/relate-bound-region-ice-144033.stderr @@ -0,0 +1,98 @@ +error[E0392]: type parameter `T` is never used + --> $DIR/relate-bound-region-ice-144033.rs:11:25 + | +LL | struct DelegatingFooMut {} + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error[E0195]: lifetime parameters do not match the trait definition + --> $DIR/relate-bound-region-ice-144033.rs:19:12 + | +LL | fn bar<'a, I>(self, collection: &'a I) + | ^^ + | + = note: lifetime parameters differ in whether they are early- or late-bound +note: `'a` differs between the trait and impl + --> $DIR/relate-bound-region-ice-144033.rs:7:12 + | +LL | trait FooMut { + | ------------ in this trait... +LL | type Baz: 'static; +LL | fn bar<'a, I>(self, iterator: &'a I) + | ^^ `'a` is early-bound +LL | where +LL | for<'b> &'b I: IntoIterator; + | ------------------------ this lifetime bound makes `'a` early-bound +... +LL | / impl FooMut for DelegatingFooMut +LL | | where +LL | | T: FooMut, + | |______________- in this impl... +... +LL | fn bar<'a, I>(self, collection: &'a I) + | ^^ `'a` is late-bound + +error[E0392]: type parameter `T` is never used + --> $DIR/relate-bound-region-ice-144033.rs:30:22 + | +LL | struct DelegatingBaz; + | ^ unused type parameter + | + = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` + = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead + +error[E0271]: type mismatch resolving `<&I as IntoIterator>::Item == &&DelegatingBaz<::Baz>` + --> $DIR/relate-bound-region-ice-144033.rs:25:18 + | +LL | self.bar(collection) + | --- ^^^^^^^^^^ expected `&&DelegatingBaz<::Baz>`, found associated type + | | + | required by a bound introduced by this call + | + = note: expected reference `&&DelegatingBaz<::Baz>` + found associated type `<&I as IntoIterator>::Item` + = help: consider constraining the associated type `<&I as IntoIterator>::Item` to `&&DelegatingBaz<::Baz>` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html +note: the method call chain might not have had the expected associated types + --> $DIR/relate-bound-region-ice-144033.rs:19:37 + | +LL | fn bar<'a, I>(self, collection: &'a I) + | ^^^^^ `IntoIterator::Item` is `<&I as IntoIterator>::Item` here +... +LL | let collection = collection.into_iter().map(|b| &b); + | ----------- ----------- `IntoIterator::Item` changed to `&<&I as IntoIterator>::Item` here + | | + | `IntoIterator::Item` remains `<&I as IntoIterator>::Item` here +note: required by a bound in `FooMut::bar` + --> $DIR/relate-bound-region-ice-144033.rs:9:37 + | +LL | fn bar<'a, I>(self, iterator: &'a I) + | --- required by a bound in this associated function +LL | where +LL | for<'b> &'b I: IntoIterator; + | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `FooMut::bar` + +error[E0308]: mismatched types + --> $DIR/relate-bound-region-ice-144033.rs:25:18 + | +LL | let collection = collection.into_iter().map(|b| &b); + | --- the found closure +LL | self.bar(collection) + | --- ^^^^^^^^^^ expected `&I`, found `Map<<&I as IntoIterator>::IntoIter, ...>` + | | + | arguments to this method are incorrect + | + = note: expected reference `&I` + found struct `Map<<&I as IntoIterator>::IntoIter, {closure@$DIR/relate-bound-region-ice-144033.rs:24:53: 24:56}>` +note: method defined here + --> $DIR/relate-bound-region-ice-144033.rs:7:8 + | +LL | fn bar<'a, I>(self, iterator: &'a I) + | ^^^ -------- + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0195, E0271, E0308, E0392. +For more information about an error, try `rustc --explain E0195`. From bf5220139f5af0dc7a9f9c8ec8d26a07ec7771d0 Mon Sep 17 00:00:00 2001 From: xonx <119700621+xonx4l@users.noreply.github.com> Date: Tue, 14 Jul 2026 05:11:50 +0000 Subject: [PATCH 22/71] Skip intrinsic-test when rustfmt is unavailable --- src/bootstrap/src/core/build_steps/test.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 6b232cc039492..9ebe3191db40f 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -1085,10 +1085,12 @@ impl Step for IntrinsicTest { cmd.env("CFLAGS", cflags); // intrinsic-test shells out to `cargo` and `rustfmt` make bootstrap's // managed binaries findable by prepending their dirs to PATH. - let rustfmt_path = builder.config.initial_rustfmt.clone().unwrap_or_else(|| { - eprintln!("intrinsic-test: rustfmt is required but not available on this channel"); - crate::exit!(1); - }); + let Some(rustfmt_path) = builder.config.initial_rustfmt.clone() else { + eprintln!( + "WARNING: intrinsic-test skipped because rustfmt is required but not available on this channel" + ); + return; + }; let mut path_dirs: Vec = Vec::new(); if let Some(cargo_dir) = builder.initial_cargo.parent() { From f3f42356b443fb03529073df77574eff4ac3198c Mon Sep 17 00:00:00 2001 From: Joel-Wwalker Date: Tue, 14 Jul 2026 01:19:24 -0400 Subject: [PATCH 23/71] Simplify regression test for issue 144033 --- .../relate-bound-region-ice-144033.rs | 28 ++--- .../relate-bound-region-ice-144033.stderr | 105 +++++------------- 2 files changed, 39 insertions(+), 94 deletions(-) diff --git a/tests/ui/higher-ranked/relate-bound-region-ice-144033.rs b/tests/ui/higher-ranked/relate-bound-region-ice-144033.rs index 7e0a684bb2ade..9c3c1a507a458 100644 --- a/tests/ui/higher-ranked/relate-bound-region-ice-144033.rs +++ b/tests/ui/higher-ranked/relate-bound-region-ice-144033.rs @@ -1,33 +1,23 @@ // Regression test for . -// A delegating impl whose method drops the trait's HRTB where-clause used to -// ICE with "cannot relate bound region" instead of emitting normal errors. +// This used to ICE with "cannot relate bound region" instead of emitting +// normal errors. trait FooMut { - type Baz: 'static; - fn bar<'a, I>(self, iterator: &'a I) + fn bar(self, _: I) where - for<'b> &'b I: IntoIterator; + for<'b> &'b I: Iterator; } -struct DelegatingFooMut {} -//~^ ERROR type parameter `T` is never used -impl FooMut for DelegatingFooMut -where - T: FooMut, -{ - type Baz = DelegatingBaz; - fn bar<'a, I>(self, collection: &'a I) - //~^ ERROR lifetime parameters do not match the trait definition +impl FooMut for () { + fn bar(self, _: I) where - for<'b> &'b I: IntoIterator, + for<'b> &'b I: Iterator, { - let collection = collection.into_iter().map(|b| &b); + let collection = std::iter::empty::<()>().map(|_| &()); self.bar(collection) - //~^ ERROR type mismatch resolving + //~^ ERROR expected `&I` to be an iterator that yields `&()` //~| ERROR mismatched types } } -struct DelegatingBaz; -//~^ ERROR type parameter `T` is never used fn main() {} diff --git a/tests/ui/higher-ranked/relate-bound-region-ice-144033.stderr b/tests/ui/higher-ranked/relate-bound-region-ice-144033.stderr index 4518587661292..e9ec3e9dc0707 100644 --- a/tests/ui/higher-ranked/relate-bound-region-ice-144033.stderr +++ b/tests/ui/higher-ranked/relate-bound-region-ice-144033.stderr @@ -1,98 +1,53 @@ -error[E0392]: type parameter `T` is never used - --> $DIR/relate-bound-region-ice-144033.rs:11:25 - | -LL | struct DelegatingFooMut {} - | ^ unused type parameter - | - = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` - = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead - -error[E0195]: lifetime parameters do not match the trait definition - --> $DIR/relate-bound-region-ice-144033.rs:19:12 - | -LL | fn bar<'a, I>(self, collection: &'a I) - | ^^ - | - = note: lifetime parameters differ in whether they are early- or late-bound -note: `'a` differs between the trait and impl - --> $DIR/relate-bound-region-ice-144033.rs:7:12 - | -LL | trait FooMut { - | ------------ in this trait... -LL | type Baz: 'static; -LL | fn bar<'a, I>(self, iterator: &'a I) - | ^^ `'a` is early-bound -LL | where -LL | for<'b> &'b I: IntoIterator; - | ------------------------ this lifetime bound makes `'a` early-bound -... -LL | / impl FooMut for DelegatingFooMut -LL | | where -LL | | T: FooMut, - | |______________- in this impl... -... -LL | fn bar<'a, I>(self, collection: &'a I) - | ^^ `'a` is late-bound - -error[E0392]: type parameter `T` is never used - --> $DIR/relate-bound-region-ice-144033.rs:30:22 - | -LL | struct DelegatingBaz; - | ^ unused type parameter - | - = help: consider removing `T`, referring to it in a field, or using a marker such as `PhantomData` - = help: if you intended `T` to be a const parameter, use `const T: /* Type */` instead - -error[E0271]: type mismatch resolving `<&I as IntoIterator>::Item == &&DelegatingBaz<::Baz>` - --> $DIR/relate-bound-region-ice-144033.rs:25:18 +error[E0271]: expected `&I` to be an iterator that yields `&()`, but it yields `<&I as Iterator>::Item` + --> $DIR/relate-bound-region-ice-144033.rs:17:18 | LL | self.bar(collection) - | --- ^^^^^^^^^^ expected `&&DelegatingBaz<::Baz>`, found associated type + | --- ^^^^^^^^^^ expected `&()`, found associated type | | | required by a bound introduced by this call | - = note: expected reference `&&DelegatingBaz<::Baz>` - found associated type `<&I as IntoIterator>::Item` - = help: consider constraining the associated type `<&I as IntoIterator>::Item` to `&&DelegatingBaz<::Baz>` + = note: expected reference `&()` + found associated type `<&I as Iterator>::Item` + = help: consider constraining the associated type `<&I as Iterator>::Item` to `&()` = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html note: the method call chain might not have had the expected associated types - --> $DIR/relate-bound-region-ice-144033.rs:19:37 + --> $DIR/relate-bound-region-ice-144033.rs:16:51 | -LL | fn bar<'a, I>(self, collection: &'a I) - | ^^^^^ `IntoIterator::Item` is `<&I as IntoIterator>::Item` here -... -LL | let collection = collection.into_iter().map(|b| &b); - | ----------- ----------- `IntoIterator::Item` changed to `&<&I as IntoIterator>::Item` here - | | - | `IntoIterator::Item` remains `<&I as IntoIterator>::Item` here +LL | let collection = std::iter::empty::<()>().map(|_| &()); + | ------------------------ ^^^^^^^^^^^^ `Iterator::Item` is `&()` here + | | + | this expression has type `Empty<()>` note: required by a bound in `FooMut::bar` - --> $DIR/relate-bound-region-ice-144033.rs:9:37 + --> $DIR/relate-bound-region-ice-144033.rs:8:33 | -LL | fn bar<'a, I>(self, iterator: &'a I) +LL | fn bar(self, _: I) | --- required by a bound in this associated function LL | where -LL | for<'b> &'b I: IntoIterator; - | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `FooMut::bar` +LL | for<'b> &'b I: Iterator; + | ^^^^^^^^^^^^^ required by this bound in `FooMut::bar` error[E0308]: mismatched types - --> $DIR/relate-bound-region-ice-144033.rs:25:18 + --> $DIR/relate-bound-region-ice-144033.rs:17:18 | -LL | let collection = collection.into_iter().map(|b| &b); - | --- the found closure +LL | fn bar(self, _: I) + | - expected this type parameter +... +LL | let collection = std::iter::empty::<()>().map(|_| &()); + | --- the found closure LL | self.bar(collection) - | --- ^^^^^^^^^^ expected `&I`, found `Map<<&I as IntoIterator>::IntoIter, ...>` + | --- ^^^^^^^^^^ expected type parameter `I`, found `Map, {closure@...}>` | | | arguments to this method are incorrect | - = note: expected reference `&I` - found struct `Map<<&I as IntoIterator>::IntoIter, {closure@$DIR/relate-bound-region-ice-144033.rs:24:53: 24:56}>` + = note: expected type parameter `I` + found struct `Map, {closure@$DIR/relate-bound-region-ice-144033.rs:16:55: 16:58}>` note: method defined here - --> $DIR/relate-bound-region-ice-144033.rs:7:8 + --> $DIR/relate-bound-region-ice-144033.rs:6:8 | -LL | fn bar<'a, I>(self, iterator: &'a I) - | ^^^ -------- +LL | fn bar(self, _: I) + | ^^^ - -error: aborting due to 5 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0195, E0271, E0308, E0392. -For more information about an error, try `rustc --explain E0195`. +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`. From ca150644d7a28f7f37d6d85db020e3236a9568ce Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 12 Jun 2026 11:19:25 +0200 Subject: [PATCH 24/71] Add regression test --- tests/ui/comptime/comptime_method.rs | 16 ++++++++++++++++ tests/ui/comptime/comptime_method.stderr | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/ui/comptime/comptime_method.rs create mode 100644 tests/ui/comptime/comptime_method.stderr diff --git a/tests/ui/comptime/comptime_method.rs b/tests/ui/comptime/comptime_method.rs new file mode 100644 index 0000000000000..a0618f97e0ea8 --- /dev/null +++ b/tests/ui/comptime/comptime_method.rs @@ -0,0 +1,16 @@ +#![feature(rustc_attrs)] + +struct Bar; + +#[rustc_comptime] +//~^ ERROR: cannot be used on inherent impl +impl Bar { + fn boo(&self) {} +} + +const _: () = { + Bar.boo(); + //~^ ERROR: cannot call non-const method `Bar::boo` in constants +}; + +fn main() {} diff --git a/tests/ui/comptime/comptime_method.stderr b/tests/ui/comptime/comptime_method.stderr new file mode 100644 index 0000000000000..682e0c364e43c --- /dev/null +++ b/tests/ui/comptime/comptime_method.stderr @@ -0,0 +1,19 @@ +error: `#[rustc_comptime]` attribute cannot be used on inherent impl blocks + --> $DIR/comptime_method.rs:5:1 + | +LL | #[rustc_comptime] + | ^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_comptime]` can only be applied to functions + +error[E0015]: cannot call non-const method `Bar::boo` in constants + --> $DIR/comptime_method.rs:12:9 + | +LL | Bar.boo(); + | ^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0015`. From 5b3e95d537e399b65a2fcb89e821b69e7efb6ccb Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 12 Jun 2026 11:19:25 +0200 Subject: [PATCH 25/71] Add regression test --- tests/ui/comptime/comptime_impl.rs | 37 ++++++++++++++++++++ tests/ui/comptime/comptime_impl.stderr | 47 ++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 tests/ui/comptime/comptime_impl.rs create mode 100644 tests/ui/comptime/comptime_impl.stderr diff --git a/tests/ui/comptime/comptime_impl.rs b/tests/ui/comptime/comptime_impl.rs new file mode 100644 index 0000000000000..84b08ded12b5c --- /dev/null +++ b/tests/ui/comptime/comptime_impl.rs @@ -0,0 +1,37 @@ +#![feature(rustc_attrs, const_trait_impl)] + +const trait Foo { + fn foo(&self); + + fn bar(&self) {} +} + +struct Bar; + +#[rustc_comptime] +//~^ ERROR: cannot be used on inherent impl +impl Bar { + fn boo(&self) {} +} + +#[rustc_comptime] +//~^ ERROR: cannot be used on trait impl +impl Foo for Bar { + fn foo(&self) { + comptime_fn(); + //~^ ERROR: comptime fns can only be called at compile time + } +} + +#[rustc_comptime] +fn comptime_fn() {} + +const _: () = { + Bar.boo(); + Bar.foo(); + //~^ ERROR: `Bar: const Foo` is not satisfied + Bar.bar(); + //~^ ERROR: `Bar: const Foo` is not satisfied +}; + +fn main() {} diff --git a/tests/ui/comptime/comptime_impl.stderr b/tests/ui/comptime/comptime_impl.stderr new file mode 100644 index 0000000000000..75ad46d60cbea --- /dev/null +++ b/tests/ui/comptime/comptime_impl.stderr @@ -0,0 +1,47 @@ +error: `#[rustc_comptime]` attribute cannot be used on inherent impl blocks + --> $DIR/comptime_impl.rs:11:1 + | +LL | #[rustc_comptime] + | ^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_comptime]` can only be applied to functions + +error: `#[rustc_comptime]` attribute cannot be used on trait impl blocks + --> $DIR/comptime_impl.rs:17:1 + | +LL | #[rustc_comptime] + | ^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_comptime]` can only be applied to functions + +error: comptime fns can only be called at compile time + --> $DIR/comptime_impl.rs:21:9 + | +LL | comptime_fn(); + | ^^^^^^^^^^^^^ + +error[E0277]: the trait bound `Bar: const Foo` is not satisfied + --> $DIR/comptime_impl.rs:31:9 + | +LL | Bar.foo(); + | ^^^ + | +help: make the `impl` of trait `Foo` `const` + | +LL | impl const Foo for Bar { + | +++++ + +error[E0277]: the trait bound `Bar: const Foo` is not satisfied + --> $DIR/comptime_impl.rs:33:9 + | +LL | Bar.bar(); + | ^^^ + | +help: make the `impl` of trait `Foo` `const` + | +LL | impl const Foo for Bar { + | +++++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`. From 7139a2b071210fd8e55f0cd174043391a0df227e Mon Sep 17 00:00:00 2001 From: "Eddy (Eduard) Stefes" Date: Tue, 14 Jul 2026 08:42:45 +0200 Subject: [PATCH 26/71] disable range-len-try-from.rs on s390x This optimisation is currently broken on s390x[^1]. It looks like a big-endian issue in general. but as we only know about the test fails on s390x we only disable it there. Should be reenabled when the problem is fixed and llvm backend is updated. [^1]: https://github.com/llvm/llvm-project/issues/208712 --- tests/codegen-llvm/range-len-try-from.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/codegen-llvm/range-len-try-from.rs b/tests/codegen-llvm/range-len-try-from.rs index e8f204b45e81b..9e23fd67bcd9c 100644 --- a/tests/codegen-llvm/range-len-try-from.rs +++ b/tests/codegen-llvm/range-len-try-from.rs @@ -4,6 +4,7 @@ //@ compile-flags: -Copt-level=3 //@ only-64bit +//@ ignore-s390x OPT missing on s390x https://github.com/llvm/llvm-project/issues/208712 #![crate_type = "lib"] From edbbeae31855d7354b30d969e94cfc3d5e5604f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Mon, 13 Jul 2026 23:10:52 +0200 Subject: [PATCH 27/71] Update tidy licenses --- src/tools/tidy/src/deps.rs | 10 ++++++++-- src/tools/tidy/src/extdeps.rs | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 60b321641ad76..aeaea8ca9bb5b 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -242,11 +242,17 @@ const EXCEPTIONS_RUST_ANALYZER: ExceptionList = &[ const EXCEPTIONS_RUSTC_PERF: ExceptionList = &[ // tidy-alphabetical-start + ("aws-lc-rs", "ISC AND (Apache-2.0 OR ISC)"), + ( + "aws-lc-sys", + "ISC AND (Apache-2.0 OR ISC) AND Apache-2.0 AND MIT AND BSD-3-Clause AND (Apache-2.0 OR ISC OR MIT) AND (Apache-2.0 OR ISC OR MIT-0)", + ), + ("brotli", "BSD-3-Clause AND MIT"), + ("fast-srgb8", "MIT OR Apache-2.0 OR CC0-1.0"), ("inferno", "CDDL-1.0"), ("option-ext", "MPL-2.0"), - ("terminfo", "WTFPL"), ("wasite", "Apache-2.0 OR BSL-1.0 OR MIT"), - ("wezterm-bidi", "MIT AND Unicode-DFS-2016"), + ("webpki-root-certs", "CDLA-Permissive-2.0"), ("whoami", "Apache-2.0 OR BSL-1.0 OR MIT"), // tidy-alphabetical-end ]; diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs index 7999386a3c298..79b562270f7ce 100644 --- a/src/tools/tidy/src/extdeps.rs +++ b/src/tools/tidy/src/extdeps.rs @@ -10,7 +10,7 @@ use crate::diagnostics::TidyCtx; const ALLOWED_SOURCES: &[&str] = &[ r#""registry+https://github.com/rust-lang/crates.io-index""#, // This is `rust_team_data` used by `site` in src/tools/rustc-perf, - r#""git+https://github.com/rust-lang/team#a5260e76d3aa894c64c56e6ddc8545b9a98043ec""#, + r#""git+https://github.com/rust-lang/team#db2c1ed9fbc0216e533db954cd249045c01c7406""#, ]; /// Checks for external package sources. `root` is the path to the directory that contains the From 7d5c9252d83446d0dbcf4ba18ac7a58f167be021 Mon Sep 17 00:00:00 2001 From: Rachit2323 Date: Tue, 14 Jul 2026 12:37:26 +0530 Subject: [PATCH 28/71] slice: make swap delegate to swap_unchecked --- library/core/src/slice/mod.rs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 25889994be977..14c207a2a0a98 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -906,16 +906,12 @@ impl [T] { #[inline] #[track_caller] pub const fn swap(&mut self, a: usize, b: usize) { - // FIXME: use swap_unchecked here (https://github.com/rust-lang/rust/pull/88540#issuecomment-944344343) - // Can't take two mutable loans from one vector, so instead use raw pointers. - let pa = &raw mut self[a]; - let pb = &raw mut self[b]; - // SAFETY: `pa` and `pb` have been created from safe mutable references and refer - // to elements in the slice and therefore are guaranteed to be valid and aligned. - // Note that accessing the elements behind `a` and `b` is checked and will - // panic when out of bounds. + // Bounds checks that panic exactly like indexing would. + let _ = &self[a]; + let _ = &self[b]; + // SAFETY: `a` and `b` were checked to be in bounds above. unsafe { - ptr::swap(pa, pb); + self.swap_unchecked(a, b); } } From 661ae59afd1dad11620bc02830a32e521879ea3f Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 09:45:53 +0200 Subject: [PATCH 29/71] avoid duplicated Link reference definition --- src/doc/rustc-dev-guide/src/diagnostics.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/diagnostics.md b/src/doc/rustc-dev-guide/src/diagnostics.md index 7e3da67d872c1..9a5539df8adba 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics.md +++ b/src/doc/rustc-dev-guide/src/diagnostics.md @@ -350,7 +350,6 @@ before emitting it by calling the [`emit`][emit] method. [spanerr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html#method.span_err [strspanerr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html#method.struct_span_err -[diag]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html [emit]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html#method.emit [cancel]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html#method.cancel @@ -390,7 +389,6 @@ To this end, suggestions pleasingly in the terminal, or (when the `--error-format json` flag is passed) as JSON for consumption by tools like [`rustfix`][rustfix]. -[diag]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html [rustfix]: https://github.com/rust-lang/rustfix Not all suggestions should be applied mechanically, they have a degree of @@ -1049,3 +1047,5 @@ Will format the message into ```text "Self = `i8`, T = `i32`, this = `From`, trait = `From`, context = `a function`" ``` + +[diag]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html From 313c4f21b1305c2b6afa352dbc965d66c8c25184 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 09:48:14 +0200 Subject: [PATCH 30/71] lighter markup --- src/doc/rustc-dev-guide/src/diagnostics.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/diagnostics.md b/src/doc/rustc-dev-guide/src/diagnostics.md index 9a5539df8adba..ef8cc9528dd3b 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics.md +++ b/src/doc/rustc-dev-guide/src/diagnostics.md @@ -339,18 +339,18 @@ For example, Most of these methods will accept strings, but it is recommended that typed identifiers for translatable diagnostics be used for new diagnostics (see -[Translation][translation]). +[Translation]). [translation]: ./diagnostics/translation.md `Diag` allows you to add related notes and suggestions to an error -before emitting it by calling the [`emit`][emit] method. +before emitting it by calling the [`emit`] method. (Failing to either emit or [cancel] a `Diag` will result in an ICE.) See the [docs][diag] for more info on what you can do. [spanerr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html#method.span_err [strspanerr]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.DiagCtxt.html#method.struct_span_err -[emit]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html#method.emit +[`emit`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html#method.emit [cancel]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_errors/struct.Diag.html#method.cancel ```rust,ignore From e866d9dabda6c5600c6ceb5f7ee699796c5cee8e Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 09:50:22 +0200 Subject: [PATCH 31/71] reflow --- src/doc/rustc-dev-guide/src/diagnostics.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/diagnostics.md b/src/doc/rustc-dev-guide/src/diagnostics.md index ef8cc9528dd3b..2717cfd7867fc 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics.md +++ b/src/doc/rustc-dev-guide/src/diagnostics.md @@ -391,9 +391,9 @@ is passed) as JSON for consumption by tools like [`rustfix`][rustfix]. [rustfix]: https://github.com/rust-lang/rustfix -Not all suggestions should be applied mechanically, they have a degree of -confidence in the suggested code, from high -(`Applicability::MachineApplicable`) to low (`Applicability::MaybeIncorrect`). +Not all suggestions should be applied mechanically, +they have a degree of confidence in the suggested code, +from high (`Applicability::MachineApplicable`) to low (`Applicability::MaybeIncorrect`). Be conservative when choosing the level. Use the [`span_suggestion`][span_suggestion] method of `Diag` to make a suggestion. From b182157a3418801de680c70096faf2880cb6e37a Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 09:50:34 +0200 Subject: [PATCH 32/71] fix punctuation --- src/doc/rustc-dev-guide/src/diagnostics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/diagnostics.md b/src/doc/rustc-dev-guide/src/diagnostics.md index 2717cfd7867fc..651f86329c9e3 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics.md +++ b/src/doc/rustc-dev-guide/src/diagnostics.md @@ -391,7 +391,7 @@ is passed) as JSON for consumption by tools like [`rustfix`][rustfix]. [rustfix]: https://github.com/rust-lang/rustfix -Not all suggestions should be applied mechanically, +Not all suggestions should be applied mechanically; they have a degree of confidence in the suggested code, from high (`Applicability::MachineApplicable`) to low (`Applicability::MaybeIncorrect`). Be conservative when choosing the level. From dff3fdedc9f0e737400156a6568b957e5a326239 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 14 Jul 2026 17:48:36 +1000 Subject: [PATCH 33/71] Rename `std_crates_for_run_make` to `std_crates_for_make_run` This function's name is referring to `Step::make_run`, and has nothing to do with run-make tests. --- src/bootstrap/src/core/build_steps/check.rs | 4 ++-- src/bootstrap/src/core/build_steps/clippy.rs | 7 ++++--- src/bootstrap/src/core/build_steps/compile.rs | 6 +++--- src/bootstrap/src/core/build_steps/doc.rs | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs index 9d958f7d10078..002b46cb7fbec 100644 --- a/src/bootstrap/src/core/build_steps/check.rs +++ b/src/bootstrap/src/core/build_steps/check.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; use crate::core::build_steps::compile::{ ArtifactKeepMode, add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, - std_crates_for_run_make, + std_crates_for_make_run, }; use crate::core::build_steps::tool; use crate::core::build_steps::tool::{ @@ -68,7 +68,7 @@ impl Step for Std { // Explicitly pass -p for all dependencies crates -- this will force cargo // to also check the tests/benches/examples for these crates, rather // than just the leaf crate. - let crates = std_crates_for_run_make(&run); + let crates = std_crates_for_make_run(&run); run.builder.ensure(Std { build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Std) .build_compiler(), diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs index f03ae75c4bbf2..09ccd14ab88c3 100644 --- a/src/bootstrap/src/core/build_steps/clippy.rs +++ b/src/bootstrap/src/core/build_steps/clippy.rs @@ -13,11 +13,12 @@ //! to pass a prebuilt Clippy from the outside when running `cargo clippy`, but that would be //! (as usual) a massive undertaking/refactoring. -use super::compile::{ArtifactKeepMode, run_cargo, rustc_cargo, std_cargo}; use super::tool::{SourceType, prepare_tool_cargo}; use crate::builder::{Builder, ShouldRun}; use crate::core::build_steps::check::{CompilerForCheck, prepare_compiler_for_check}; -use crate::core::build_steps::compile::std_crates_for_run_make; +use crate::core::build_steps::compile::{ + ArtifactKeepMode, run_cargo, rustc_cargo, std_cargo, std_crates_for_make_run, +}; use crate::core::builder; use crate::core::builder::{Alias, Kind, RunConfig, Step, StepMetadata, crate_description}; use crate::utils::build_stamp::{self, BuildStamp}; @@ -178,7 +179,7 @@ impl Step for Std { } fn make_run(run: RunConfig<'_>) { - let crates = std_crates_for_run_make(&run); + let crates = std_crates_for_make_run(&run); let config = LintConfig::new(run.builder); run.builder.ensure(Std::new(run.builder, run.target, config, crates)); } diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs index 54f93d8f72bf8..1248438e93605 100644 --- a/src/bootstrap/src/core/build_steps/compile.rs +++ b/src/bootstrap/src/core/build_steps/compile.rs @@ -122,7 +122,7 @@ impl Step for Std { } fn make_run(run: RunConfig<'_>) { - let crates = std_crates_for_run_make(&run); + let crates = std_crates_for_make_run(&run); let builder = run.builder; // Force compilation of the standard library from source if the `library` is modified. This allows @@ -479,9 +479,9 @@ fn copy_self_contained_objects( target_deps } -/// Resolves standard library crates for `Std::run_make` for any build kind (like check, doc, +/// Resolves standard library crates for [`Std::make_run`] for any build kind (like check, doc, /// build, clippy, etc.). -pub fn std_crates_for_run_make(run: &RunConfig<'_>) -> Vec { +pub fn std_crates_for_make_run(run: &RunConfig<'_>) -> Vec { let mut crates = run.make_run_crates(builder::Alias::Library); // For no_std targets, we only want to check core and alloc diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs index 0f051232757e8..847282ee62719 100644 --- a/src/bootstrap/src/core/build_steps/doc.rs +++ b/src/bootstrap/src/core/build_steps/doc.rs @@ -667,7 +667,7 @@ impl Step for Std { } fn make_run(run: RunConfig<'_>) { - let crates = compile::std_crates_for_run_make(&run); + let crates = compile::std_crates_for_make_run(&run); let target_is_no_std = run.builder.no_std(run.target).unwrap_or(false); if crates.is_empty() && target_is_no_std { return; From 070dd6849a30a3749f84d4933ee457912d22f903 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 10:15:59 +0200 Subject: [PATCH 34/71] remove obsolete comment This was a reminder to make the lint translatable, but the code to make lints translatable has since been removed --- compiler/rustc_lint/src/lints.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 783829b356369..494a6f2903a07 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -601,8 +601,6 @@ pub(crate) struct BuiltinDerefNullptr { pub label: Span, } -// FIXME: migrate fluent::lint::builtin_asm_labels - #[derive(Diagnostic)] pub(crate) enum BuiltinSpecialModuleNameUsed { #[diag("found module declaration for lib.rs")] From d97dfeed70c853ad16a86c083d674e61523818ba Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 10:23:31 +0200 Subject: [PATCH 35/71] sembr src/ambig-unambig-ty-and-consts.md --- .../src/ambig-unambig-ty-and-consts.md | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) 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 6cb74fbef560f..145e542734fa9 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 @@ -1,6 +1,7 @@ # Ambig/Unambig Types and Consts -Types and Consts args in the AST/HIR can be in two kinds of positions ambiguous (ambig) or unambiguous (unambig). Ambig positions are where +Types and Consts args in the AST/HIR can be in two kinds of positions ambiguous (ambig) or unambiguous (unambig). +Ambig positions are where it would be valid to parse either a type or a const, unambig positions are where only one kind would be valid to parse. @@ -21,7 +22,8 @@ fn func(arg: T) { ``` -Most types/consts in ambig positions are able to be disambiguated as either a type or const during parsing. The only exceptions to this are paths and inferred generic arguments. +Most types/consts in ambig positions are able to be disambiguated as either a type or const during parsing. +The only exceptions to this are paths and inferred generic arguments. ## Paths @@ -31,7 +33,8 @@ struct Foo; fn foo(_: Foo) {} ``` -At parse time we parse all unbraced generic arguments as *types* (ie they wind up as [`ast::GenericArg::Ty`]). In the above example this means we would parse the generic argument to `Foo` as an `ast::GenericArg::Ty` wrapping a [`ast::Ty::Path(N)`]. +At parse time we parse all unbraced generic arguments as *types* (ie they wind up as [`ast::GenericArg::Ty`]). +In the above example this means we would parse the generic argument to `Foo` as an `ast::GenericArg::Ty` wrapping a [`ast::Ty::Path(N)`]. Then during name resolution: - When encountering a single segment path with no generic arguments in generic argument position, we will first try to resolve it in the type namespace and if that fails we then attempt to resolve in the value namespace. @@ -39,7 +42,8 @@ Then during name resolution: See [`LateResolutionVisitor::visit_generic_arg`] for where this is implemented. -Finally during AST lowering when we attempt to lower a type argument, we first check if it is a `Ty::Path` and if it resolved to something in the value namespace. If it did then we create an *anon const* and lower to a const argument instead of a type argument. +Finally during AST lowering when we attempt to lower a type argument, we first check if it is a `Ty::Path` and if it resolved to something in the value namespace. +If it did then we create an *anon const* and lower to a const argument instead of a type argument. See [`LoweringContext::lower_generic_arg`] for where this is implemented. @@ -55,16 +59,20 @@ 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. +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 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 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. +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. It may be worth seeing if we can refactor the AST to have `ast::GenericArg::Infer` and then get rid of this overloaded meaning of `AnonConst`, as well as the reuse of `ast::Ty::Infer` in ambig positions. In unambig AST positions, during AST lowering we lower inferred arguments to [`hir::TyKind::Infer`][ty_infer] or [`hir::ConstArgKind::Infer`][const_infer] depending on whether it is a type or const position respectively. -In ambig AST positions, during AST lowering we lower inferred arguments to [`hir::GenericArg::Infer`][generic_arg_infer]. See [`LoweringContext::lower_generic_arg`] for where this is implemented. +In ambig AST positions, during AST lowering we lower inferred arguments to [`hir::GenericArg::Infer`][generic_arg_infer]. +See [`LoweringContext::lower_generic_arg`] for where this is implemented. A naive implementation of this would result in there being potentially 5 places where you might think an inferred type/const could be found in the HIR from looking at the structure of the HIR: 1. In unambig type position as a `TyKind::Infer` @@ -73,7 +81,7 @@ A naive implementation of this would result in there being potentially 5 places 4. In an ambig position as a [`GenericArg::Const(ConstArgKind::Infer)`][generic_arg_const] 5. In an ambig position as a `GenericArg::Infer` -Note that places 3 and 4 would never actually be possible to encounter as we always lower to `GenericArg::Infer` in generic arg position. +Note that places 3 and 4 would never actually be possible to encounter as we always lower to `GenericArg::Infer` in generic arg position. This has a few failure modes: - People may write visitors which check for `GenericArg::Infer` but forget to check for `hir::TyKind/ConstArgKind::Infer`, only handling infers in ambig positions by accident. @@ -83,9 +91,11 @@ This has a few failure modes: To make writing HIR visitors less error prone when caring about inferred types/consts we have a relatively complex system: -1. We have different types in the compiler for when a type or const is in an unambig or ambig position, `Ty` and `Ty<()>`. [`AmbigArg`][ambig_arg] is an uninhabited type which we use in the `Infer` variant of `TyKind` and `ConstArgKind` to selectively "disable" it if we are in an ambig position. +1. We have different types in the compiler for when a type or const is in an unambig or ambig position, `Ty` and `Ty<()>`. + [`AmbigArg`][ambig_arg] is an uninhabited type which we use in the `Infer` variant of `TyKind` and `ConstArgKind` to selectively "disable" it if we are in an ambig position. -2. The [`visit_ty`][visit_ty] and [`visit_const_arg`][visit_const_arg] methods on HIR visitors only accept the ambig position versions of types/consts. Unambig types/consts are implicitly converted to ambig types/consts during the visiting process, with the `Infer` variant handled by a dedicated [`visit_infer`][visit_infer] method. +2. The [`visit_ty`][visit_ty] and [`visit_const_arg`][visit_const_arg] methods on HIR visitors only accept the ambig position versions of types/consts. + Unambig types/consts are implicitly converted to ambig types/consts during the visiting process, with the `Infer` variant handled by a dedicated [`visit_infer`][visit_infer] method. This has a number of benefits: - It's clear that `GenericArg::Type/Const` cannot represent inferred type/const arguments From 95362d8a6d8d692f4c7dd8aeedd2b548d3fd7472 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 10:30:18 +0200 Subject: [PATCH 36/71] improve ambig-unambig-ty-and-consts.md --- .../src/ambig-unambig-ty-and-consts.md | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) 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 145e542734fa9..43daf6e15ef8d 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 @@ -1,19 +1,18 @@ # Ambig/Unambig Types and Consts Types and Consts args in the AST/HIR can be in two kinds of positions ambiguous (ambig) or unambiguous (unambig). -Ambig positions are where -it would be valid to parse either a type or a const, unambig positions are where only one kind would be valid to -parse. +Ambig positions are where it would be valid to parse either a type or a const. +Unambig positions are where only one kind would be valid to parse. ```rust fn func(arg: T) { // ^ Unambig type position - let a: _ = arg; + let a: _ = arg; // ^ Unambig type position func::(arg); // ^ ^ - // ^^^^ Ambig position + // ^^^^ Ambig position let _: [u8; 10]; // ^^ ^^ Unambig const position @@ -43,7 +42,7 @@ Then during name resolution: See [`LateResolutionVisitor::visit_generic_arg`] for where this is implemented. Finally during AST lowering when we attempt to lower a type argument, we first check if it is a `Ty::Path` and if it resolved to something in the value namespace. -If it did then we create an *anon const* and lower to a const argument instead of a type argument. +If it did, then we create an *anon const* and lower to a const argument instead of a type argument. See [`LoweringContext::lower_generic_arg`] for where this is implemented. @@ -60,15 +59,21 @@ 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 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 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`]. +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. +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. -It may be worth seeing if we can refactor the AST to have `ast::GenericArg::Infer` and then get rid of this overloaded meaning of `AnonConst`, as well as the reuse of `ast::Ty::Infer` in ambig positions. +It may be worth seeing if we can refactor the AST to have `ast::GenericArg::Infer` and then get rid of this overloaded meaning of `AnonConst`, +as well as the reuse of `ast::Ty::Infer` in ambig positions. In unambig AST positions, during AST lowering we lower inferred arguments to [`hir::TyKind::Infer`][ty_infer] or [`hir::ConstArgKind::Infer`][const_infer] depending on whether it is a type or const position respectively. In ambig AST positions, during AST lowering we lower inferred arguments to [`hir::GenericArg::Infer`][generic_arg_infer]. @@ -99,7 +104,7 @@ To make writing HIR visitors less error prone when caring about inferred types/c This has a number of benefits: - It's clear that `GenericArg::Type/Const` cannot represent inferred type/const arguments -- Implementors of `visit_ty` and `visit_const_arg` will never encounter inferred types/consts making it impossible to write a visitor that seems to work right but handles edge cases wrong +- Implementors of `visit_ty` and `visit_const_arg` will never encounter inferred types/consts making it impossible to write a visitor that seems to work right but handles edge cases wrong - The `visit_infer` method handles *all* cases of inferred type/consts in the HIR making it easy for visitors to handle inferred type/consts in one dedicated place and not forget cases [ty_infer]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.TyKind.html#variant.Infer From 887fb7eab8fcb2163a34cdf1dbc86d7826a1aae9 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 10:31:59 +0200 Subject: [PATCH 37/71] sembr src/mir/optimizations.md --- .../rustc-dev-guide/src/mir/optimizations.md | 80 ++++++++++--------- 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/optimizations.md b/src/doc/rustc-dev-guide/src/mir/optimizations.md index 964fa063a680f..50f5ab25d51ce 100644 --- a/src/doc/rustc-dev-guide/src/mir/optimizations.md +++ b/src/doc/rustc-dev-guide/src/mir/optimizations.md @@ -1,24 +1,26 @@ # MIR optimizations -MIR optimizations are optimizations run on the [MIR][mir] to produce better MIR -before codegen. This is important for two reasons: first, it makes the final +MIR optimizations are optimizations run on the [MIR][mir] to produce better MIR before codegen. +This is important for two reasons: first, it makes the final generated executable code better, and second, it means that LLVM has less work -to do, so compilation is faster. Note that since MIR is generic (not +to do, so compilation is faster. +Note that since MIR is generic (not [monomorphized][monomorph] yet), these optimizations are particularly -effective; we can optimize the generic version, so all of the monomorphizations -are cheaper! +effective; we can optimize the generic version, so all of the monomorphizations are cheaper! [mir]: ../mir/index.md [monomorph]: ../appendix/glossary.md#mono -MIR optimizations run after borrow checking. We run a series of optimization -passes over the MIR to improve it. Some passes are required to run on all code, +MIR optimizations run after borrow checking. +We run a series of optimization passes over the MIR to improve it. +Some passes are required to run on all code, some passes don't actually do optimizations but only check stuff, and some passes are only turned on in `release` mode. The [`optimized_mir`][optmir] [query] is called to produce the optimized MIR -for a given [`DefId`][defid]. This query makes sure that the borrow checker has -run and that some validation has occurred. Then, it [steals][steal] the MIR, +for a given [`DefId`][defid]. +This query makes sure that the borrow checker has run and that some validation has occurred. +Then, it [steals][steal] the MIR, optimizes it, and returns the improved MIR. [optmir]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/fn.optimized_mir.html @@ -28,17 +30,18 @@ optimizes it, and returns the improved MIR. ## Quickstart for adding a new optimization -1. Make a Rust source file in `tests/mir-opt` that shows the code you want to - optimize. This should be kept simple, so avoid `println!` or other formatting - code if it's not necessary for the optimization. The reason for this is that +1. Make a Rust source file in `tests/mir-opt` that shows the code you want to optimize. + This should be kept simple, so avoid `println!` or other formatting + code if it's not necessary for the optimization. + The reason for this is that `println!`, `format!`, etc. generate a lot of MIR that can make it harder to understand what the optimization does to the test. -2. Run `./x test --bless tests/mir-opt/.rs` to generate a MIR - dump. Read [this README][mir-opt-test-readme] for instructions on how to dump - things. +2. Run `./x test --bless tests/mir-opt/.rs` to generate a MIR dump. + Read [this README][mir-opt-test-readme] for instructions on how to dump things. -3. Commit the current working directory state. The reason you should commit the +3. Commit the current working directory state. + The reason you should commit the test output before you implement the optimization is so that you (and your reviewers) can see a before/after diff of what the optimization changed. @@ -47,28 +50,26 @@ optimizes it, and returns the improved MIR. 1. pick a small optimization (such as [`remove_storage_markers`]) and copy it to a new file, - 2. add your optimization to one of the lists in the - [`run_optimization_passes()`] function, + 2. add your optimization to one of the lists in the [`run_optimization_passes()`] function, 3. and then start modifying the copied optimization. -5. Rerun `./x test --bless tests/mir-opt/.rs` to regenerate the - MIR dumps. Look at the diffs to see if they are what you expect. +5. Rerun `./x test --bless tests/mir-opt/.rs` to regenerate the MIR dumps. + Look at the diffs to see if they are what you expect. 6. Run `./x test tests/ui` to see if your optimization broke anything. -7. If there are issues with your optimization, experiment with it a bit and - repeat steps 5 and 6. +7. If there are issues with your optimization, experiment with it a bit and repeat steps 5 and 6. -8. Commit and open a PR. You can do this at any point, even if things aren't - working yet, so that you can ask for feedback on the PR. Open a "WIP" PR - (just prefix your PR title with `[WIP]` or otherwise note that it is a +8. Commit and open a PR. + You can do this at any point, even if things aren't + working yet, so that you can ask for feedback on the PR. + Open a "WIP" PR (just prefix your PR title with `[WIP]` or otherwise note that it is a work in progress) in that case. - Make sure to commit the blessed test output as well! It's necessary for CI to - pass and it's very helpful to reviewers. + Make sure to commit the blessed test output as well! + It's necessary for CI to pass and it's very helpful to reviewers. -If you have any questions along the way, feel free to ask in -`#t-compiler/wg-mir-opt` on Zulip. +If you have any questions along the way, feel free to ask in `#t-compiler/wg-mir-opt` on Zulip. [mir-opt-test-readme]: https://github.com/rust-lang/rust/blob/HEAD/tests/mir-opt/README.md [`compiler/rustc_mir_transform/src`]: https://github.com/rust-lang/rust/tree/HEAD/compiler/rustc_mir_transform/src @@ -78,10 +79,11 @@ If you have any questions along the way, feel free to ask in ## Defining optimization passes The list of passes run and the order in which they are run is defined by the -[`run_optimization_passes`][rop] function. It contains an array of passes to -run. Each pass in the array is a struct that implements the [`MirPass`] trait. -The array is an array of `&dyn MirPass` trait objects. Typically, a pass is -implemented in its own module of the [`rustc_mir_transform`][trans] crate. +[`run_optimization_passes`][rop] function. +It contains an array of passes to run. + Each pass in the array is a struct that implements the [`MirPass`] trait. +The array is an array of `&dyn MirPass` trait objects. +Typically, a pass is implemented in its own module of the [`rustc_mir_transform`][trans] crate. [rop]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/fn.run_optimization_passes.html [`MirPass`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/pass_manager/trait.MirPass.html @@ -99,12 +101,14 @@ You can see the ["Implementors" section of the `MirPass` rustdocs][impl] for mor ## MIR optimization levels -MIR optimizations can come in various levels of readiness. Experimental -optimizations may cause miscompilations, or slow down compile times. +MIR optimizations can come in various levels of readiness. +Experimental optimizations may cause miscompilations, or slow down compile times. These passes are still included in nightly builds to gather feedback and make it easier to modify -the pass. To enable working with slow or otherwise experimental optimization passes, -you can specify the `-Z mir-opt-level` debug flag. You can find the -definitions of the levels in the [compiler MCP]. If you are developing a MIR pass and +the pass. +To enable working with slow or otherwise experimental optimization passes, +you can specify the `-Z mir-opt-level` debug flag. +You can find the definitions of the levels in the [compiler MCP]. +If you are developing a MIR pass and want to query whether your optimization pass should run, you can check the current level using [`tcx.sess.opts.unstable_opts.mir_opt_level`][mir_opt_level]. From 36c1fcd13ab5c28b4a64b1503e541be3fec93a11 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 10:47:55 +0200 Subject: [PATCH 38/71] lighter markup --- .../rustc-dev-guide/src/mir/optimizations.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/optimizations.md b/src/doc/rustc-dev-guide/src/mir/optimizations.md index 50f5ab25d51ce..01a8e7ed0dd69 100644 --- a/src/doc/rustc-dev-guide/src/mir/optimizations.md +++ b/src/doc/rustc-dev-guide/src/mir/optimizations.md @@ -1,15 +1,15 @@ # MIR optimizations -MIR optimizations are optimizations run on the [MIR][mir] to produce better MIR before codegen. +MIR optimizations are optimizations run on the [MIR] to produce better MIR before codegen. This is important for two reasons: first, it makes the final generated executable code better, and second, it means that LLVM has less work to do, so compilation is faster. Note that since MIR is generic (not -[monomorphized][monomorph] yet), these optimizations are particularly +[monomorphized] yet), these optimizations are particularly effective; we can optimize the generic version, so all of the monomorphizations are cheaper! [mir]: ../mir/index.md -[monomorph]: ../appendix/glossary.md#mono +[monomorphized]: ../appendix/glossary.md#mono MIR optimizations run after borrow checking. We run a series of optimization passes over the MIR to improve it. @@ -17,16 +17,16 @@ Some passes are required to run on all code, some passes don't actually do optimizations but only check stuff, and some passes are only turned on in `release` mode. -The [`optimized_mir`][optmir] [query] is called to produce the optimized MIR -for a given [`DefId`][defid]. +The [`optimized_mir`] [query] is called to produce the optimized MIR +for a given [`DefId`]. This query makes sure that the borrow checker has run and that some validation has occurred. -Then, it [steals][steal] the MIR, +Then, it [steals] the MIR, optimizes it, and returns the improved MIR. -[optmir]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/fn.optimized_mir.html +[`optimized_mir`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir_transform/fn.optimized_mir.html [query]: ../query.md -[defid]: ../appendix/glossary.md#def-id -[steal]: ../mir/passes.md#stealing +[`defid`]: ../appendix/glossary.md#def-id +[steals]: ../mir/passes.md#stealing ## Quickstart for adding a new optimization From c985b998bbcfde42ba8b5b29d1613c8b9f9f71cc Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 10:51:51 +0200 Subject: [PATCH 39/71] whitespace --- src/doc/rustc-dev-guide/src/mir/optimizations.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/mir/optimizations.md b/src/doc/rustc-dev-guide/src/mir/optimizations.md index 01a8e7ed0dd69..237177ba56c0d 100644 --- a/src/doc/rustc-dev-guide/src/mir/optimizations.md +++ b/src/doc/rustc-dev-guide/src/mir/optimizations.md @@ -81,7 +81,7 @@ If you have any questions along the way, feel free to ask in `#t-compiler/wg-mir The list of passes run and the order in which they are run is defined by the [`run_optimization_passes`][rop] function. It contains an array of passes to run. - Each pass in the array is a struct that implements the [`MirPass`] trait. +Each pass in the array is a struct that implements the [`MirPass`] trait. The array is an array of `&dyn MirPass` trait objects. Typically, a pass is implemented in its own module of the [`rustc_mir_transform`][trans] crate. From 75f10b7606b3ec1122a15155539b6276f9bf04b1 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 10:52:16 +0200 Subject: [PATCH 40/71] sembr src/mir/debugging.md --- src/doc/rustc-dev-guide/src/mir/debugging.md | 29 ++++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/debugging.md b/src/doc/rustc-dev-guide/src/mir/debugging.md index eec2737a423c5..7e1d5b4fa9da3 100644 --- a/src/doc/rustc-dev-guide/src/mir/debugging.md +++ b/src/doc/rustc-dev-guide/src/mir/debugging.md @@ -4,24 +4,24 @@ The `-Z dump-mir` flag can be used to dump a text representation of the MIR. The following optional flags, used in combination with `-Z dump-mir`, enable additional output formats, including: -* `-Z dump-mir-graphviz` - dumps a `.dot` file that represents MIR as a -control-flow graph +* `-Z dump-mir-graphviz` - dumps a `.dot` file that represents MIR as a control-flow graph * `-Z dump-mir-dataflow` - dumps a `.dot` file showing the [dataflow state] at each point in the control-flow graph `-Z dump-mir=F` is a handy compiler option that will let you view the MIR for -each function at each stage of compilation. `-Z dump-mir` takes a **filter** `F` -which allows you to control which functions and which passes you are -interested in. For example: +each function at each stage of compilation. +`-Z dump-mir` takes a **filter** `F` +which allows you to control which functions and which passes you are interested in. +For example: ```bash > rustc -Z dump-mir=foo ... ``` This will dump the MIR for any function whose name contains `foo`; it -will dump the MIR both before and after every pass. Those files will -be created in the `mir_dump` directory. There will likely be quite a -lot of them! +will dump the MIR both before and after every pass. +Those files will be created in the `mir_dump` directory. +There will likely be quite a lot of them! ```bash > cat > foo.rs @@ -34,8 +34,8 @@ fn main() { 161 ``` -The files have names like `rustc.main.000-000.CleanEndRegions.after.mir`. These -names have a number of parts: +The files have names like `rustc.main.000-000.CleanEndRegions.after.mir`. +These names have a number of parts: ```text rustc.main.000-000.CleanEndRegions.after.mir @@ -46,9 +46,9 @@ rustc.main.000-000.CleanEndRegions.after.mir def-path to the function etc being dumped ``` -You can also make more selective filters. For example, `main & CleanEndRegions` -will select for things that reference *both* `main` and the pass -`CleanEndRegions`: +You can also make more selective filters. +For example, `main & CleanEndRegions` +will select for things that reference *both* `main` and the pass `CleanEndRegions`: ```bash > rustc -Z dump-mir='main & CleanEndRegions' foo.rs @@ -58,8 +58,7 @@ rustc.main.000-000.CleanEndRegions.after.mir rustc.main.000-000.CleanEndRegions. Filters can also have `|` parts to combine multiple sets of `&`-filters. For example `main & CleanEndRegions | main & -NoLandingPads` will select *either* `main` and `CleanEndRegions` *or* -`main` and `NoLandingPads`: +NoLandingPads` will select *either* `main` and `CleanEndRegions` *or* `main` and `NoLandingPads`: ```bash > rustc -Z dump-mir='main & CleanEndRegions | main & NoLandingPads' foo.rs From 6741f56b0277b37f468a68fa89f61f2f5c3adf7e Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Fri, 12 Jun 2026 11:35:19 +0200 Subject: [PATCH 41/71] =?UTF-8?q?Add=20=C2=B4#[rustc=5Fcomptime]`=20inhere?= =?UTF-8?q?nt=20impls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rustc_ast_lowering/src/expr/closure.rs | 6 ++- compiler/rustc_ast_lowering/src/item.rs | 45 ++++++++++--------- .../src/attributes/semantics.rs | 1 + .../src/traits/effects.rs | 3 +- tests/ui/comptime/comptime_closure.rs | 13 ++++++ tests/ui/comptime/comptime_closure.stderr | 20 +++++++++ tests/ui/comptime/comptime_impl.rs | 2 - tests/ui/comptime/comptime_impl.stderr | 24 +++------- tests/ui/comptime/comptime_method.rs | 7 +-- tests/ui/comptime/comptime_method.stderr | 19 ++------ tests/ui/comptime/comptime_method_bounds.rs | 24 ++++++++++ tests/ui/comptime/comptime_trait.rs | 32 +++++++++++++ tests/ui/comptime/comptime_trait.stderr | 35 +++++++++++++++ tests/ui/comptime/trait_comptime.stderr | 6 +-- 14 files changed, 172 insertions(+), 65 deletions(-) create mode 100644 tests/ui/comptime/comptime_closure.rs create mode 100644 tests/ui/comptime/comptime_closure.stderr create mode 100644 tests/ui/comptime/comptime_method_bounds.rs create mode 100644 tests/ui/comptime/comptime_trait.rs create mode 100644 tests/ui/comptime/comptime_trait.stderr diff --git a/compiler/rustc_ast_lowering/src/expr/closure.rs b/compiler/rustc_ast_lowering/src/expr/closure.rs index cc2c6ae2879c1..32e2e5d40d132 100644 --- a/compiler/rustc_ast_lowering/src/expr/closure.rs +++ b/compiler/rustc_ast_lowering/src/expr/closure.rs @@ -38,6 +38,7 @@ impl<'hir> LoweringContext<'_, 'hir> { &closure.body, closure.fn_decl_span, closure.fn_arg_span, + attrs, ), span: self.lower_span(e.span), }, @@ -240,7 +241,7 @@ impl<'hir> LoweringContext<'_, 'hir> { fn_decl_span: self.lower_span(fn_decl_span), fn_arg_span: Some(self.lower_span(fn_arg_span)), kind: closure_kind, - constness: self.lower_constness(constness), + constness: self.lower_constness(attrs, constness), explicit_captures, }); @@ -308,6 +309,7 @@ impl<'hir> LoweringContext<'_, 'hir> { body: &Expr, fn_decl_span: Span, fn_arg_span: Span, + attrs: &[hir::Attribute], ) -> hir::ExprKind<'hir> { let closure_def_id = self.local_def_id(closure_id); let (binder_clause, generic_params) = self.lower_closure_binder(binder); @@ -369,7 +371,7 @@ impl<'hir> LoweringContext<'_, 'hir> { // knows that a `FnDecl` output type like `-> &str` actually means // "coroutine that returns &str", rather than directly returning a `&str`. kind: hir::ClosureKind::CoroutineClosure(coroutine_desugaring), - constness: self.lower_constness(constness), + constness: self.lower_constness(attrs, constness), explicit_captures: &[], }); hir::ExprKind::Closure(c) diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 692c178a6875c..8d5bd34f13444 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -479,7 +479,7 @@ impl<'hir> LoweringContext<'_, 'hir> { .arena .alloc_from_iter(impl_items.iter().map(|item| self.lower_impl_item_ref(item))); - let constness = self.lower_constness(*constness); + let constness = self.lower_constness(attrs, *constness); hir::ItemKind::Impl(hir::Impl { generics, @@ -499,7 +499,7 @@ impl<'hir> LoweringContext<'_, 'hir> { bounds, items, }) => { - let constness = self.lower_constness(*constness); + let constness = self.lower_constness(attrs, *constness); let impl_restriction = self.lower_impl_restriction(impl_restriction); let ident = self.lower_ident(*ident); let (generics, (safety, items, bounds)) = self.lower_generics( @@ -530,7 +530,7 @@ impl<'hir> LoweringContext<'_, 'hir> { } } ItemKind::TraitAlias(TraitAlias { constness, ident, generics, bounds }) => { - let constness = self.lower_constness(*constness); + let constness = self.lower_constness(attrs, *constness); let ident = self.lower_ident(*ident); let (generics, bounds) = self.lower_generics( generics, @@ -1702,21 +1702,7 @@ impl<'hir> LoweringContext<'_, 'hir> { safety.into() }; - let mut constness = self.lower_constness(h.constness); - if let Some(&attr_span) = find_attr!(attrs, RustcComptime(span) => span) { - match std::mem::replace(&mut constness, rustc_hir::Constness::Const { always: true }) { - rustc_hir::Constness::Const { always: true } => { - unreachable!("lower_constness cannot produce comptime") - } - // A function can't be `const` and `comptime` at the same time - rustc_hir::Constness::Const { always: false } => { - let Const::Yes(span) = h.constness else { unreachable!() }; - self.dcx().emit_err(ConstComptimeFn { span, attr_span }); - } - // Good - rustc_hir::Constness::NotConst => {} - } - } + let constness = self.lower_constness(attrs, h.constness); hir::FnHeader { safety, asyncness, constness, abi: self.lower_extern(h.ext) } } @@ -1778,11 +1764,30 @@ impl<'hir> LoweringContext<'_, 'hir> { }); } - pub(super) fn lower_constness(&mut self, c: Const) -> hir::Constness { - match c { + /// Lowers constness or comptime attribute. + /// Whether `const` is allowed here is checked by ast validation. + /// Whether `comptime` is allowed here is checked by the `comptime` attribute parser. + pub(super) fn lower_constness(&mut self, attrs: &[hir::Attribute], c: Const) -> hir::Constness { + let mut constness = match c { Const::Yes(_) => hir::Constness::Const { always: false }, Const::No => hir::Constness::NotConst, + }; + + if let Some(&attr_span) = find_attr!(attrs, RustcComptime(span) => span) { + match std::mem::replace(&mut constness, hir::Constness::Const { always: true }) { + hir::Constness::Const { always: true } => { + unreachable!("lower_constness cannot produce comptime") + } + // A function can't be `const` and `comptime` at the same time + hir::Constness::Const { always: false } => { + let Const::Yes(span) = c else { unreachable!() }; + self.dcx().emit_err(ConstComptimeFn { span, attr_span }); + } + // Good + hir::Constness::NotConst => {} + } } + constness } pub(super) fn lower_safety(&self, s: Safety, default: hir::Safety) -> hir::Safety { diff --git a/compiler/rustc_attr_parsing/src/attributes/semantics.rs b/compiler/rustc_attr_parsing/src/attributes/semantics.rs index cdcb8e95da726..fa19d51c397eb 100644 --- a/compiler/rustc_attr_parsing/src/attributes/semantics.rs +++ b/compiler/rustc_attr_parsing/src/attributes/semantics.rs @@ -23,6 +23,7 @@ impl NoArgsAttributeParser for ComptimeParser { const ALLOWED_TARGETS: AllowedTargets<'_> = AllowedTargets::AllowList(&[ Allow(Target::Method(MethodKind::Inherent)), Allow(Target::Fn), + Allow(Target::Impl { of_trait: false }), ]); const STABILITY: AttributeStability = unstable!(rustc_attrs); const CREATE: fn(Span) -> AttributeKind = AttributeKind::RustcComptime; diff --git a/compiler/rustc_trait_selection/src/traits/effects.rs b/compiler/rustc_trait_selection/src/traits/effects.rs index fb44b3b9a9988..1d5c657cb6723 100644 --- a/compiler/rustc_trait_selection/src/traits/effects.rs +++ b/compiler/rustc_trait_selection/src/traits/effects.rs @@ -601,7 +601,8 @@ fn evaluate_host_effect_from_selection_candidate<'tcx>( match tcx.impl_trait_header(impl_.impl_def_id).constness { rustc_hir::Constness::Const { always } => { if always { - unimplemented!() + // FIXME(comptime): just bailing for now to avoid an ICE in a test. + return Err(EvaluationFailure::NoSolution); } } rustc_hir::Constness::NotConst => { diff --git a/tests/ui/comptime/comptime_closure.rs b/tests/ui/comptime/comptime_closure.rs new file mode 100644 index 0000000000000..bc3d58104cbc6 --- /dev/null +++ b/tests/ui/comptime/comptime_closure.rs @@ -0,0 +1,13 @@ +#![feature(rustc_attrs, stmt_expr_attributes)] + +const _: () = { + let f = #[rustc_comptime] + //~^ ERROR: `#[rustc_comptime]` attribute cannot be used on closures + || (); + + // FIXME(comptime): closures should work, too. + f(); + //~^ ERROR: cannot call non-const closure in constants +}; + +fn main() {} diff --git a/tests/ui/comptime/comptime_closure.stderr b/tests/ui/comptime/comptime_closure.stderr new file mode 100644 index 0000000000000..602f88e64b7ad --- /dev/null +++ b/tests/ui/comptime/comptime_closure.stderr @@ -0,0 +1,20 @@ +error: `#[rustc_comptime]` attribute cannot be used on closures + --> $DIR/comptime_closure.rs:4:13 + | +LL | let f = #[rustc_comptime] + | ^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_comptime]` can be applied to functions, inherent impl blocks, and inherent methods + +error[E0015]: cannot call non-const closure in constants + --> $DIR/comptime_closure.rs:9:5 + | +LL | f(); + | ^^^ + | + = note: closures need an RFC before allowed to be called in constants + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/comptime/comptime_impl.rs b/tests/ui/comptime/comptime_impl.rs index 84b08ded12b5c..6e8768aaed109 100644 --- a/tests/ui/comptime/comptime_impl.rs +++ b/tests/ui/comptime/comptime_impl.rs @@ -9,7 +9,6 @@ const trait Foo { struct Bar; #[rustc_comptime] -//~^ ERROR: cannot be used on inherent impl impl Bar { fn boo(&self) {} } @@ -19,7 +18,6 @@ impl Bar { impl Foo for Bar { fn foo(&self) { comptime_fn(); - //~^ ERROR: comptime fns can only be called at compile time } } diff --git a/tests/ui/comptime/comptime_impl.stderr b/tests/ui/comptime/comptime_impl.stderr index 75ad46d60cbea..25f5e878b57f6 100644 --- a/tests/ui/comptime/comptime_impl.stderr +++ b/tests/ui/comptime/comptime_impl.stderr @@ -1,27 +1,13 @@ -error: `#[rustc_comptime]` attribute cannot be used on inherent impl blocks - --> $DIR/comptime_impl.rs:11:1 - | -LL | #[rustc_comptime] - | ^^^^^^^^^^^^^^^^^ - | - = help: `#[rustc_comptime]` can only be applied to functions - error: `#[rustc_comptime]` attribute cannot be used on trait impl blocks - --> $DIR/comptime_impl.rs:17:1 + --> $DIR/comptime_impl.rs:16:1 | LL | #[rustc_comptime] | ^^^^^^^^^^^^^^^^^ | - = help: `#[rustc_comptime]` can only be applied to functions - -error: comptime fns can only be called at compile time - --> $DIR/comptime_impl.rs:21:9 - | -LL | comptime_fn(); - | ^^^^^^^^^^^^^ + = help: `#[rustc_comptime]` can be applied to functions and inherent impl blocks error[E0277]: the trait bound `Bar: const Foo` is not satisfied - --> $DIR/comptime_impl.rs:31:9 + --> $DIR/comptime_impl.rs:29:9 | LL | Bar.foo(); | ^^^ @@ -32,7 +18,7 @@ LL | impl const Foo for Bar { | +++++ error[E0277]: the trait bound `Bar: const Foo` is not satisfied - --> $DIR/comptime_impl.rs:33:9 + --> $DIR/comptime_impl.rs:31:9 | LL | Bar.bar(); | ^^^ @@ -42,6 +28,6 @@ help: make the `impl` of trait `Foo` `const` LL | impl const Foo for Bar { | +++++ -error: aborting due to 5 previous errors +error: aborting due to 3 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/comptime/comptime_method.rs b/tests/ui/comptime/comptime_method.rs index a0618f97e0ea8..a55c950b01680 100644 --- a/tests/ui/comptime/comptime_method.rs +++ b/tests/ui/comptime/comptime_method.rs @@ -3,14 +3,15 @@ struct Bar; #[rustc_comptime] -//~^ ERROR: cannot be used on inherent impl impl Bar { fn boo(&self) {} } const _: () = { Bar.boo(); - //~^ ERROR: cannot call non-const method `Bar::boo` in constants }; -fn main() {} +fn main() { + Bar.boo(); + //~^ ERROR: comptime fns can only be called at compile time +} diff --git a/tests/ui/comptime/comptime_method.stderr b/tests/ui/comptime/comptime_method.stderr index 682e0c364e43c..f188ab7e4be58 100644 --- a/tests/ui/comptime/comptime_method.stderr +++ b/tests/ui/comptime/comptime_method.stderr @@ -1,19 +1,8 @@ -error: `#[rustc_comptime]` attribute cannot be used on inherent impl blocks - --> $DIR/comptime_method.rs:5:1 - | -LL | #[rustc_comptime] - | ^^^^^^^^^^^^^^^^^ - | - = help: `#[rustc_comptime]` can only be applied to functions - -error[E0015]: cannot call non-const method `Bar::boo` in constants - --> $DIR/comptime_method.rs:12:9 +error: comptime fns can only be called at compile time + --> $DIR/comptime_method.rs:15:5 | LL | Bar.boo(); - | ^^^^^ - | - = note: calls in constants are limited to constant functions, tuple structs and tuple variants + | ^^^^^^^^^ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/comptime/comptime_method_bounds.rs b/tests/ui/comptime/comptime_method_bounds.rs new file mode 100644 index 0000000000000..993fd28abd1f1 --- /dev/null +++ b/tests/ui/comptime/comptime_method_bounds.rs @@ -0,0 +1,24 @@ +//@check-pass + +#![feature(rustc_attrs, const_trait_impl)] + +struct Bar(T); + +const trait Trait { + fn method(&self) {} +} + +#[rustc_comptime] +impl Bar { + fn boo(&self) { + self.0.method() + } +} + +const impl Trait for () {} + +const _: () = { + Bar(()).boo(); +}; + +fn main() {} diff --git a/tests/ui/comptime/comptime_trait.rs b/tests/ui/comptime/comptime_trait.rs new file mode 100644 index 0000000000000..6f7b601149191 --- /dev/null +++ b/tests/ui/comptime/comptime_trait.rs @@ -0,0 +1,32 @@ +#![feature(rustc_attrs, const_trait_impl, trait_alias)] + +#[rustc_comptime] +//~^ ERROR: `#[rustc_comptime]` attribute cannot be used on traits +trait Trait { + fn method(&self) {} +} + +const impl Trait for () {} + +#[rustc_comptime] +//~^ ERROR: `#[rustc_comptime]` attribute cannot be used on trait impl +impl Trait for u32 { + fn method(&self) { + comptime_fn(); + } +} + +#[rustc_comptime] +fn comptime_fn() {} + +#[rustc_comptime] +//~^ ERROR: `#[rustc_comptime]` attribute cannot be used on trait aliases +trait TraitAlias = const Trait; + +#[rustc_comptime] +fn func(t: &T) { + t.method() + //~^ ERROR: cannot call non-const method `::method` in constants +} + +fn main() {} diff --git a/tests/ui/comptime/comptime_trait.stderr b/tests/ui/comptime/comptime_trait.stderr new file mode 100644 index 0000000000000..8e9272e238fe1 --- /dev/null +++ b/tests/ui/comptime/comptime_trait.stderr @@ -0,0 +1,35 @@ +error: `#[rustc_comptime]` attribute cannot be used on traits + --> $DIR/comptime_trait.rs:3:1 + | +LL | #[rustc_comptime] + | ^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_comptime]` can be applied to functions and inherent impl blocks + +error: `#[rustc_comptime]` attribute cannot be used on trait impl blocks + --> $DIR/comptime_trait.rs:11:1 + | +LL | #[rustc_comptime] + | ^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_comptime]` can be applied to functions and inherent impl blocks + +error: `#[rustc_comptime]` attribute cannot be used on trait aliases + --> $DIR/comptime_trait.rs:22:1 + | +LL | #[rustc_comptime] + | ^^^^^^^^^^^^^^^^^ + | + = help: `#[rustc_comptime]` can be applied to functions and inherent impl blocks + +error[E0015]: cannot call non-const method `::method` in constants + --> $DIR/comptime_trait.rs:28:7 + | +LL | t.method() + | ^^^^^^^^ + | + = note: calls in constants are limited to constant functions, tuple structs and tuple variants + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0015`. diff --git a/tests/ui/comptime/trait_comptime.stderr b/tests/ui/comptime/trait_comptime.stderr index 06e982288471d..0ff981e290b17 100644 --- a/tests/ui/comptime/trait_comptime.stderr +++ b/tests/ui/comptime/trait_comptime.stderr @@ -4,7 +4,7 @@ error: `#[rustc_comptime]` attribute cannot be used on required trait methods LL | #[rustc_comptime] | ^^^^^^^^^^^^^^^^^ | - = help: `#[rustc_comptime]` can only be applied to functions with a body + = help: `#[rustc_comptime]` can be applied to functions with a body and inherent impl blocks error: `#[rustc_comptime]` attribute cannot be used on provided trait methods --> $DIR/trait_comptime.rs:8:5 @@ -12,7 +12,7 @@ error: `#[rustc_comptime]` attribute cannot be used on provided trait methods LL | #[rustc_comptime] | ^^^^^^^^^^^^^^^^^ | - = help: `#[rustc_comptime]` can be applied to functions and inherent methods + = help: `#[rustc_comptime]` can be applied to functions, inherent impl blocks, and inherent methods error: `#[rustc_comptime]` attribute cannot be used on trait methods in impl blocks --> $DIR/trait_comptime.rs:21:5 @@ -20,7 +20,7 @@ error: `#[rustc_comptime]` attribute cannot be used on trait methods in impl blo LL | #[rustc_comptime] | ^^^^^^^^^^^^^^^^^ | - = help: `#[rustc_comptime]` can be applied to functions and inherent methods + = help: `#[rustc_comptime]` can be applied to functions, inherent impl blocks, and inherent methods error: aborting due to 3 previous errors From 7951d5d0a1468b5733be80726606d222d2f5bae2 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 10:58:26 +0200 Subject: [PATCH 42/71] sembr src/mir/index.md --- src/doc/rustc-dev-guide/src/mir/index.md | 159 ++++++++++++----------- 1 file changed, 80 insertions(+), 79 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/index.md b/src/doc/rustc-dev-guide/src/mir/index.md index 8ba5f3ac8b784..292c1817b44b6 100644 --- a/src/doc/rustc-dev-guide/src/mir/index.md +++ b/src/doc/rustc-dev-guide/src/mir/index.md @@ -1,15 +1,15 @@ # The MIR (Mid-level IR) -MIR is Rust's _Mid-level Intermediate Representation_. It is -constructed from [HIR](../hir.html). MIR was introduced in -[RFC 1211]. It is a radically simplified form of Rust that is used for -certain flow-sensitive safety checks – notably the borrow checker! – -and also for optimization and code generation. +MIR is Rust's _Mid-level Intermediate Representation_. +It is constructed from [HIR](../hir.html). +MIR was introduced in [RFC 1211]. +It is a radically simplified form of Rust that is used for +certain flow-sensitive safety checks – notably the borrow checker! +– and also for optimization and code generation. If you'd like a very high-level introduction to MIR, as well as some of the compiler concepts that it relies on (such as control-flow -graphs and desugaring), you may enjoy the -[rust-lang blog post that introduced MIR][blog]. +graphs and desugaring), you may enjoy the [rust-lang blog post that introduced MIR][blog]. [blog]: https://blog.rust-lang.org/2016/04/19/MIR.html @@ -36,28 +36,24 @@ This section introduces the key concepts of MIR, summarized here: - **Basic blocks**: units of the control-flow graph, consisting of: - **statements:** actions with one successor - - **terminators:** actions with potentially multiple successors; always at - the end of a block - - (if you're not familiar with the term *basic block*, see the [background - chapter][cfg]) + - **terminators:** actions with potentially multiple successors; always at the end of a block + - (if you're not familiar with the term *basic block*, see the [background chapter][cfg]) - **Locals:** Memory locations allocated on the stack (conceptually, at - least), such as function arguments, local variables, and - temporaries. These are identified by an index, written with a - leading underscore, like `_1`. There is also a special "local" - (`_0`) allocated to store the return value. -- **Places:** expressions that identify a location in memory, like `_1` or - `_1.f`. -- **Rvalues:** expressions that produce a value. The "R" stands for - the fact that these are the "right-hand side" of an assignment. + least), such as function arguments, local variables, and temporaries. + These are identified by an index, written with a leading underscore, like `_1`. + There is also a special "local" (`_0`) allocated to store the return value. +- **Places:** expressions that identify a location in memory, like `_1` or `_1.f`. +- **Rvalues:** expressions that produce a value. + The "R" stands for the fact that these are the "right-hand side" of an assignment. - **Operands:** the arguments to an rvalue, which can either be a constant (like `22`) or a place (like `_1`). You can get a feeling for how MIR is constructed by translating simple -programs into MIR and reading the pretty printed output. In fact, the -playground makes this easy, since it supplies a MIR button that will -show you the MIR for your program. Try putting this program into play -(or [clicking on this link][sample-play]), and then clicking the "MIR" -button on the top: +programs into MIR and reading the pretty printed output. +In fact, the playground makes this easy, since it supplies a MIR button that will +show you the MIR for your program. +Try putting this program into play +(or [clicking on this link][sample-play]), and then clicking the "MIR" button on the top: [sample-play]: https://play.rust-lang.org/?gist=30074856e62e74e91f06abd19bd72ece&version=stable&edition=2021 @@ -88,7 +84,8 @@ This requires the nightly toolchain. **Variable declarations.** If we drill in a bit, we'll see it begins -with a bunch of variable declarations. They look like this: +with a bunch of variable declarations. +They look like this: ```mir let mut _0: (); // return place @@ -124,8 +121,7 @@ annotated with `// in scope 0` would be missing `vec`, if you were stepping through the code in a debugger, for example. **Basic blocks.** Reading further, we see our first **basic block** (naturally -it may look slightly different when you view it, and I am ignoring some of the -comments): +it may look slightly different when you view it, and I am ignoring some of the comments): ```mir bb0: { @@ -143,9 +139,8 @@ StorageLive(_1); This statement indicates that the variable `_1` is "live", meaning that it may be used later – this will persist until we encounter a -`StorageDead(_1)` statement, which indicates that the variable `_1` is -done being used. These "storage statements" are used by LLVM to -allocate stack space. +`StorageDead(_1)` statement, which indicates that the variable `_1` is done being used. +These "storage statements" are used by LLVM to allocate stack space. The **terminator** of the block `bb0` is the call to `Vec::new`: @@ -154,8 +149,8 @@ _1 = const >::new() -> bb2; ``` Terminators are different from statements because they can have more -than one successor – that is, control may flow to different -places. Function calls like the call to `Vec::new` are always +than one successor – that is, control may flow to different places. +Function calls like the call to `Vec::new` are always terminators because of the possibility of unwinding, although in the case of `Vec::new` we are able to see that indeed unwinding is not possible, and hence we list only one successor block, `bb2`. @@ -183,11 +178,10 @@ Assignments in general have the form: = ``` -A place is an expression like `_3`, `_3.f` or `*_3` – it denotes a -location in memory. An **Rvalue** is an expression that creates a -value: in this case, the rvalue is a mutable borrow expression, which -looks like `&mut `. So we can kind of define a grammar for -rvalues like so: +A place is an expression like `_3`, `_3.f` or `*_3` – it denotes a location in memory. + An **Rvalue** is an expression that creates a +value: in this case, the rvalue is a mutable borrow expression, which looks like `&mut `. +So we can kind of define a grammar for rvalues like so: ```text = & (mut)? @@ -201,12 +195,12 @@ rvalues like so: ``` As you can see from this grammar, rvalues cannot be nested – they can -only reference places and constants. Moreover, when you use a place, +only reference places and constants. +Moreover, when you use a place, we indicate whether we are **copying it** (which requires that the -place have a type `T` where `T: Copy`) or **moving it** (which works -for a place of any type). So, for example, if we had the expression `x -= a + b + c` in Rust, that would get compiled to two statements and a -temporary: +place have a type `T` where `T: Copy`) or **moving it** (which works for a place of any type). +So, for example, if we had the expression `x += a + b + c` in Rust, that would get compiled to two statements and a temporary: ```mir TMP1 = a + b @@ -220,33 +214,33 @@ over the overflow checks.) ## MIR data types -The MIR data types are defined in the [`compiler/rustc_middle/src/mir/`][mir] -module. Each of the key concepts mentioned in the previous section +The MIR data types are defined in the [`compiler/rustc_middle/src/mir/`][mir] module. +Each of the key concepts mentioned in the previous section maps in a fairly straightforward way to a Rust type. -The main MIR data type is [`Body`]. It contains the data for a single +The main MIR data type is [`Body`]. +It contains the data for a single function (along with sub-instances of Mir for "promoted constants", but [you can read about those below](#promoted)). - **Basic blocks**: The basic blocks are stored in the field - [`Body::basic_blocks`][basicblocks]; this is a vector - of [`BasicBlockData`] structures. Nobody ever references a - basic block directly: instead, we pass around [`BasicBlock`] + [`Body::basic_blocks`][basicblocks]; this is a vector of [`BasicBlockData`] structures. + Nobody ever references a basic block directly: instead, we pass around [`BasicBlock`] values, which are [newtype'd] indices into this vector. - **Statements** are represented by the type [`Statement`]. - **Terminators** are represented by the [`Terminator`]. - **Locals** are represented by a [newtype'd] index type [`Local`]. - The data for a local variable is found in the - [`Body::local_decls`][localdecls] vector. There is also a special constant + The data for a local variable is found in the [`Body::local_decls`][localdecls] vector. + There is also a special constant [`RETURN_PLACE`] identifying the special "local" representing the return value. -- **Places** are identified by the struct [`Place`]. There are a few - fields: +- **Places** are identified by the struct [`Place`]. + There are a few fields: - Local variables like `_1` - - **Projections**, which are fields or other things that "project - out" from a base place. These are represented by the [newtype'd] type + - **Projections**, which are fields or other things that "project out" from a base place. + These are represented by the [newtype'd] type [`ProjectionElem`]. So e.g. the place `_1.f` is a projection, - with `f` being the "projection element" and `_1` being the base - path. `*_1` is also a projection, with the `*` being represented + with `f` being the "projection element" and `_1` being the base path. + `*_1` is also a projection, with the `*` being represented by the [`ProjectionElem::Deref`] element. - **Rvalues** are represented by the enum [`Rvalue`]. - **Operands** are represented by the enum [`Operand`]. @@ -256,12 +250,14 @@ but [you can read about those below](#promoted)). When code has reached the MIR stage, constants can generally come in two forms: *MIR constants* ([`mir::Constant`]) and *type system constants* ([`ty::Const`]). MIR constants are used as operands: in `x + CONST`, `CONST` is a MIR constant; -similarly, in `x + 2`, `2` is a MIR constant. Type system constants are used in +similarly, in `x + 2`, `2` is a MIR constant. +Type system constants are used in the type system, in particular for array lengths but also for const generics. Generally, both kinds of constants can be "unevaluated" or "already evaluated". An unevaluated constant simply stores the `DefId` of what needs to be evaluated -to compute this result. An evaluated constant (a "value") has already been +to compute this result. +An evaluated constant (a "value") has already been computed; their representation differs between type system constants and MIR constants: MIR constants evaluate to a `mir::ConstValue`; type system constants evaluate to a `ty::ValTree`. @@ -275,22 +271,24 @@ parameter is used as an operand. ### MIR constant values In general, a MIR constant value (`mir::ConstValue`) was computed by evaluating -some constant the user wrote. This [const evaluation](../const-eval.md) produces -a very low-level representation of the result in terms of individual bytes. We -call this an "indirect" constant (`mir::ConstValue::Indirect`) since the value +some constant the user wrote. +This [const evaluation](../const-eval.md) produces +a very low-level representation of the result in terms of individual bytes. +We call this an "indirect" constant (`mir::ConstValue::Indirect`) since the value is stored in-memory. -However, storing everything in-memory would be awfully inefficient. Hence there -are some other variants in `mir::ConstValue` that can represent certain simple -and common values more efficiently. In particular, everything that can be +However, storing everything in-memory would be awfully inefficient. +Hence there are some other variants in `mir::ConstValue` that can represent certain simple +and common values more efficiently. +In particular, everything that can be directly written as a literal in Rust (integers, floats, chars, bools, but also `"string literals"` and `b"byte string literals"`) has an optimized variant that avoids the full overhead of the in-memory representation. ### ValTrees -An evaluated type system constant is a "valtree". The `ty::ValTree` datastructure -allows us to represent +An evaluated type system constant is a "valtree". +The `ty::ValTree` datastructure allows us to represent * arrays, * many structs, @@ -298,30 +296,33 @@ allows us to represent * enums and, * most primitives. -The most important rule for -this representation is that every value must be uniquely represented. In other -words: a specific value must only be representable in one specific way. For example: there is only -one way to represent an array of two integers as a `ValTree`: +The most important rule for this representation is that every value must be uniquely represented. +In other words: a specific value must only be representable in one specific way. +For example: there is only one way to represent an array of two integers as a `ValTree`: `Branch([Leaf(first_int), Leaf(second_int)])`. Even though theoretically a `[u32; 2]` could be encoded in a `u64` and thus just be a `Leaf(bits_of_two_u32)`, that is not a legal construction of `ValTree` (and is very complex to do, so it is unlikely anyone is tempted to do so). -These rules also mean that some values are not representable. There can be no `union`s in type +These rules also mean that some values are not representable. +There can be no `union`s in type level constants, as it is not clear how they should be represented, because their active variant -is unknown. Similarly there is no way to represent raw pointers, as addresses are unknown at -compile-time and thus we cannot make any assumptions about them. References on the other hand +is unknown. +Similarly there is no way to represent raw pointers, as addresses are unknown at +compile-time and thus we cannot make any assumptions about them. +References on the other hand *can* be represented, as equality for references is defined as equality on their value, so we -ignore their address and just look at the backing value. We must make sure that the pointer values -of the references are not observable at compile time. We thus encode `&42` exactly like `42`. -Any conversion from -valtree back to a MIR constant value must reintroduce an actual indirection. At codegen time the +ignore their address and just look at the backing value. +We must make sure that the pointer values of the references are not observable at compile time. +We thus encode `&42` exactly like `42`. +Any conversion from valtree back to a MIR constant value must reintroduce an actual indirection. +At codegen time the addresses may be deduplicated between multiple uses or not, entirely depending on arbitrary optimization choices. As a consequence, all decoding of `ValTree` must happen by matching on the type first and making -decisions depending on that. The value itself gives no useful information without the type that -belongs to it. +decisions depending on that. +The value itself gives no useful information without the type that belongs to it. From 29668b5535ed6b015788c08932491cb5f19b23da Mon Sep 17 00:00:00 2001 From: Roland Xu Date: Tue, 14 Jul 2026 17:01:16 +0800 Subject: [PATCH 43/71] Construct tokens for attrs made by mk_attr_word --- compiler/rustc_ast/src/attr/mod.rs | 135 +++++++++++++++--- .../test-followed-by-attr-macros.rs | 26 ++++ 2 files changed, 139 insertions(+), 22 deletions(-) create mode 100644 tests/ui/proc-macro/test-followed-by-attr-macros.rs diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index 2e54e1a7c320c..9774173f24780 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -7,7 +7,7 @@ use std::fmt::Debug; use std::sync::atomic::{AtomicU32, Ordering}; use rustc_index::bit_set::GrowableBitSet; -use rustc_span::{Ident, Span, Symbol, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use smallvec::{SmallVec, smallvec}; use thin_vec::{ThinVec, thin_vec}; @@ -21,7 +21,8 @@ use crate::token::{ self, CommentKind, Delimiter, DocFragmentKind, InvisibleOrigin, MetaVarKind, Token, }; use crate::tokenstream::{ - DelimSpan, LazyAttrTokenStream, Spacing, TokenStream, TokenStreamIter, TokenTree, + AttrTokenStream, AttrTokenTree, DelimSpacing, DelimSpan, LazyAttrTokenStream, Spacing, + TokenStream, TokenStreamIter, TokenTree, }; use crate::util::comments; use crate::util::literal::escape_string_symbol; @@ -737,23 +738,6 @@ pub fn mk_doc_comment( Attribute { kind: AttrKind::DocComment(comment_kind, data), id: g.mk_attr_id(), style, span } } -fn mk_attr( - g: &AttrIdGenerator, - style: AttrStyle, - unsafety: Safety, - path: Path, - args: AttrArgs, - span: Span, -) -> Attribute { - mk_attr_from_item( - g, - AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args) }, - None, - style, - span, - ) -} - pub fn mk_attr_from_item( g: &AttrIdGenerator, item: AttrItem, @@ -769,6 +753,50 @@ pub fn mk_attr_from_item( } } +fn mk_attr_tokens( + style: AttrStyle, + unsafety: Safety, + item_tokens: AttrTokenStream, + span: Span, +) -> LazyAttrTokenStream { + let safety_kw = match unsafety { + Safety::Default => None, + Safety::Unsafe(span) => Some((kw::Unsafe, span)), + Safety::Safe(span) => Some((kw::Safe, span)), + }; + let item_tokens = if let Some((kw, kw_span)) = safety_kw { + AttrTokenStream::new(vec![ + AttrTokenTree::Token(Token::from_ast_ident(Ident::new(kw, kw_span)), Spacing::Alone), + AttrTokenTree::Delimited( + DelimSpan::from_single(span), + DelimSpacing::new(Spacing::JointHidden, Spacing::Alone), + Delimiter::Parenthesis, + item_tokens, + ), + ]) + } else { + item_tokens + }; + + let mut tokens = match style { + AttrStyle::Outer => { + vec![AttrTokenTree::Token(Token::new(token::Pound, span), Spacing::JointHidden)] + } + AttrStyle::Inner => vec![ + AttrTokenTree::Token(Token::new(token::Pound, span), Spacing::Joint), + AttrTokenTree::Token(Token::new(token::Bang, span), Spacing::JointHidden), + ], + }; + tokens.push(AttrTokenTree::Delimited( + DelimSpan::from_single(span), + DelimSpacing::new(Spacing::JointHidden, Spacing::Alone), + Delimiter::Bracket, + item_tokens, + )); + + LazyAttrTokenStream::new_direct(AttrTokenStream::new(tokens)) +} + pub fn mk_attr_word( g: &AttrIdGenerator, style: AttrStyle, @@ -778,7 +806,24 @@ pub fn mk_attr_word( ) -> Attribute { let path = Path::from_ident(Ident::new(name, span)); let args = AttrArgs::Empty; - mk_attr(g, style, unsafety, path, args, span) + + let tokens = Some(mk_attr_tokens( + style, + unsafety, + AttrTokenStream::new(vec![AttrTokenTree::Token( + Token::from_ast_ident(Ident::new(name, span)), + Spacing::Alone, + )]), + span, + )); + + mk_attr_from_item( + g, + AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args) }, + tokens, + style, + span, + ) } pub fn mk_attr_nested_word( @@ -800,7 +845,32 @@ pub fn mk_attr_nested_word( delim: Delimiter::Parenthesis, tokens: inner_tokens, }); - mk_attr(g, style, unsafety, path, attr_args, span) + + let tokens = Some(mk_attr_tokens( + style, + unsafety, + AttrTokenStream::new(vec![ + AttrTokenTree::Token(Token::from_ast_ident(Ident::new(outer, span)), Spacing::Alone), + AttrTokenTree::Delimited( + DelimSpan::from_single(span), + DelimSpacing::new(Spacing::JointHidden, Spacing::Alone), + Delimiter::Parenthesis, + AttrTokenStream::new(vec![AttrTokenTree::Token( + Token::from_ast_ident(Ident::new(inner, span)), + Spacing::Alone, + )]), + ), + ]), + span, + )); + + mk_attr_from_item( + g, + AttrItem { unsafety, path, args: AttrItemKind::Unparsed(attr_args) }, + tokens, + style, + span, + ) } pub fn mk_attr_name_value_str( @@ -821,7 +891,28 @@ pub fn mk_attr_name_value_str( }); let path = Path::from_ident(Ident::new(name, span)); let args = AttrArgs::Eq { eq_span: span, expr }; - mk_attr(g, style, unsafety, path, args, span) + + let tokens = Some(mk_attr_tokens( + style, + unsafety, + AttrTokenStream::new(vec![ + AttrTokenTree::Token(Token::from_ast_ident(Ident::new(name, span)), Spacing::Alone), + AttrTokenTree::Token(Token::new(token::Eq, span), Spacing::Alone), + AttrTokenTree::Token( + Token::new(token::TokenKind::lit(lit.kind, lit.symbol, lit.suffix), span), + Spacing::Alone, + ), + ]), + span, + )); + + mk_attr_from_item( + g, + AttrItem { unsafety, path, args: AttrItemKind::Unparsed(args) }, + tokens, + style, + span, + ) } pub fn filter_by_name(attrs: &[Attribute], name: Symbol) -> impl Iterator { diff --git a/tests/ui/proc-macro/test-followed-by-attr-macros.rs b/tests/ui/proc-macro/test-followed-by-attr-macros.rs new file mode 100644 index 0000000000000..4f5b9801c552c --- /dev/null +++ b/tests/ui/proc-macro/test-followed-by-attr-macros.rs @@ -0,0 +1,26 @@ +//@ check-pass +//@ compile-flags: --test +//@ proc-macro: test-macros.rs +//@ edition: 2024 + +#![feature(macro_attr)] + +extern crate test_macros; + +fn main() {} + +mod proc_macro_attr { + #[test] + #[test_macros::identity_attr] + fn test() {} +} + +mod macro_rules_attr { + macro_rules! repro { + attr() { $($tt:tt)* } => { $($tt)* } + } + + #[test] + #[repro] + fn test() {} +} From a8019c596798e409c16964efe3a963ad024f4202 Mon Sep 17 00:00:00 2001 From: Adwin White Date: Wed, 8 Jul 2026 21:58:02 +0800 Subject: [PATCH 44/71] rerun if we meet any opaques in post analysis --- .../src/solve/eval_ctxt/mod.rs | 2 +- ...paque-or-has-infer-as-hidden-in-codegen.rs | 34 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) create mode 100644 tests/ui/traits/next-solver/rerun-if-any-opaque-or-has-infer-as-hidden-in-codegen.rs diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index d83320f5fe800..3a98abf06a342 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -870,7 +870,7 @@ where ( RerunCondition::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(_), TypingMode::PostAnalysis | TypingMode::Codegen, - ) => RerunDecision::No, + ) => RerunDecision::Yes, ( RerunCondition::OpaqueInStorageOrAnyOpaqueHasInferAsHidden(defids), TypingMode::Typeck { defining_opaque_types_and_generators: opaques }, diff --git a/tests/ui/traits/next-solver/rerun-if-any-opaque-or-has-infer-as-hidden-in-codegen.rs b/tests/ui/traits/next-solver/rerun-if-any-opaque-or-has-infer-as-hidden-in-codegen.rs new file mode 100644 index 0000000000000..8f691ad8f4c45 --- /dev/null +++ b/tests/ui/traits/next-solver/rerun-if-any-opaque-or-has-infer-as-hidden-in-codegen.rs @@ -0,0 +1,34 @@ +//@ compile-flags: -Znext-solver +//@ check-pass + +fn mk_vec() -> Vec> { + loop {} +} + +fn parse_feature(feature: &str) -> impl Iterator { + std::iter::once(feature) +} + +fn main() { + // In `codegen_select_candidate`, we try to find an impl for `FlatMap::into_iter` + // We try to prove `FlatMap<...>: IntoIterator`. + // The blanket impl requires `FlatMap<...>: Iterator`. + // The `Iterator` impl of `FlatMap` has nested goals: + // `Projection(Fn::Output, iter::Once`. + // We normalize this goal and set rerun condition to `AnyOpaqueHasInferAsHidden` + // with reason `SelfTyInfer` because we instantiated impls with infers when + // assembling candidates for intermediate goals. + // Then we evaluate the normalized goal: + // `Projection(Fn::Output, iter::Once`. + // The alias term is normalized to rigid alias `impl Iterator` as + // we're in `TypingMode::ErasedNotCoherence`. + // But the expected term is revealed `iter::Once` thus relating failed. + // The goal fails with rerun condition `OpaqueInStorage(parse_feature::opaque)`. + // This goal should be rerun in `TypingMode::Codegen` mode, but + // `AnyOpaqueHasInferAsHidden + OpaqueInStorage = OpaqueInStorageOrAnyOpaqueHasInferAsHidden` + // which didn't trigger rerun in `TypingMode::Codegen` mode previously. + mk_vec() + .into_iter() + .flatten() + .flat_map(parse_feature).into_iter(); +} From c9528a4cabb9e4bae4b5bbed11dc2e95aa1aa56c Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 11:45:08 +0200 Subject: [PATCH 45/71] improve mir/index.md --- src/doc/rustc-dev-guide/src/mir/index.md | 32 +++++++++++------------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/index.md b/src/doc/rustc-dev-guide/src/mir/index.md index 292c1817b44b6..45d72226807a7 100644 --- a/src/doc/rustc-dev-guide/src/mir/index.md +++ b/src/doc/rustc-dev-guide/src/mir/index.md @@ -179,7 +179,7 @@ Assignments in general have the form: ``` A place is an expression like `_3`, `_3.f` or `*_3` – it denotes a location in memory. - An **Rvalue** is an expression that creates a +An **Rvalue** is an expression that creates a value: in this case, the rvalue is a mutable borrow expression, which looks like `&mut `. So we can kind of define a grammar for rvalues like so: @@ -221,7 +221,7 @@ maps in a fairly straightforward way to a Rust type. The main MIR data type is [`Body`]. It contains the data for a single function (along with sub-instances of Mir for "promoted constants", -but [you can read about those below](#promoted)). +but [you can read about those below](#promoted-constants)). - **Basic blocks**: The basic blocks are stored in the field [`Body::basic_blocks`][basicblocks]; this is a vector of [`BasicBlockData`] structures. @@ -297,35 +297,33 @@ The `ty::ValTree` datastructure allows us to represent * most primitives. The most important rule for this representation is that every value must be uniquely represented. -In other words: a specific value must only be representable in one specific way. -For example: there is only one way to represent an array of two integers as a `ValTree`: +In other words, a specific value must only be representable in one specific way. +For example, there is only one way to represent an array of two integers as a `ValTree`: `Branch([Leaf(first_int), Leaf(second_int)])`. Even though theoretically a `[u32; 2]` could be encoded in a `u64` and thus just be a `Leaf(bits_of_two_u32)`, that is not a legal construction of `ValTree` (and is very complex to do, so it is unlikely anyone is tempted to do so). These rules also mean that some values are not representable. -There can be no `union`s in type -level constants, as it is not clear how they should be represented, because their active variant -is unknown. -Similarly there is no way to represent raw pointers, as addresses are unknown at -compile-time and thus we cannot make any assumptions about them. -References on the other hand -*can* be represented, as equality for references is defined as equality on their value, so we -ignore their address and just look at the backing value. +There can be no `union`s in type level constants, +as it is not clear how they should be represented, +because their active variant is unknown. +Similarly, there is no way to represent raw pointers as addresses are unknown at compile-time, +and thus we cannot make any assumptions about them. +References on the other hand *can* be represented, +as equality for references is defined as equality on their value, +so we ignore their address and just look at the backing value. We must make sure that the pointer values of the references are not observable at compile time. We thus encode `&42` exactly like `42`. Any conversion from valtree back to a MIR constant value must reintroduce an actual indirection. -At codegen time the -addresses may be deduplicated between multiple uses or not, entirely depending on arbitrary -optimization choices. +At codegen time, +the addresses may be deduplicated between multiple uses or not, +entirely depending on arbitrary optimization choices. As a consequence, all decoding of `ValTree` must happen by matching on the type first and making decisions depending on that. The value itself gives no useful information without the type that belongs to it. - - ### Promoted constants See the const-eval WG's [docs on promotion](https://github.com/rust-lang/const-eval/blob/master/promotion.md). From f2dacde4957eda36e950c6ae9b7307119e33f15e Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 11:45:23 +0200 Subject: [PATCH 46/71] sembr src/mir/dataflow.md --- src/doc/rustc-dev-guide/src/mir/dataflow.md | 94 +++++++++++---------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/dataflow.md b/src/doc/rustc-dev-guide/src/mir/dataflow.md index 970e61196c122..5b33f0343ee18 100644 --- a/src/doc/rustc-dev-guide/src/mir/dataflow.md +++ b/src/doc/rustc-dev-guide/src/mir/dataflow.md @@ -1,51 +1,54 @@ # Dataflow Analysis If you work on the MIR, you will frequently come across various flavors of -[dataflow analysis][wiki]. `rustc` uses dataflow to find uninitialized +[dataflow analysis][wiki]. +`rustc` uses dataflow to find uninitialized variables, determine what variables are live across a generator `yield` -statement, and compute which `Place`s are borrowed at a given point in the -control-flow graph. Dataflow analysis is a fundamental concept in modern -compilers, and knowledge of the subject will be helpful to prospective -contributors. +statement, and compute which `Place`s are borrowed at a given point in the control-flow graph. +Dataflow analysis is a fundamental concept in modern +compilers, and knowledge of the subject will be helpful to prospective contributors. However, this documentation is not a general introduction to dataflow analysis. -It is merely a description of the framework used to define these analyses in -`rustc`. It assumes that the reader is familiar with the core ideas as well as +It is merely a description of the framework used to define these analyses in `rustc`. +It assumes that the reader is familiar with the core ideas as well as some basic terminology, such as "transfer function", "fixpoint" and "lattice". If you're unfamiliar with these terms, or if you want a quick refresher, -[*Static Program Analysis*] by Anders Møller and Michael I. Schwartzbach is an -excellent, freely available textbook. For those who prefer audiovisual -learning, we previously recommended a series of short lectures +[*Static Program Analysis*] by Anders Møller and Michael I. +Schwartzbach is an excellent, freely available textbook. +For those who prefer audiovisual learning, we previously recommended a series of short lectures by the Goethe University Frankfurt on YouTube, but it has since been deleted. See [this PR][pr-1295] for the context and [this comment][pr-1295-comment] for the alternative lectures. ## Defining a Dataflow Analysis -A dataflow analysis is defined by the [`Analysis`] trait. In addition to the -type of the dataflow state, this trait defines the initial value of that state -at entry to each block, as well as the direction of the analysis, either -forward or backward. The domain of your dataflow analysis must be a [lattice][] -(strictly speaking a join-semilattice) with a well-behaved `join` operator. See -documentation for the [`lattice`] module, as well as the [`JoinSemiLattice`] +A dataflow analysis is defined by the [`Analysis`] trait. +In addition to the type of the dataflow state, this trait defines the initial value of that state +at entry to each block, as well as the direction of the analysis, either forward or backward. +The domain of your dataflow analysis must be a [lattice][] +(strictly speaking a join-semilattice) with a well-behaved `join` operator. +See documentation for the [`lattice`] module, as well as the [`JoinSemiLattice`] trait, for more information. ### Transfer Functions and Effects The dataflow framework in `rustc` allows each statement (and terminator) inside -a basic block to define its own transfer function. For brevity, these -individual transfer functions are known as "effects". Each effect is applied +a basic block to define its own transfer function. +For brevity, these individual transfer functions are known as "effects". +Each effect is applied successively in dataflow order, and together they define the transfer function -for the entire basic block. It's also possible to define an effect for +for the entire basic block. +It's also possible to define an effect for particular outgoing edges of some terminators (e.g. -[`apply_call_return_effect`] for the `success` edge of a `Call` -terminator). Collectively, these are referred to as "per-edge effects". +[`apply_call_return_effect`] for the `success` edge of a `Call` terminator). +Collectively, these are referred to as "per-edge effects". ### "Before" Effects Observant readers of the documentation may notice that there are actually *two* possible effects for each statement and terminator, the "before" effect and the -unprefixed (or "primary") effect. The "before" effects are applied immediately +unprefixed (or "primary") effect. +The "before" effects are applied immediately before the unprefixed effect **regardless of the direction of the analysis**. In other words, a backward analysis will apply the "before" effect and then the "primary" effect when computing the transfer function for a basic block, just @@ -53,7 +56,8 @@ like a forward analysis. The vast majority of analyses should use only the unprefixed effects: Having multiple effects for each statement makes it difficult for consumers to know -where they should be looking. However, the "before" variants can be useful in +where they should be looking. +However, the "before" variants can be useful in some scenarios, such as when the effect of the right-hand side of an assignment statement must be considered separately from the left-hand side. @@ -61,9 +65,10 @@ statement must be considered separately from the left-hand side. Your analysis must converge to "fixpoint", otherwise it will run forever. Converging to fixpoint is just another way of saying "reaching equilibrium". -In order to reach equilibrium, your analysis must obey some laws. One of the -laws it must obey is that the bottom value[^bottom-purpose] joined with some -other value equals the second value. Or, as an equation: +In order to reach equilibrium, your analysis must obey some laws. +One of the laws it must obey is that the bottom value[^bottom-purpose] joined with some +other value equals the second value. +Or, as an equation: > *bottom* join *x* = *x* @@ -76,22 +81,23 @@ law state above ensures that once the dataflow state reaches top, it will no longer change (the fixpoint will be top). [^bottom-purpose]: The bottom value's primary purpose is as the initial dataflow - state. Each basic block's entry state is initialized to bottom before the - analysis starts. + state. + Each basic block's entry state is initialized to bottom before the analysis starts. ## A Brief Example -This section provides a brief example of a simple data-flow analysis at a high -level. It doesn't explain everything you need to know, but hopefully it will +This section provides a brief example of a simple data-flow analysis at a high level. +It doesn't explain everything you need to know, but hopefully it will make the rest of this page clearer. Let's say we want to do a simple analysis to find if `mem::transmute` may have -been called by a certain point in the program. Our analysis domain will just -be a `bool` that records whether `transmute` has been called so far. The bottom -value will be `false`, since by default `transmute` has not been called. The top -value will be `true`, since our analysis is done as soon as we determine that +been called by a certain point in the program. +Our analysis domain will just be a `bool` that records whether `transmute` has been called so far. +The bottom value will be `false`, since by default `transmute` has not been called. +The top value will be `true`, since our analysis is done as soon as we determine that `transmute` has been called. Our join operator will just be the boolean OR (`||`) -operator. We use OR and not AND because of this case: +operator. +We use OR and not AND because of this case: ```rust # unsafe fn example(some_cond: bool) { @@ -111,11 +117,11 @@ println!("x: {}", x); Once you have constructed an analysis, you must call `iterate_to_fixpoint` which will return a `Results`, which contains the dataflow state at fixpoint -upon entry of each block. Once you have a `Results`, you can inspect the -dataflow state at fixpoint at any point in the CFG. If you only need the state +upon entry of each block. +Once you have a `Results`, you can inspect the dataflow state at fixpoint at any point in the CFG. +If you only need the state at a few locations (e.g., each `Drop` terminator) use a [`ResultsCursor`]. If -you need the state at *every* location, a [`ResultsVisitor`] will be more -efficient. +you need the state at *every* location, a [`ResultsVisitor`] will be more efficient. ```text Analysis @@ -161,16 +167,16 @@ for (bb, block) in body.basic_blocks().iter_enumerated() { ### Graphviz Diagrams -When the results of a dataflow analysis are not what you expect, it often helps -to visualize them. This can be done with the `-Z dump-mir` flags described in -[Debugging MIR]. Start with `-Z dump-mir=F -Z dump-mir-dataflow`, where `F` is +When the results of a dataflow analysis are not what you expect, it often helps to visualize them. +This can be done with the `-Z dump-mir` flags described in [Debugging MIR]. +Start with `-Z dump-mir=F -Z dump-mir-dataflow`, where `F` is either "all" or the name of the MIR body you are interested in. These `.dot` files will be saved in your `mir_dump` directory and will have the [`NAME`] of the analysis (e.g. `maybe_inits`) as part of their filename. Each visualization will display the full dataflow state at entry and exit of each -block, as well as any changes that occur in each statement and terminator. See -the example below: +block, as well as any changes that occur in each statement and terminator. + See the example below: ![A graphviz diagram for a dataflow analysis](../img/dataflow-graphviz-example.png) From 084cbebf968abdfc9561812cff32859046706319 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 11:54:24 +0200 Subject: [PATCH 47/71] improve mir/dataflow.md --- src/doc/rustc-dev-guide/src/mir/dataflow.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/dataflow.md b/src/doc/rustc-dev-guide/src/mir/dataflow.md index 5b33f0343ee18..b679b258fff78 100644 --- a/src/doc/rustc-dev-guide/src/mir/dataflow.md +++ b/src/doc/rustc-dev-guide/src/mir/dataflow.md @@ -1,7 +1,7 @@ # Dataflow Analysis If you work on the MIR, you will frequently come across various flavors of -[dataflow analysis][wiki]. +[dataflow analysis]. `rustc` uses dataflow to find uninitialized variables, determine what variables are live across a generator `yield` statement, and compute which `Place`s are borrowed at a given point in the control-flow graph. @@ -13,8 +13,8 @@ It is merely a description of the framework used to define these analyses in `ru It assumes that the reader is familiar with the core ideas as well as some basic terminology, such as "transfer function", "fixpoint" and "lattice". If you're unfamiliar with these terms, or if you want a quick refresher, -[*Static Program Analysis*] by Anders Møller and Michael I. -Schwartzbach is an excellent, freely available textbook. +[*Static Program Analysis*] by Anders Møller and Michael I. Schwartzbach is an excellent, +freely available textbook. For those who prefer audiovisual learning, we previously recommended a series of short lectures by the Goethe University Frankfurt on YouTube, but it has since been deleted. See [this PR][pr-1295] for the context and [this comment][pr-1295-comment] @@ -25,7 +25,7 @@ for the alternative lectures. A dataflow analysis is defined by the [`Analysis`] trait. In addition to the type of the dataflow state, this trait defines the initial value of that state at entry to each block, as well as the direction of the analysis, either forward or backward. -The domain of your dataflow analysis must be a [lattice][] +The domain of your dataflow analysis must be a [lattice] (strictly speaking a join-semilattice) with a well-behaved `join` operator. See documentation for the [`lattice`] module, as well as the [`JoinSemiLattice`] trait, for more information. @@ -35,9 +35,8 @@ trait, for more information. The dataflow framework in `rustc` allows each statement (and terminator) inside a basic block to define its own transfer function. For brevity, these individual transfer functions are known as "effects". -Each effect is applied -successively in dataflow order, and together they define the transfer function -for the entire basic block. +Each effect is applied successively in dataflow order, +and together they define the transfer function for the entire basic block. It's also possible to define an effect for particular outgoing edges of some terminators (e.g. [`apply_call_return_effect`] for the `success` edge of a `Call` terminator). @@ -176,7 +175,7 @@ These `.dot` files will be saved in your `mir_dump` directory and will have the [`NAME`] of the analysis (e.g. `maybe_inits`) as part of their filename. Each visualization will display the full dataflow state at entry and exit of each block, as well as any changes that occur in each statement and terminator. - See the example below: +See the example below: ![A graphviz diagram for a dataflow analysis](../img/dataflow-graphviz-example.png) @@ -195,4 +194,4 @@ block, as well as any changes that occur in each statement and terminator. [pr-1295]: https://github.com/rust-lang/rustc-dev-guide/pull/1295 [pr-1295-comment]: https://github.com/rust-lang/rustc-dev-guide/pull/1295#issuecomment-1118131294 [lattice]: https://en.wikipedia.org/wiki/Lattice_(order) -[wiki]: https://en.wikipedia.org/wiki/Data-flow_analysis#Basic_principles +[dataflow analysis]: https://en.wikipedia.org/wiki/Data-flow_analysis#Basic_principles From ef461352c2de6dd2514dd3f961bfb8badb3b65ae Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 11:56:22 +0200 Subject: [PATCH 48/71] book title should be enough Also, does not play well with sembr tool, since lines are split on the dot --- src/doc/rustc-dev-guide/src/mir/dataflow.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/dataflow.md b/src/doc/rustc-dev-guide/src/mir/dataflow.md index b679b258fff78..651de63e54a04 100644 --- a/src/doc/rustc-dev-guide/src/mir/dataflow.md +++ b/src/doc/rustc-dev-guide/src/mir/dataflow.md @@ -13,8 +13,7 @@ It is merely a description of the framework used to define these analyses in `ru It assumes that the reader is familiar with the core ideas as well as some basic terminology, such as "transfer function", "fixpoint" and "lattice". If you're unfamiliar with these terms, or if you want a quick refresher, -[*Static Program Analysis*] by Anders Møller and Michael I. Schwartzbach is an excellent, -freely available textbook. +[*Static Program Analysis*] is an excellent and freely available textbook. For those who prefer audiovisual learning, we previously recommended a series of short lectures by the Goethe University Frankfurt on YouTube, but it has since been deleted. See [this PR][pr-1295] for the context and [this comment][pr-1295-comment] From ad50c18e21539d444a4b450f5060f523b313300f Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 11:57:59 +0200 Subject: [PATCH 49/71] sembr src/mir/passes.md --- src/doc/rustc-dev-guide/src/mir/passes.md | 61 ++++++++++++----------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/passes.md b/src/doc/rustc-dev-guide/src/mir/passes.md index 5e3dbb5984ba9..d7ac6e38ab0e2 100644 --- a/src/doc/rustc-dev-guide/src/mir/passes.md +++ b/src/doc/rustc-dev-guide/src/mir/passes.md @@ -5,16 +5,17 @@ If you would like to get the MIR: - for a function - you can use the `optimized_mir` query (typically used by codegen) or the `mir_for_ctfe` query (typically used by compile time function evaluation, i.e., *CTFE*); - for a promoted - you can use the `promoted_mir` query. -These will give you back the final, optimized MIR. For foreign def-ids, we simply read the MIR -from the other crate's metadata. But for local def-ids, the query will +These will give you back the final, optimized MIR. +For foreign def-ids, we simply read the MIR from the other crate's metadata. +But for local def-ids, the query will construct the optimized MIR by requesting a pipeline of upstream queries[^query]. Each query will contain a series of passes. This section describes how those queries and passes work and how you can extend them. To produce the optimized MIR for a given def-id `D`, `optimized_mir(D)` -goes through several suites of passes, each grouped by a -query. Each suite consists of passes which perform linting, analysis, transformation or -optimization. Each query represent a useful intermediate point +goes through several suites of passes, each grouped by a query. +Each suite consists of passes which perform linting, analysis, transformation or optimization. +Each query represent a useful intermediate point where we can access the MIR dialect for type checking or other purposes: - `mir_built(D)` – it gives the initial MIR just after it's built; @@ -40,23 +41,23 @@ are defined in the [`rustc_mir_transform`][mirtransform] crate, the `MirPass` tr The MIR is therefore modified in place (which helps to keep things efficient). A basic example of a MIR pass is [`RemoveStorageMarkers`], which walks -the MIR and removes all storage marks if they won't be emitted during codegen. As you -can see from its source, a MIR pass is defined by first defining a +the MIR and removes all storage marks if they won't be emitted during codegen. +As you can see from its source, a MIR pass is defined by first defining a dummy type, a struct with no fields: ```rust pub struct RemoveStorageMarkers; ``` -for which we implement the `MirPass` trait. We can then insert -this pass into the appropriate list of passes found in a query like +for which we implement the `MirPass` trait. +We can then insert this pass into the appropriate list of passes found in a query like `mir_built`, `optimized_mir`, etc. (If this is an optimization, it should go into the `optimized_mir` list.) Another example of a simple MIR pass is [`CleanupPostBorrowck`][cleanup-pass], which walks -the MIR and removes all statements that are not relevant to code generation. As you can see from -its [source][cleanup-source], it is defined by first defining a dummy type, a struct with no -fields: +the MIR and removes all statements that are not relevant to code generation. +As you can see from +its [source][cleanup-source], it is defined by first defining a dummy type, a struct with no fields: ```rust pub struct CleanupPostBorrowck; @@ -75,30 +76,28 @@ impl<'tcx> MirPass<'tcx> for CleanupPostBorrowck { We [register][pass-register] this pass inside the `mir_drops_elaborated_and_const_checked` query. (If this is an optimization, it should go into the `optimized_mir` list.) -If you are writing a pass, there's a good chance that you are going to -want to use a [MIR visitor]. MIR visitors are a handy way to walk all -the parts of the MIR, either to search for something or to make small -edits. +If you are writing a pass, there's a good chance that you are going to want to use a [MIR visitor]. +MIR visitors are a handy way to walk all +the parts of the MIR, either to search for something or to make small edits. ## Stealing The intermediate queries `mir_const()` and `mir_promoted()` yield up a `&'tcx Steal>`, allocated using `tcx.alloc_steal_mir()`. This indicates that the result may be **stolen** by a subsequent query – this is an -optimization to avoid cloning the MIR. Attempting to use a stolen -result will cause a panic in the compiler. Therefore, it is important -that you do not accidentally read from these intermediate queries without +optimization to avoid cloning the MIR. +Attempting to use a stolen result will cause a panic in the compiler. +Therefore, it is important that you do not accidentally read from these intermediate queries without the consideration of the dependency in the MIR processing pipeline. Because of this stealing mechanism, some care must be taken to ensure that, before the MIR at a particular phase in the processing -pipeline is stolen, anyone who may want to read from it has already -done so. +pipeline is stolen, anyone who may want to read from it has already done so. Concretely, this means that if you have a query `foo(D)` that wants to access the result of `mir_promoted(D)`, you need to have `foo(D)` -calling the `mir_const(D)` query first. This will force it -to execute even though you don't directly require its result. +calling the `mir_const(D)` query first. +This will force it to execute even though you don't directly require its result. > This mechanism is a bit dodgy. There is a discussion of more elegant alternatives in [rust-lang/rust#41710]. @@ -138,14 +137,16 @@ flowchart BT The stadium-shape queries (e.g., `mir_built`) with a deep color are the primary queries in the pipeline, while the rectangle-shape queries (e.g., `mir_const_qualif*`[^star]) with a shallow color -are those subsequent queries that need to read the results from `&'tcx Steal>`. With the +are those subsequent queries that need to read the results from `&'tcx Steal>`. +With the stealing mechanism, the rectangle-shape queries must be performed before any stadium-shape queries, that have an equal or larger height in the dependency tree, ever do. [^part]: The `mir_promoted` query will yield up a tuple `(&'tcx Steal>, &'tcx Steal>>)`, `promoted_mir` will steal part 1 (`&'tcx Steal>>`) and `mir_drops_elaborated_and_const_checked` -will steal part 0 (`&'tcx Steal>`). And their stealing is irrelevant to each other, +will steal part 0 (`&'tcx Steal>`). +And their stealing is irrelevant to each other, i.e., can be performed separately. [^star]: Note that the `*` suffix in the queries represent a set of queries with the same prefix. @@ -154,11 +155,13 @@ For example, `mir_borrowck*` represents `mir_borrowck`, `mir_borrowck_const_arg` ### Example -As an example, consider MIR const qualification. It wants to read the result produced by the -`mir_const` query. However, that result will be **stolen** by the `mir_promoted` query at some -time in the pipeline. Before `mir_promoted` is ever queried, calling the `mir_const_qualif` query +As an example, consider MIR const qualification. +It wants to read the result produced by the `mir_const` query. +However, that result will be **stolen** by the `mir_promoted` query at some time in the pipeline. +Before `mir_promoted` is ever queried, calling the `mir_const_qualif` query will succeed since `mir_const` will produce (if queried the first time) or cache (if queried -multiple times) the `Steal` result and the result is **not** stolen yet. After `mir_promoted` is +multiple times) the `Steal` result and the result is **not** stolen yet. +After `mir_promoted` is queried, the result would be stolen and calling the `mir_const_qualif` query to read the result would cause a panic. From 41ebf0678008db9817662e85a24083ae988770f9 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 12:14:35 +0200 Subject: [PATCH 50/71] improve mir/passes.md --- src/doc/rustc-dev-guide/src/mir/passes.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/passes.md b/src/doc/rustc-dev-guide/src/mir/passes.md index d7ac6e38ab0e2..2ee7a4df5c9f4 100644 --- a/src/doc/rustc-dev-guide/src/mir/passes.md +++ b/src/doc/rustc-dev-guide/src/mir/passes.md @@ -14,7 +14,7 @@ This section describes how those queries and passes work and how you can extend To produce the optimized MIR for a given def-id `D`, `optimized_mir(D)` goes through several suites of passes, each grouped by a query. -Each suite consists of passes which perform linting, analysis, transformation or optimization. +Each suite consists of passes which perform linting, analysis, transformation, or optimization. Each query represent a useful intermediate point where we can access the MIR dialect for type checking or other purposes: @@ -42,8 +42,8 @@ The MIR is therefore modified in place (which helps to keep things efficient). A basic example of a MIR pass is [`RemoveStorageMarkers`], which walks the MIR and removes all storage marks if they won't be emitted during codegen. -As you can see from its source, a MIR pass is defined by first defining a -dummy type, a struct with no fields: +As you can see from its source, +a MIR pass is defined by first defining a dummy type, a struct with no fields: ```rust pub struct RemoveStorageMarkers; @@ -138,8 +138,8 @@ flowchart BT The stadium-shape queries (e.g., `mir_built`) with a deep color are the primary queries in the pipeline, while the rectangle-shape queries (e.g., `mir_const_qualif*`[^star]) with a shallow color are those subsequent queries that need to read the results from `&'tcx Steal>`. -With the -stealing mechanism, the rectangle-shape queries must be performed before any stadium-shape queries, +With the stealing mechanism, +the rectangle-shape queries must be performed before any stadium-shape queries, that have an equal or larger height in the dependency tree, ever do. [^part]: The `mir_promoted` query will yield up a tuple From 4e9b7c58aa349d7306512e2b7c2bd234f0510fc3 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 12:14:49 +0200 Subject: [PATCH 51/71] sembr src/mir/visitor.md --- src/doc/rustc-dev-guide/src/mir/visitor.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/visitor.md b/src/doc/rustc-dev-guide/src/mir/visitor.md index b0facee69a5fa..3d4d0f6d211b3 100644 --- a/src/doc/rustc-dev-guide/src/mir/visitor.md +++ b/src/doc/rustc-dev-guide/src/mir/visitor.md @@ -1,17 +1,16 @@ # MIR visitor The MIR visitor is a convenient tool for traversing the MIR and either -looking for things or making changes to it. The visitor traits are -defined in [the `rustc_middle::mir::visit` module][m-v] – there are two of +looking for things or making changes to it. +The visitor traits are defined in [the `rustc_middle::mir::visit` module][m-v] – there are two of them, generated via a single macro: `Visitor` (which operates on a `&Mir` and gives back shared references) and `MutVisitor` (which operates on a `&mut Mir` and gives back mutable references). [m-v]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/visit/index.html -To implement a visitor, you have to create a type that represents -your visitor. Typically, this type wants to "hang on" to whatever -state you will need while processing MIR: +To implement a visitor, you have to create a type that represents your visitor. +Typically, this type wants to "hang on" to whatever state you will need while processing MIR: ```rust,ignore struct MyVisitor<...> { @@ -33,9 +32,11 @@ impl<'tcx> MutVisitor<'tcx> for MyVisitor { As shown above, within the impl, you can override any of the `visit_foo` methods (e.g., `visit_terminator`) in order to write some -code that will execute whenever a `foo` is found. If you want to -recursively walk the contents of the `foo`, you then invoke the -`super_foo` method. (NB. You never want to override `super_foo`.) +code that will execute whenever a `foo` is found. +If you want to recursively walk the contents of the `foo`, you then invoke the +`super_foo` method. +(NB. +You never want to override `super_foo`.) A very simple example of a visitor can be found in [`LocalFinder`]. By implementing `visit_local` method, this visitor identifies local variables that From a4f7cd66a7042387c9378cb4f4c6a0e1e3dec938 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 12:22:43 +0200 Subject: [PATCH 52/71] use plain english Also, does not play well with sembr tool, since lines are split on the dot --- src/doc/rustc-dev-guide/src/mir/visitor.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/visitor.md b/src/doc/rustc-dev-guide/src/mir/visitor.md index 3d4d0f6d211b3..1b1797b993d4c 100644 --- a/src/doc/rustc-dev-guide/src/mir/visitor.md +++ b/src/doc/rustc-dev-guide/src/mir/visitor.md @@ -35,8 +35,7 @@ As shown above, within the impl, you can override any of the code that will execute whenever a `foo` is found. If you want to recursively walk the contents of the `foo`, you then invoke the `super_foo` method. -(NB. -You never want to override `super_foo`.) +Note that you never want to override `super_foo`. A very simple example of a visitor can be found in [`LocalFinder`]. By implementing `visit_local` method, this visitor identifies local variables that @@ -53,4 +52,3 @@ post-order, and so forth). [t]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/mir/traversal/index.html [traversal]: https://en.wikipedia.org/wiki/Tree_traversal - From ce63e67890620e6e13caace815704555e4f25055 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 12:23:06 +0200 Subject: [PATCH 53/71] sembr src/mir/drop-elaboration.md --- .../src/mir/drop-elaboration.md | 129 +++++++++--------- 1 file changed, 67 insertions(+), 62 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md b/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md index 7ef60f4cca00b..a2cb7f6a1e8eb 100644 --- a/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md +++ b/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md @@ -10,11 +10,12 @@ According to the [reference][reference-drop]: > initialized, only its initialized fields are dropped. When building the MIR, the `Drop` and `DropAndReplace` terminators represent -places where drops may occur. However, in this phase, the presence of these -terminators does not guarantee that a destructor will run. That's because the -target of a drop may be uninitialized (usually because it has been moved from) -before the terminator is reached. In general, we cannot know at compile-time whether a -variable is initialized. +places where drops may occur. +However, in this phase, the presence of these +terminators does not guarantee that a destructor will run. +That's because the target of a drop may be uninitialized (usually because it has been moved from) +before the terminator is reached. +In general, we cannot know at compile-time whether a variable is initialized. ```rust let mut y = vec![]; @@ -28,8 +29,7 @@ let mut y = vec![]; ``` In these cases, we need to keep track of whether a variable is initialized -*dynamically*. The rules are laid out in detail in [RFC 320: Non-zeroing -dynamic drops][RFC 320]. +*dynamically*. The rules are laid out in detail in [RFC 320: Non-zeroing dynamic drops][RFC 320]. ## Drop obligations @@ -46,17 +46,19 @@ From the RFC: When a structural path is moved from (and thus becomes uninitialized), any drop obligations for that path or its descendants (`path.f`, `path.f.g.h`, etc.) are -released. Types with `Drop` implementations do not permit moves from individual +released. +Types with `Drop` implementations do not permit moves from individual fields, so there is no need to track initializedness through them. When a local variable goes out of scope (`Drop`), or when a structural path is overwritten via assignment (`DropAndReplace`), we check for any drop -obligations for that variable or path. Unless that obligation has been +obligations for that variable or path. + Unless that obligation has been released by this point, its associated `Drop` implementation will be called. -For `enum` types, only fields corresponding to the "active" variant need to be -dropped. When processing drop obligations for such types, we first check the -discriminant to determine the active variant. All drop obligations for variants -besides the active one are ignored. +For `enum` types, only fields corresponding to the "active" variant need to be dropped. +When processing drop obligations for such types, we first check the +discriminant to determine the active variant. +All drop obligations for variants besides the active one are ignored. Here are a few interesting types to help illustrate these rules: @@ -80,89 +82,90 @@ enum MaybeDrop { ## Drop elaboration One valid model for these rules is to keep a boolean flag (a "drop flag") for -every structural path that is used at any point in the function. This flag is -set when its path is initialized and is cleared when the path is moved from. +every structural path that is used at any point in the function. +This flag is set when its path is initialized and is cleared when the path is moved from. When a `Drop` occurs, we check the flags for every obligation associated with -the target of the `Drop` and call the associated `Drop` impl for those that are -still applicable. +the target of the `Drop` and call the associated `Drop` impl for those that are still applicable. This process—transforming the newly built MIR with its imprecise `Drop` and -`DropAndReplace` terminators into one with drop flags—is known as drop -elaboration. When a MIR statement causes a variable to become initialized (or -uninitialized), drop elaboration inserts code that sets (or clears) the drop -flag for that variable. It wraps `Drop` terminators in conditionals that check -the newly inserted drop flags. +`DropAndReplace` terminators into one with drop flags—is known as drop elaboration. +When a MIR statement causes a variable to become initialized (or +uninitialized), drop elaboration inserts code that sets (or clears) the drop flag for that variable. +It wraps `Drop` terminators in conditionals that check the newly inserted drop flags. Drop elaboration also splits `DropAndReplace` terminators into a `Drop` of the -target and a write of the newly dropped place. This is somewhat unrelated to what -we've discussed above. +target and a write of the newly dropped place. +This is somewhat unrelated to what we've discussed above. Once this is complete, `Drop` terminators in the MIR correspond to a call to -the "drop glue" or "drop shim" for the type of the dropped place. The drop -glue for a type calls the `Drop` impl for that type (if one exists), and then +the "drop glue" or "drop shim" for the type of the dropped place. +The drop glue for a type calls the `Drop` impl for that type (if one exists), and then recursively calls the drop glue for all fields of that type. ## Drop elaboration in `rustc` -The approach described above is more expensive than necessary. One can imagine -a few optimizations: +The approach described above is more expensive than necessary. +One can imagine a few optimizations: -- Only paths that are the target of a `Drop` (or have the target as a prefix) - need drop flags. -- Some variables are known to be initialized (or uninitialized) when they are - dropped. These do not need drop flags. +- Only paths that are the target of a `Drop` (or have the target as a prefix) need drop flags. +- Some variables are known to be initialized (or uninitialized) when they are dropped. + These do not need drop flags. - If a set of paths are only dropped or moved from via a shared prefix, those paths can share a single drop flag. A subset of these are implemented in `rustc`. -In the compiler, drop elaboration is split across several modules. The pass -itself is defined [here][drops-transform], but the [main logic][drops] is +In the compiler, drop elaboration is split across several modules. +The pass itself is defined [here][drops-transform], but the [main logic][drops] is defined elsewhere since it is also used to build [drop shims][drops-shim]. -Drop elaboration designates each `Drop` in the newly built MIR as one of four -kinds: +Drop elaboration designates each `Drop` in the newly built MIR as one of four kinds: - `Static`, the target is always initialized. - `Dead`, the target is always **un**initialized. -- `Conditional`, the target is either wholly initialized or wholly - uninitialized. It is not partly initialized. +- `Conditional`, the target is either wholly initialized or wholly uninitialized. + It is not partly initialized. - `Open`, the target may be partly initialized. For this, it uses a pair of dataflow analyses, `MaybeInitializedPlaces` and -`MaybeUninitializedPlaces`. If a place is in one but not the other, then the +`MaybeUninitializedPlaces`. +If a place is in one but not the other, then the initializedness of the target is known at compile-time (`Dead` or `Static`). -In this case, drop elaboration does not add a flag for the target. It simply -removes (`Dead`) or preserves (`Static`) the `Drop` terminator. +In this case, drop elaboration does not add a flag for the target. +It simply removes (`Dead`) or preserves (`Static`) the `Drop` terminator. For `Conditional` drops, we know that the initializedness of the variable as a -whole is the same as the initializedness of its fields. Therefore, once we -generate a drop flag for the target of that drop, it's safe to call the drop +whole is the same as the initializedness of its fields. +Therefore, once we generate a drop flag for the target of that drop, it's safe to call the drop glue for that target. ### `Open` drops `Open` drops are the most complex, since we need to break down a single `Drop` terminator into several different ones, one for each field of the target whose -type has drop glue (`Ty::needs_drop`). We cannot call the drop glue for the +type has drop glue (`Ty::needs_drop`). +We cannot call the drop glue for the target itself because that requires all fields of the target to be initialized. Remember, variables whose type has a custom `Drop` impl do not allow `Open` drops because their fields cannot be moved from. This is accomplished by recursively categorizing each field as `Dead`, -`Static`, `Conditional` or `Open`. Fields whose type does not have drop glue -are automatically `Dead` and need not be considered during the recursion. When -we reach a field whose kind is not `Open`, we handle it as we did above. If the -field is also `Open`, the recursion continues. - -It's worth noting how we handle `Open` drops of enums. Inside drop elaboration, +`Static`, `Conditional` or `Open`. +Fields whose type does not have drop glue +are automatically `Dead` and need not be considered during the recursion. +When we reach a field whose kind is not `Open`, we handle it as we did above. +If the field is also `Open`, the recursion continues. + +It's worth noting how we handle `Open` drops of enums. +Inside drop elaboration, each variant of the enum is treated like a field, with the invariant that only -one of those "variant fields" can be initialized at any given time. In the -general case, we do not know which variant is the active one, so we will have +one of those "variant fields" can be initialized at any given time. +In the general case, we do not know which variant is the active one, so we will have to call the drop glue for the enum (which checks the discriminant) or check the -discriminant ourselves as part of an elaborated `Open` drop. However, in -certain cases (within a `match` arm, for example) we do know which variant of -an enum is active. This information is encoded in the `MaybeInitializedPlaces` +discriminant ourselves as part of an elaborated `Open` drop. +However, in certain cases (within a `match` arm, for example) we do know which variant of +an enum is active. +This information is encoded in the `MaybeInitializedPlaces` and `MaybeUninitializedPlaces` dataflow analyses by marking all places corresponding to inactive variants as uninitialized. @@ -173,16 +176,18 @@ TODO: Discuss drop elaboration and unwinding. ## Aside: drop elaboration and const-eval In Rust, functions that are eligible for evaluation at compile-time must be -marked explicitly using the `const` keyword. This includes implementations of -the `Drop` trait, which may or may not be `const`. Code that is eligible for -compile-time evaluation may only call `const` functions, so any calls to +marked explicitly using the `const` keyword. +This includes implementations of the `Drop` trait, which may or may not be `const`. +Code that is eligible for compile-time evaluation may only call `const` functions, so any calls to non-const `Drop` implementations in such code must be forbidden. -A call to a `Drop` impl is encoded as a `Drop` terminator in the MIR. However, +A call to a `Drop` impl is encoded as a `Drop` terminator in the MIR. +However, as we discussed above, a `Drop` terminator in newly built MIR does not -necessarily result in a call to `Drop::drop`. The drop target may be -uninitialized at that point. This means that checking for non-const `Drop`s on -the newly built MIR can result in spurious errors. Instead, we wait until after +necessarily result in a call to `Drop::drop`. +The drop target may be uninitialized at that point. +This means that checking for non-const `Drop`s on the newly built MIR can result in spurious errors. +Instead, we wait until after drop elaboration runs, which eliminates `Dead` drops (ones where the target is known to be uninitialized) to run these checks. From 1710ce149c3f05c572aabccc14828e1d7a57a7fa Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 12:27:14 +0200 Subject: [PATCH 54/71] clarity --- src/doc/rustc-dev-guide/src/mir/drop-elaboration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md b/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md index a2cb7f6a1e8eb..a0491f87326ac 100644 --- a/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md +++ b/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md @@ -2,7 +2,7 @@ ## Dynamic drops -According to the [reference][reference-drop]: +According to [Rust Reference][reference-drop]: > When an initialized variable or temporary goes out of scope, its destructor > is run, or it is dropped. Assignment also runs the destructor of its From 60d5c32e36524f7f756662c3be6f2ce5e19f305c Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Tue, 14 Jul 2026 13:38:51 +0200 Subject: [PATCH 55/71] unstable book: Document `diagnostic_on_unknown` --- .../diagnostic-on-unknown.md | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 src/doc/unstable-book/src/language-features/diagnostic-on-unknown.md diff --git a/src/doc/unstable-book/src/language-features/diagnostic-on-unknown.md b/src/doc/unstable-book/src/language-features/diagnostic-on-unknown.md new file mode 100644 index 0000000000000..dee89a6916353 --- /dev/null +++ b/src/doc/unstable-book/src/language-features/diagnostic-on-unknown.md @@ -0,0 +1,196 @@ +# `diagnostic_on_unknown` + +The tracking issue for this feature is: [#152900](https://github.com/rust-lang/rust/issues/152900) + +------------------------ + +The `diagnostic_on_unknown` feature allows use of the `#[diagnostic::on_unknown]` attribute. It should be +placed on use and module declarations as well as the crate root, though it is not an error to be located in other +positions. This attribute is a hint to the compiler to supplement the error message when the +annotated declaration is involved in a name resolution error. + +Format parameters with the given named parameter will be replaced with the following text: + +- `{Unresolved}` — The `SimplePathSegment` of the import path that could not be resolved. +- `{This}` — The name of the annotated item. On `use` statements this is identical to `{Unresolved}`. + +The original error message will not be suppressed but is emitted as a note instead. + +### On use declarations + +```rust,edition2018,compile_fail,E0432 +#![feature(diagnostic_on_unknown)] + +#[diagnostic::on_unknown( + message = "`{Unresolved}` doesn't exist", + label = "you did something silly here" +)] +use doesnt_exist; +``` +This will result in the following error: +```text +error[E0432]: `doesnt_exist` doesn't exist + --> src/lib.rs:7:5 + | +7 | use doesnt_exist; + | ^^^^^^^^^^^^ you did something silly here + | + = note: unresolved import `doesnt_exist` + +For more information about this error, try `rustc --explain E0432`. +``` + +### On module declarations + +```rust,edition2018,compile_fail,E0432 +#![feature(diagnostic_on_unknown)] + +#[diagnostic::on_unknown( + message = "module `{This}` is empty, there is no `{Unresolved}` here", + label = "can't import something from an empty module" +)] +mod empty {} + +use empty::what; +``` +This will result in the following error: + +```text +error[E0432]: module `empty` is empty, there is no `what` here + --> src/lib.rs:9:5 + | +9 | use empty::what; + | ^^^^^^^---- + | | + | can't import something from an empty module + | + = note: unresolved import `empty::what` +``` + +### On the crate root + +This additionally requires the `#![feature(custom_inner_attributes)]` feature: + +```rust,edition2018,compile_fail,E0432 +#![feature(diagnostic_on_unknown)] +#![feature(custom_inner_attributes)] +#![diagnostic::on_unknown(message = "Say `{Unresolved}` again!")] + +use self::what; +``` +### Example + +Consider the following pair of macros, one which creates a hidden module with a constant and another that reads it. + +```rust +#![feature(macro_metavar_expr, macro_attr)] + +macro_rules! instrument { + attr() { $vis:vis fn $fn_name:ident ($($arg_name:ident : $arg_ty:ty),*) $(-> $ret:ty)? $body:block } => { + $vis fn $fn_name ($($arg_name:$arg_ty),*) $(-> $ret)? $body + #[doc(hidden)] + $vis mod $fn_name { + pub const LEN: usize = ${ count($arg_name) }; + } + } +} + +macro_rules! count_args { + ($name:ident) => {{ + $name::LEN + }} +} + +#[instrument] +fn add(a: u8, b: u8) -> u8 { + a + b +} + +fn main() { + let n = count_args!(add); + println!("`add` has {n} arguments"); +} +``` + +If the `#[instrument]` macro is omitted it will emit this confusing error: +```text +error[E0433]: cannot find module or crate `add` in this scope + --> src/main.rs:25:25 + | +25 | let n = count_args!(add); + | ^^^ function `add` is not a crate or module +``` + +`#[diagnostic::on_unknown]` can be used to customize this error message: + +```rust,edition2018,compile_fail,E0432 +#![feature(diagnostic_on_unknown)] +# #![feature(macro_metavar_expr, macro_attr)] +# +# #[allow(unused_macros)] +# macro_rules! instrument { +# attr() { $vis:vis fn $fn_name:ident ($($arg_name:ident : $arg_ty:ty),*) $(-> $ret:ty)? $body:block } => { +# $vis fn $fn_name ($($arg_name:$arg_ty),*) $(-> $ret)? $body +# #[doc(hidden)] +# $vis mod $fn_name { +# pub const LEN: usize = ${ count($arg_name) }; +# } +# } +# } + +macro_rules! count_args { + ($name:ident) => {{ + #[diagnostic::on_unknown( + message = "cannot count arguments of `{Unresolved}`", + label = "`{Unresolved}` is not a function decorated \ + with the `#[instrument]` macro" + )] + use $name::LEN as length; + length + }} +} + +// #[instrument] +fn add(a: u8, b: u8) -> u8 { + a + b +} + +fn main() { + let n = count_args!(add); + println!("`add` has {n} arguments"); +} +``` +This produces: +```text +error[E0432]: cannot count arguments of `add` + --> src/main.rs:33:25 + | +33 | let n = count_args!(add); + | ^^^ `add` is not a function decorated with + | the `#[instrument]` macro + | + = note: unresolved import `add` +``` + +### Edition differences + +In the 2015 edition, use paths are relative to the crate root. For example, `use empty` will be resolved relative to the crate root and resolution will not encounter the `mod empty {}` declaration. +```rust,edition2015,compile_fail,E0432 +#![feature(diagnostic_on_unknown)] + +mod foo { + #[diagnostic::on_unknown(message = "oh oh")] + mod empty {} + + use empty::what; +} +``` + +```text +error[E0432]: unresolved import `empty` + --> src/main.rs:9:8 + | +9 | use empty::what; + | ^^^^^ + | +``` From 4d9f95421a14d140e0a95795a2da68a4e22a3d06 Mon Sep 17 00:00:00 2001 From: reidliu Date: Tue, 14 Jul 2026 19:52:29 +0800 Subject: [PATCH 56/71] Remove obsolete verbose flag from deref/ref suggestions --- .../src/fn_ctxt/suggestions.rs | 42 +++++-------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index a9886e2419ee7..3aec9d4dae5ca 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -359,14 +359,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let methods = self.get_conversion_methods_for_diagnostic(expr.span, expected, found, expr.hir_id); - if let Some((suggestion, msg, applicability, verbose, annotation)) = + if let Some((suggestion, msg, applicability, annotation)) = self.suggest_deref_or_ref(expr, found, expected) { - if verbose { - err.multipart_suggestion(msg, suggestion, applicability); - } else { - err.multipart_suggestion(msg, suggestion, applicability); - } + err.multipart_suggestion(msg, suggestion, applicability); if annotation { let suggest_annotation = match expr.peel_drop_temps().kind { hir::ExprKind::AddrOf(hir::BorrowKind::Ref, mutbl, _) => mutbl.ref_prefix_str(), @@ -2918,7 +2914,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Vec<(Span, String)>, String, Applicability, - bool, /* verbose */ bool, /* suggest `&` or `&mut` type annotation */ )> { let sess = self.sess(); @@ -2949,7 +2944,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { vec![(sp.with_hi(pos), String::new())], "consider removing the leading `b`".to_string(), Applicability::MachineApplicable, - true, false, )); } @@ -2963,7 +2957,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { vec![(sp.shrink_to_lo(), "b".to_string())], "consider adding a leading `b`".to_string(), Applicability::MachineApplicable, - true, false, )); } @@ -3020,7 +3013,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { vec![(sugg_sp, String::new())], "consider removing deref here".to_string(), Applicability::MachineApplicable, - true, false, )); } @@ -3035,7 +3027,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // If the block does not have a final expression, it will return () and we do not make a suggestion to borrow that. let ExprKind::Block(then, _) = then.kind else { return None }; let Some(then) = then.expr else { return None }; - let (mut suggs, help, app, verbose, mutref) = + let (mut suggs, help, app, mutref) = self.suggest_deref_or_ref(then, checked_ty, expected)?; // If there is no `else`, the return type of this `if` will be (), so suggesting to change the `then` block is useless @@ -3047,7 +3039,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.suggest_deref_or_ref(els_expr, checked_ty, expected)?; suggs.extend(else_suggs); - return Some((suggs, help, app, verbose, mutref)); + return Some((suggs, help, app, mutref)); } if let Some((sugg, msg)) = self.can_use_as_ref(expr) { @@ -3055,7 +3047,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { sugg, msg.to_string(), Applicability::MachineApplicable, - true, false, )); } @@ -3076,15 +3067,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let make_sugg = |expr: &Expr<'_>, span: Span, sugg: &str| { if expr_needs_parens(expr) { - ( - vec![ - (span.shrink_to_lo(), format!("{prefix}{sugg}(")), - (span.shrink_to_hi(), ")".to_string()), - ], - false, - ) + vec![ + (span.shrink_to_lo(), format!("{prefix}{sugg}(")), + (span.shrink_to_hi(), ")".to_string()), + ] } else { - (vec![(span.shrink_to_lo(), format!("{prefix}{sugg}"))], true) + vec![(span.shrink_to_lo(), format!("{prefix}{sugg}"))] } }; @@ -3095,24 +3083,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { }) = self.tcx.parent_hir_node(expr.hir_id) && let &ty::Ref(..) = self.check_expr(lhs).kind() { - let (sugg, verbose) = make_sugg(lhs, lhs.span, "*"); + let sugg = make_sugg(lhs, lhs.span, "*"); return Some(( sugg, "consider dereferencing the borrow".to_string(), Applicability::MachineApplicable, - verbose, false, )); } let sugg = mutability.ref_prefix_str(); - let (sugg, verbose) = make_sugg(expr, sp, sugg); + let sugg = make_sugg(expr, sp, sugg); return Some(( sugg, format!("consider {}borrowing here", mutability.mutably_str()), Applicability::MachineApplicable, - verbose, false, )); } @@ -3131,7 +3117,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "consider removing the borrow".to_string(), Applicability::MachineApplicable, true, - true, )) }; @@ -3194,7 +3179,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { vec![(span, src)], "consider dereferencing".to_string(), applicability, - true, false, )); } @@ -3231,7 +3215,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } else { Applicability::MachineApplicable }, - true, false, )); } @@ -3267,7 +3250,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { "consider removing the Box".to_string(), Applicability::MachineApplicable, false, - false, )); } "unboxing the value" @@ -3316,7 +3298,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ], message, Applicability::MachineApplicable, - true, false, )); } @@ -3325,7 +3306,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { vec![(span, suggestion)], message, Applicability::MachineApplicable, - true, false, )); } From 62c7143a8b6ab8818bd559e835d18f44ce3dde27 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Mon, 13 Jul 2026 23:05:50 +0200 Subject: [PATCH 57/71] Don't run doctest because it doesn't compile on stage1 --- compiler/rustc_lint_defs/src/builtin.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 99216026b5a30..609b8e91926d2 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -5586,12 +5586,13 @@ declare_lint! { /// /// ### Example /// - /// ```rust,no_run + #[cfg_attr(bootstrap, doc = "```rust,compile_fail")] + #[cfg_attr(not(bootstrap), doc = "```rust,no_run")] /// fn main() { /// let x = panic!(); /// x.clone(); /// } - /// ``` + #[doc = "```"] /// /// {{produces}} /// From 48a980d9a35f07b73f2013bd82740b4280db493a Mon Sep 17 00:00:00 2001 From: cezarbbb Date: Mon, 22 Jun 2026 09:45:25 +0800 Subject: [PATCH 58/71] Store DefId instead of EiiDecl in EiiImplResolution::Known --- compiler/rustc_ast/src/ast.rs | 2 +- compiler/rustc_ast_lowering/src/item.rs | 10 ++----- compiler/rustc_builtin_macros/src/eii.rs | 15 ++++------ .../rustc_codegen_ssa/src/codegen_attrs.rs | 8 +++-- .../rustc_hir/src/attrs/data_structures.rs | 4 +-- .../rustc_hir_analysis/src/check/wfcheck.rs | 4 +-- compiler/rustc_metadata/src/eii.rs | 28 ++++++++++++++---- compiler/rustc_passes/src/check_attr.rs | 29 +++++++++++++++---- compiler/rustc_resolve/src/late.rs | 7 +---- 9 files changed, 67 insertions(+), 40 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index bd5ce5d18b839..9b881688a6ba9 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3924,7 +3924,7 @@ pub struct EiiImpl { /// /// This field is that shortcut: we prefill the extern target to skip a name resolution step, /// making sure it never fails. It'd be awful UX if we fail name resolution in code invisible to the user. - pub known_eii_macro_resolution: Option, + pub known_eii_macro_resolution: Option, pub impl_safety: Safety, pub span: Span, pub inner_span: Span, diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index 8ebecb222b940..8d4a6f104765f 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -141,13 +141,9 @@ impl<'hir> LoweringContext<'_, 'hir> { }: &EiiImpl, ) -> hir::attrs::EiiImpl { let resolution = if let Some(target) = known_eii_macro_resolution - && let Some(decl) = self.lower_eii_decl( - *node_id, - // the expect is ok here since we always generate this path in the eii macro. - eii_macro_path.segments.last().expect("at least one segment").ident, - target, - ) { - EiiImplResolution::Known(decl) + && let Some(foreign_item_did) = self.lower_path_simple_eii(*node_id, target) + { + EiiImplResolution::Known(foreign_item_did) } else if let Some(macro_did) = self.lower_path_simple_eii(*node_id, eii_macro_path) { EiiImplResolution::Macro(macro_did) } else { diff --git a/compiler/rustc_builtin_macros/src/eii.rs b/compiler/rustc_builtin_macros/src/eii.rs index 68d5278c85ef1..66879d5daf7c5 100644 --- a/compiler/rustc_builtin_macros/src/eii.rs +++ b/compiler/rustc_builtin_macros/src/eii.rs @@ -302,15 +302,12 @@ fn generate_default_impl( }, span: eii_attr_span, is_default: true, - known_eii_macro_resolution: Some(ast::EiiDecl { - foreign_item: ecx.path( - foreign_item_name.span, - // prefix self to explicitly escape the const block generated below - // NOTE: this is why EIIs can't be used on statements - vec![Ident::from_str_and_span("self", foreign_item_name.span), foreign_item_name], - ), - impl_unsafe, - }), + known_eii_macro_resolution: Some(ecx.path( + foreign_item_name.span, + // prefix self to explicitly escape the const block generated below + // NOTE: this is why EIIs can't be used on statements + vec![Ident::from_str_and_span("self", foreign_item_name.span), foreign_item_name], + )), }; let mut item_kind = item_kind.clone(); diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 46bb1182c298c..25d82ee0f3c2f 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -7,6 +7,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; use rustc_hir::{self as hir, Attribute, find_attr}; use rustc_macros::Diagnostic; +use rustc_middle::bug; use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, InstrumentFnAttr, PatchableFunctionEntry, SanitizerFnAttrs, }; @@ -238,7 +239,7 @@ fn process_builtin_attrs( }; extern_item } - EiiImplResolution::Known(decl) => decl.foreign_item, + EiiImplResolution::Known(def_id) => def_id, EiiImplResolution::Error(_eg) => continue, }; @@ -253,7 +254,10 @@ fn process_builtin_attrs( // iterate over all implementations *in the current crate* // (this is ok since we generate codegen fn attrs in the local crate) // if any of them is *not default* then don't emit the alias. - && tcx.externally_implementable_items(LOCAL_CRATE).get(&foreign_item).expect("at least one").1.iter().any(|(_, imp)| !imp.is_default) + && { + let (_, impls) = tcx.externally_implementable_items(LOCAL_CRATE).get(&foreign_item).unwrap_or_else(|| bug!("EII impl should have an entry")); + impls.iter().any(|(_, imp)| !imp.is_default) + } { continue; } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 17d00863d99d5..f88dcb53b0ad7 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -31,8 +31,8 @@ pub enum EiiImplResolution { /// what foreign item its associated with. Macro(DefId), /// Sometimes though, we already know statically and can skip some name resolution. - /// Stored together with the eii's name for diagnostics. - Known(EiiDecl), + /// DefId of the extern item that the EII implementation implements. + Known(DefId), /// For when resolution failed, but we want to continue compilation Error(ErrorGuaranteed), } diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index f2a6c2747f380..45db2ffad4747 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -1232,7 +1232,7 @@ fn check_eiis_fn(tcx: TyCtxt<'_>, def_id: LocalDefId) { continue; } } - EiiImplResolution::Known(decl) => (decl.foreign_item, decl.name.name), + EiiImplResolution::Known(def_id) => (*def_id, tcx.item_name(*def_id)), EiiImplResolution::Error(_eg) => continue, }; @@ -1259,7 +1259,7 @@ fn check_eiis_static<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId, ty: Ty<'tcx>) continue; } } - EiiImplResolution::Known(decl) => (decl.foreign_item, decl.name.name), + EiiImplResolution::Known(def_id) => (*def_id, tcx.item_name(*def_id)), EiiImplResolution::Error(_eg) => continue, }; diff --git a/compiler/rustc_metadata/src/eii.rs b/compiler/rustc_metadata/src/eii.rs index 11ea4cc492921..4328e8de901d8 100644 --- a/compiler/rustc_metadata/src/eii.rs +++ b/compiler/rustc_metadata/src/eii.rs @@ -2,6 +2,7 @@ use rustc_data_structures::fx::FxIndexMap; use rustc_hir::attrs::{EiiDecl, EiiImpl, EiiImplResolution}; use rustc_hir::def_id::DefId; use rustc_hir::find_attr; +use rustc_middle::bug; use rustc_middle::query::LocalCrate; use rustc_middle::ty::TyCtxt; @@ -23,10 +24,19 @@ pub(crate) type EiiMap = FxIndexMap< pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap { let mut eiis = EiiMap::default(); + // Needed because Known only stores a DefId (not the full EiiDecl), + // and we can't call the externally_implementable_items query (cycle). + let decls_by_foreign_item: FxIndexMap = tcx + .hir_crate_items(()) + .eiis() + .filter_map(|id| find_attr!(tcx, id, EiiDeclaration(d) => *d)) + .map(|decl| (decl.foreign_item, decl)) + .collect(); + // iterate over all items in the current crate for id in tcx.hir_crate_items(()).eiis() { for i in find_attr!(tcx, id, EiiImpls(e) => e).into_flat_iter() { - let decl = match i.resolution { + let (foreign_item, decl) = match i.resolution { EiiImplResolution::Macro(macro_defid) => { // find the decl for this one if it wasn't in yet (maybe it's from the local crate? not very useful but not illegal) let Some(decl) = find_attr!(tcx, macro_defid, EiiDeclaration(d) => *d) else { @@ -35,14 +45,22 @@ pub(crate) fn collect<'tcx>(tcx: TyCtxt<'tcx>, LocalCrate: LocalCrate) -> EiiMap .span_delayed_bug(i.span, "resolved to something that's not an EII"); continue; }; - decl + (decl.foreign_item, decl) + } + // Recover the EiiDecl from the local lookup map. + EiiImplResolution::Known(foreign_item_did) => { + let decl = decls_by_foreign_item.get(&foreign_item_did).unwrap_or_else(|| { + bug!( + "EII impl has Known resolution but can't find EiiDeclaration for {:?}", + foreign_item_did + ) + }); + (foreign_item_did, *decl) } - EiiImplResolution::Known(decl) => decl, EiiImplResolution::Error(_eg) => continue, }; - // FIXME(eii) remove extern target from encoded decl - eiis.entry(decl.foreign_item) + eiis.entry(foreign_item) .or_insert_with(|| (decl, Default::default())) .1 .insert(id.into(), *i); diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 7ae6005e9bc98..88fbbf583064f 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -486,13 +486,30 @@ impl<'tcx> CheckAttrVisitor<'tcx> { } } - if let EiiImplResolution::Macro(eii_macro) = resolution - && find_attr!(self.tcx, *eii_macro, EiiDeclaration(EiiDecl { impl_unsafe, .. }) if *impl_unsafe) - && !impl_marked_unsafe - { + let needs_unsafe = match resolution { + EiiImplResolution::Macro(eii_macro) => { + find_attr!(self.tcx, *eii_macro, EiiDeclaration(EiiDecl { impl_unsafe, .. }) if *impl_unsafe) + } + EiiImplResolution::Known(foreign_item_did) => { + let foreign_item_did = *foreign_item_did; + self.tcx + .externally_implementable_items(foreign_item_did.krate) + .get(&foreign_item_did) + .map(|(decl, _)| decl.impl_unsafe) + .unwrap_or(false) + } + EiiImplResolution::Error(_) => false, + }; + + if needs_unsafe && !impl_marked_unsafe { + let name = match resolution { + EiiImplResolution::Macro(eii_macro) => self.tcx.item_name(*eii_macro), + EiiImplResolution::Known(def_id) => self.tcx.item_name(*def_id), + EiiImplResolution::Error(_) => unreachable!(), + }; self.dcx().emit_err(diagnostics::EiiImplRequiresUnsafe { span: *span, - name: self.tcx.item_name(*eii_macro), + name, suggestion: diagnostics::EiiImplRequiresUnsafeSuggestion { left: inner_span.shrink_to_lo(), right: inner_span.shrink_to_hi(), @@ -755,7 +772,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { for i in impls { let name = match i.resolution { EiiImplResolution::Macro(def_id) => self.tcx.item_name(def_id), - EiiImplResolution::Known(decl) => decl.name.name, + EiiImplResolution::Known(def_id) => self.tcx.item_name(def_id), EiiImplResolution::Error(_eg) => continue, }; self.dcx().emit_err(diagnostics::EiiWithTrackCaller { diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index c94f2f3ffb85d..6f206aadf0ad2 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -5609,12 +5609,7 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { // See docs on the `known_eii_macro_resolution` field: // if we already know the resolution statically, don't bother resolving it. if let Some(target) = known_eii_macro_resolution { - self.smart_resolve_path( - *node_id, - &None, - &target.foreign_item, - PathSource::ExternItemImpl, - ); + self.smart_resolve_path(*node_id, &None, target, PathSource::ExternItemImpl); } else { self.smart_resolve_path(*node_id, &None, &eii_macro_path, PathSource::Macro); } From 52ab8f096694398cfaf0eb8d4d7f73d7e7c04db8 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 12:29:06 +0200 Subject: [PATCH 59/71] *. confused sembr tool --- src/doc/rustc-dev-guide/src/mir/drop-elaboration.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md b/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md index a0491f87326ac..991a10e1c4853 100644 --- a/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md +++ b/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md @@ -28,8 +28,8 @@ let mut y = vec![]; } // `x` goes out of scope here. Should it be dropped? ``` -In these cases, we need to keep track of whether a variable is initialized -*dynamically*. The rules are laid out in detail in [RFC 320: Non-zeroing dynamic drops][RFC 320]. +In these cases, we need to keep track of whether a variable is initialized _dynamically_. +The rules are laid out in detail in [RFC 320: Non-zeroing dynamic drops][RFC 320]. ## Drop obligations From a1e73613e620e9689870af0750bda51d131562bb Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 15:24:27 +0200 Subject: [PATCH 60/71] nit --- src/doc/rustc-dev-guide/src/mir/drop-elaboration.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md b/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md index 991a10e1c4853..7445ea2d980c1 100644 --- a/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md +++ b/src/doc/rustc-dev-guide/src/mir/drop-elaboration.md @@ -150,7 +150,7 @@ Remember, variables whose type has a custom `Drop` impl do not allow `Open` drops because their fields cannot be moved from. This is accomplished by recursively categorizing each field as `Dead`, -`Static`, `Conditional` or `Open`. +`Static`, `Conditional`, or `Open`. Fields whose type does not have drop glue are automatically `Dead` and need not be considered during the recursion. When we reach a field whose kind is not `Open`, we handle it as we did above. From ac3ddd6befcbc9d787527594517d7d2c96eb29fd Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 15:24:50 +0200 Subject: [PATCH 61/71] sembr src/coroutine-closures.md --- .../rustc-dev-guide/src/coroutine-closures.md | 101 ++++++++++++------ 1 file changed, 69 insertions(+), 32 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/coroutine-closures.md b/src/doc/rustc-dev-guide/src/coroutine-closures.md index 45d6a24a65998..00e8a5f80545e 100644 --- a/src/doc/rustc-dev-guide/src/coroutine-closures.md +++ b/src/doc/rustc-dev-guide/src/coroutine-closures.md @@ -1,6 +1,7 @@ # Async closures/"coroutine-closures" -Please read [RFC 3668](https://rust-lang.github.io/rfcs/3668-async-closures.html) to understand the general motivation of the feature. This is a very technical and somewhat "vertical" chapter; ideally we'd split this and sprinkle it across all the relevant chapters, but for the purposes of understanding async closures *holistically*, I've put this together all here in one chapter. +Please read [RFC 3668](https://rust-lang.github.io/rfcs/3668-async-closures.html) to understand the general motivation of the feature. +This is a very technical and somewhat "vertical" chapter; ideally we'd split this and sprinkle it across all the relevant chapters, but for the purposes of understanding async closures *holistically*, I've put this together all here in one chapter. ## Coroutine-closures -- a technical deep dive @@ -8,7 +9,8 @@ Coroutine-closures are a generalization of async closures, being special syntax For now, the only usable kind of coroutine-closure is the async closure, and supporting async closures is the extent of this PR. We may eventually support `gen || {}`, etc., and most of the problems and curiosities described in this document apply to all coroutine-closures in general. -As a consequence of the code being somewhat general, this document may flip between calling them "async closures" and "coroutine-closures". The future that is returned by the async closure will generally be called the "coroutine" or the "child coroutine". +As a consequence of the code being somewhat general, this document may flip between calling them "async closures" and "coroutine-closures". +The future that is returned by the async closure will generally be called the "coroutine" or the "child coroutine". ### HIR @@ -20,7 +22,10 @@ The closure-kind of the async block is `ClosureKind::Closure(CoroutineKind::Desu [^k2]: -Like `async fn`, when lowering an async closure's body, we need to unconditionally move all of the closures arguments into the body so they are captured. This is handled by `lower_coroutine_body_with_moved_arguments`[^l1]. The only notable quirk with this function is that the async block we end up generating as a capture kind of `CaptureBy::ByRef`[^l2]. We later force all of the *closure args* to be captured by-value[^l3], but we don't want the *whole* async block to act as if it were an `async move`, since that would defeat the purpose of the self-borrowing of an async closure. +Like `async fn`, when lowering an async closure's body, we need to unconditionally move all of the closures arguments into the body so they are captured. +This is handled by `lower_coroutine_body_with_moved_arguments`[^l1]. +The only notable quirk with this function is that the async block we end up generating as a capture kind of `CaptureBy::ByRef`[^l2]. +We later force all of the *closure args* to be captured by-value[^l3], but we don't want the *whole* async block to act as if it were an `async move`, since that would defeat the purpose of the self-borrowing of an async closure. [^l1]: @@ -36,7 +41,8 @@ The main thing that this PR introduces is a new `TyKind` called `CoroutineClosur [^t1]: -We introduce a new `TyKind` instead of generalizing the existing `TyKind::Closure` due to major representational differences in the type. The major differences between `CoroutineClosure`s can be explored by first inspecting the `CoroutineClosureArgsParts`, which is the "unpacked" representation of the coroutine-closure's generics. +We introduce a new `TyKind` instead of generalizing the existing `TyKind::Closure` due to major representational differences in the type. +The major differences between `CoroutineClosure`s can be explored by first inspecting the `CoroutineClosureArgsParts`, which is the "unpacked" representation of the coroutine-closure's generics. #### Similarities to closures @@ -44,11 +50,13 @@ Like a closure, we have `parent_args`, a `closure_kind_ty`, and a `tupled_upvars #### The signature -A traditional closure has a `fn_sig_as_fn_ptr_ty` which it uses to represent the signature of the closure. In contrast, we store the signature of a coroutine closure in a somewhat "exploded" way, since coroutine-closures have *two* signatures depending on what `AsyncFn*` trait you call it with (see below sections). +A traditional closure has a `fn_sig_as_fn_ptr_ty` which it uses to represent the signature of the closure. +In contrast, we store the signature of a coroutine closure in a somewhat "exploded" way, since coroutine-closures have *two* signatures depending on what `AsyncFn*` trait you call it with (see below sections). Conceptually, the coroutine-closure may be thought as containing several different signature types depending on whether it is being called by-ref or by-move. -To conveniently recreate both of these signatures, the `signature_parts_ty` stores all of the relevant parts of the coroutine returned by this coroutine-closure. This signature parts type will have the general shape of `fn(tupled_inputs, resume_ty) -> (return_ty, yield_ty)`, where `resume_ty`, `return_ty`, and `yield_ty` are the respective types for the *coroutine* returned by the coroutine-closure[^c1]. +To conveniently recreate both of these signatures, the `signature_parts_ty` stores all of the relevant parts of the coroutine returned by this coroutine-closure. +This signature parts type will have the general shape of `fn(tupled_inputs, resume_ty) -> (return_ty, yield_ty)`, where `resume_ty`, `return_ty`, and `yield_ty` are the respective types for the *coroutine* returned by the coroutine-closure[^c1]. [^c1]: @@ -78,7 +86,8 @@ Most of the args to that function will be components that you can get out of the ### Trait Hierarchy -We introduce a parallel hierarchy of `Fn*` traits that are implemented for . The motivation for the introduction was covered in a blog post: [Async Closures](https://hackmd.io/@compiler-errors/async-closures). +We introduce a parallel hierarchy of `Fn*` traits that are implemented for . +The motivation for the introduction was covered in a blog post: [Async Closures](https://hackmd.io/@compiler-errors/async-closures). All currently-stable callable types (i.e., closures, function items, function pointers, and `dyn Fn*` trait objects) automatically implement `AsyncFn*() -> T` if they implement `Fn*() -> Fut` for some output type `Fut`, and `Fut` implements `Future`[^tr1]. @@ -88,23 +97,27 @@ Async closures implement `AsyncFn*` as their bodies permit; i.e. if they end up #### Lending -We may in the future move `AsyncFn*` onto a more general set of `LendingFn*` traits; however, there are some concrete technical implementation details that limit our ability to use `LendingFn` ergonomically in the compiler today. These have to do with: +We may in the future move `AsyncFn*` onto a more general set of `LendingFn*` traits; however, there are some concrete technical implementation details that limit our ability to use `LendingFn` ergonomically in the compiler today. +These have to do with: - Closure signature inference. - Limitations around higher-ranked trait bounds. - Shortcomings with error messages. -These limitations, plus the fact that the underlying trait should have no effect on the user experience of async closures and async `Fn` trait bounds, leads us to `AsyncFn*` for now. To ensure we can eventually move to these more general traits, the precise `AsyncFn*` trait definitions (including the associated types) are left as an implementation detail. +These limitations, plus the fact that the underlying trait should have no effect on the user experience of async closures and async `Fn` trait bounds, leads us to `AsyncFn*` for now. +To ensure we can eventually move to these more general traits, the precise `AsyncFn*` trait definitions (including the associated types) are left as an implementation detail. #### When do async closures implement the regular `Fn*` traits? We mention above that "regular" callable types can implement `AsyncFn*`, but the reverse question exists of "can async closures implement `Fn*` too"? The short answer is "when it's valid", i.e. when the coroutine that would have been returned from `AsyncFn`/`AsyncFnMut` does not actually have any upvars that are "lent" from the parent coroutine-closure. -See the "follow-up: when do..." section below for an elaborated answer. The full answer describes a pretty interesting and hopefully thorough heuristic that is used to ensure that most async closures "just work". +See the "follow-up: when do..." section below for an elaborated answer. +The full answer describes a pretty interesting and hopefully thorough heuristic that is used to ensure that most async closures "just work". ### Tale of two bodies... -When async closures are called with `AsyncFn`/`AsyncFnMut`, they return a coroutine that borrows from the closure. However, when they are called via `AsyncFnOnce`, we consume that closure, and cannot return a coroutine that borrows from data that is now dropped. +When async closures are called with `AsyncFn`/`AsyncFnMut`, they return a coroutine that borrows from the closure. +However, when they are called via `AsyncFnOnce`, we consume that closure, and cannot return a coroutine that borrows from data that is now dropped. To work around this limitation, we synthesize a separate by-move MIR body for calling `AsyncFnOnce::call_once` on a coroutine-closure that can be called by-ref. @@ -120,18 +133,24 @@ This query synthesizes a new MIR body by copying the MIR body of the coroutine a [^b2]: -Since we've synthesized a new def id, this query is also responsible for feeding a ton of other relevant queries for the MIR body. This query is `ensure()`d[^b3] during the `mir_promoted` query, since it operates on the *built* mir of the coroutine. +Since we've synthesized a new def id, this query is also responsible for feeding a ton of other relevant queries for the MIR body. +This query is `ensure()`d[^b3] during the `mir_promoted` query, since it operates on the *built* mir of the coroutine. [^b3]: ### Closure signature inference -The closure signature inference algorithm for async closures is a bit more complicated than the inference algorithm for "traditional" closures. Like closures, we iterate through all of the clauses that may be relevant (for the expectation type passed in)[^deduce1]. +The closure signature inference algorithm for async closures is a bit more complicated than the inference algorithm for "traditional" closures. +Like closures, we iterate through all of the clauses that may be relevant (for the expectation type passed in)[^deduce1]. To extract a signature, we consider two situations: -* Projection predicates with `AsyncFnOnce::Output`, which we will use to extract the inputs and output type for the closure. This corresponds to the situation that there was a `F: AsyncFn*() -> T` bound[^deduce2]. -* Projection predicates with `FnOnce::Output`, which we will use to extract the inputs. For the output, we also try to deduce an output by looking for relevant `Future::Output` projection predicates. This corresponds to the situation that there was an `F: Fn*() -> T, T: Future` bound.[^deduce3] - * If there is no `Future` bound, we simply use a fresh infer var for the output. This corresponds to the case where one can pass an async closure to a combinator function like `Option::map`.[^deduce4] +* Projection predicates with `AsyncFnOnce::Output`, which we will use to extract the inputs and output type for the closure. + This corresponds to the situation that there was a `F: AsyncFn*() -> T` bound[^deduce2]. +* Projection predicates with `FnOnce::Output`, which we will use to extract the inputs. + For the output, we also try to deduce an output by looking for relevant `Future::Output` projection predicates. + This corresponds to the situation that there was an `F: Fn*() -> T, T: Future` bound.[^deduce3] + * If there is no `Future` bound, we simply use a fresh infer var for the output. + This corresponds to the case where one can pass an async closure to a combinator function like `Option::map`.[^deduce4] [^deduce1]: @@ -149,7 +168,7 @@ We defer[^call1] the computation of a coroutine-closure's "kind" (i.e. its maxim [^call1]: -Unlike regular closures, whose return type does not change depending on what `Fn*` trait we call it with, coroutine-closures *do* end up returning different coroutine types depending on the flavor of `AsyncFn*` trait used to call it. +Unlike regular closures, whose return type does not change depending on what `Fn*` trait we call it with, coroutine-closures *do* end up returning different coroutine types depending on the flavor of `AsyncFn*` trait used to call it. Specifically, while the def-id of the returned coroutine does not change, the upvars[^call2] (which are either borrowed or moved from the parent coroutine-closure) and the coroutine-kind[^call3] are dependent on the calling mode. @@ -165,7 +184,9 @@ We introduce a `AsyncFnKindHelper` trait which allows us to defer the question o #### Ok, so why? -This seems a bit roundabout and complex, and I admit that it is. But let's think of the "do nothing" alternative -- we could instead mark all `AsyncFn*` goals as ambiguous until upvar analysis, at which point we would know exactly what to put into the upvars of the coroutine we return. However, this is actually *very* detrimental to inference in the program, since it means that programs like this would not be valid: +This seems a bit roundabout and complex, and I admit that it is. +But let's think of the "do nothing" alternative -- we could instead mark all `AsyncFn*` goals as ambiguous until upvar analysis, at which point we would know exactly what to put into the upvars of the coroutine we return. +However, this is actually *very* detrimental to inference in the program, since it means that programs like this would not be valid: ```rust,ignore let c = async || -> String { .. }; @@ -179,27 +200,35 @@ So *instead*, we use this alias (in this case, a projection: `AsyncFnKindHelper: ### Upvar analysis -By and large, the upvar analysis for coroutine-closures and their child coroutines proceeds like normal upvar analysis. However, there are several interesting bits that happen to account for async closures' special natures: +By and large, the upvar analysis for coroutine-closures and their child coroutines proceeds like normal upvar analysis. +However, there are several interesting bits that happen to account for async closures' special natures: #### Forcing all inputs to be captured -Like async fn, all input arguments are captured. We explicitly force[^f1] all of these inputs to be captured by move so that the future coroutine returned by async closures does not depend on whether the input is *used* by the body or not, which would impart an interesting semver hazard. +Like async fn, all input arguments are captured. +We explicitly force[^f1] all of these inputs to be captured by move so that the future coroutine returned by async closures does not depend on whether the input is *used* by the body or not, which would impart an interesting semver hazard. [^f1]: #### Computing the by-ref captures -For a coroutine-closure that supports `AsyncFn`/`AsyncFnMut`, we must also compute the relationship between the captures of the coroutine-closure and its child coroutine. Specifically, the coroutine-closure may `move` a upvar into its captures, but the coroutine may only borrow that upvar. +For a coroutine-closure that supports `AsyncFn`/`AsyncFnMut`, we must also compute the relationship between the captures of the coroutine-closure and its child coroutine. +Specifically, the coroutine-closure may `move` a upvar into its captures, but the coroutine may only borrow that upvar. -We compute the "`coroutine_captures_by_ref_ty`" by looking at all of the child coroutine's captures and comparing them to the corresponding capture of the parent coroutine-closure[^br1]. This `coroutine_captures_by_ref_ty` ends up being represented as a `for<'env> fn() -> captures...` type, with the additional binder lifetime representing the "`&self`" lifetime of calling `AsyncFn::async_call` or `AsyncFnMut::async_call_mut`. We instantiate that binder later when actually calling the methods. +We compute the "`coroutine_captures_by_ref_ty`" by looking at all of the child coroutine's captures and comparing them to the corresponding capture of the parent coroutine-closure[^br1]. +This `coroutine_captures_by_ref_ty` ends up being represented as a `for<'env> fn() -> captures...` type, with the additional binder lifetime representing the "`&self`" lifetime of calling `AsyncFn::async_call` or `AsyncFnMut::async_call_mut`. +We instantiate that binder later when actually calling the methods. [^br1]: -Note that not every by-ref capture from the parent coroutine-closure results in a "lending" borrow. See the **Follow-up: When do async closures implement the regular `Fn*` traits?** section below for more details, since this intimately influences whether or not the coroutine-closure is allowed to implement the `Fn*` family of traits. +Note that not every by-ref capture from the parent coroutine-closure results in a "lending" borrow. +See the **Follow-up: When do async closures implement the regular `Fn*` traits?** section below for more details, since this intimately influences whether or not the coroutine-closure is allowed to implement the `Fn*` family of traits. #### By-move body + `FnOnce` quirk -There are several situations where the closure upvar analysis ends up inferring upvars for the coroutine-closure's child coroutine that are too relaxed, and end up resulting in borrow-checker errors. This is best illustrated via examples. For example, given: +There are several situations where the closure upvar analysis ends up inferring upvars for the coroutine-closure's child coroutine that are too relaxed, and end up resulting in borrow-checker errors. +This is best illustrated via examples. +For example, given: ```rust fn force_fnonce(t: T) -> T { t } @@ -210,7 +239,8 @@ let c = force_fnonce(async move || { }); ``` -`x` will be moved into the coroutine-closure, but the coroutine that is returned would only borrow `&x`. However, since `force_fnonce` forces the coroutine-closure to `AsyncFnOnce`, which is not *lending*, we must force the capture to happen by-move[^bm1]. +`x` will be moved into the coroutine-closure, but the coroutine that is returned would only borrow `&x`. +However, since `force_fnonce` forces the coroutine-closure to `AsyncFnOnce`, which is not *lending*, we must force the capture to happen by-move[^bm1]. Similarly: @@ -233,13 +263,16 @@ let c = async move || { Well, first of all, all async closures implement `FnOnce` since they can always be called *at least once*. -For `Fn`/`FnMut`, the detailed answer involves answering a related question: is the coroutine-closure lending? Because if it is, then it cannot implement the non-lending `Fn`/`FnMut` traits. +For `Fn`/`FnMut`, the detailed answer involves answering a related question: is the coroutine-closure lending? +Because if it is, then it cannot implement the non-lending `Fn`/`FnMut` traits. -Determining when the coroutine-closure must *lend* its upvars is implemented in the `should_reborrow_from_env_of_parent_coroutine_closure` helper function[^u1]. Specifically, this needs to happen in two places: +Determining when the coroutine-closure must *lend* its upvars is implemented in the `should_reborrow_from_env_of_parent_coroutine_closure` helper function[^u1]. +Specifically, this needs to happen in two places: [^u1]: -1. Are we borrowing data owned by the parent closure? We can determine if that is the case by checking if the parent capture is by-move, EXCEPT if we apply a deref projection, which means we're reborrowing a reference that we captured by-move. +1. Are we borrowing data owned by the parent closure? + We can determine if that is the case by checking if the parent capture is by-move, EXCEPT if we apply a deref projection, which means we're reborrowing a reference that we captured by-move. ```rust let x = &1i32; // Let's call this lifetime `'1`. @@ -250,7 +283,8 @@ let c = async move || { }; ``` -2. If a coroutine is mutably borrowing from a parent capture, then that mutable borrow cannot live for longer than either the parent *or* the borrow that we have on the original upvar. Therefore we always need to borrow the child capture with the lifetime of the parent coroutine-closure's env. +2. If a coroutine is mutably borrowing from a parent capture, then that mutable borrow cannot live for longer than either the parent *or* the borrow that we have on the original upvar. + Therefore we always need to borrow the child capture with the lifetime of the parent coroutine-closure's env. ```rust let mut x = 1i32; @@ -264,7 +298,8 @@ let c = async || { }; ``` -If either of these cases apply, then we should capture the borrow with the lifetime of the parent coroutine-closure's env. Luckily, if this function is not correct, then the program is not unsound, since we still borrowck and validate the choices made from this function -- the only side-effect is that the user may receive unnecessary borrowck errors. +If either of these cases apply, then we should capture the borrow with the lifetime of the parent coroutine-closure's env. +Luckily, if this function is not correct, then the program is not unsound, since we still borrowck and validate the choices made from this function -- the only side-effect is that the user may receive unnecessary borrowck errors. ### Instance resolution @@ -278,7 +313,8 @@ If a coroutine-closure has a closure-kind of `FnMut`/`Fn`, then the same applies [^res3]: -This is represented by the `ConstructCoroutineInClosureShim`[^i1]. The `receiver_by_ref` bool will be true if this is the instance of `Fn::call`/`FnMut::call_mut`.[^i2] The coroutine that all of these instances returns corresponds to the by-move body we will have synthesized by this point.[^i3] +This is represented by the `ConstructCoroutineInClosureShim`[^i1]. +The `receiver_by_ref` bool will be true if this is the instance of `Fn::call`/`FnMut::call_mut`.[^i2] The coroutine that all of these instances returns corresponds to the by-move body we will have synthesized by this point.[^i3] [^i1]: @@ -288,7 +324,8 @@ This is represented by the `ConstructCoroutineInClosureShim`[^i1]. The `receiver ### Borrow-checking -It turns out that borrow-checking async closures is pretty straightforward. After adding a new `DefiningTy::CoroutineClosure`[^bck1] variant, and teaching borrowck how to generate the signature of the coroutine-closure[^bck2], borrowck proceeds totally fine. +It turns out that borrow-checking async closures is pretty straightforward. +After adding a new `DefiningTy::CoroutineClosure`[^bck1] variant, and teaching borrowck how to generate the signature of the coroutine-closure[^bck2], borrowck proceeds totally fine. One thing to note is that we don't borrow-check the synthetic body we make for by-move coroutines, since by construction (and the validity of the by-ref coroutine body it was derived from) it must be valid. From 0c03df62fba74869d7a7283c7e1b2e7386f7dd65 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 15:29:33 +0200 Subject: [PATCH 62/71] avoid inline external links --- src/doc/rustc-dev-guide/src/coroutine-closures.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/coroutine-closures.md b/src/doc/rustc-dev-guide/src/coroutine-closures.md index 00e8a5f80545e..ae177caf6c4ed 100644 --- a/src/doc/rustc-dev-guide/src/coroutine-closures.md +++ b/src/doc/rustc-dev-guide/src/coroutine-closures.md @@ -1,6 +1,6 @@ # Async closures/"coroutine-closures" -Please read [RFC 3668](https://rust-lang.github.io/rfcs/3668-async-closures.html) to understand the general motivation of the feature. +Please read [RFC 3668] to understand the general motivation of the feature. This is a very technical and somewhat "vertical" chapter; ideally we'd split this and sprinkle it across all the relevant chapters, but for the purposes of understanding async closures *holistically*, I've put this together all here in one chapter. ## Coroutine-closures -- a technical deep dive @@ -332,3 +332,5 @@ One thing to note is that we don't borrow-check the synthetic body we make for b [^bck1]: [^bck2]: + +[RFC 3668]: https://rust-lang.github.io/rfcs/3668-async-closures.html From 8e9528fe7e436cf2839717cf61312965095813bd Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 15:31:32 +0200 Subject: [PATCH 63/71] reflow --- src/doc/rustc-dev-guide/src/coroutine-closures.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/coroutine-closures.md b/src/doc/rustc-dev-guide/src/coroutine-closures.md index ae177caf6c4ed..548c3c6c3dbc7 100644 --- a/src/doc/rustc-dev-guide/src/coroutine-closures.md +++ b/src/doc/rustc-dev-guide/src/coroutine-closures.md @@ -1,7 +1,10 @@ # Async closures/"coroutine-closures" Please read [RFC 3668] to understand the general motivation of the feature. -This is a very technical and somewhat "vertical" chapter; ideally we'd split this and sprinkle it across all the relevant chapters, but for the purposes of understanding async closures *holistically*, I've put this together all here in one chapter. +This is a very technical and somewhat "vertical" chapter; +ideally, we'd split this and sprinkle it across all the relevant chapters, +but for the purposes of understanding async closures *holistically*, +I've put this together all here in one chapter. ## Coroutine-closures -- a technical deep dive From 5e823b7719cdf542f9eac45a71951c9a25a87bbc Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 15:36:38 +0200 Subject: [PATCH 64/71] use sentence case for titles --- src/doc/rustc-dev-guide/src/coroutine-closures.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/coroutine-closures.md b/src/doc/rustc-dev-guide/src/coroutine-closures.md index 548c3c6c3dbc7..0e917c4300bab 100644 --- a/src/doc/rustc-dev-guide/src/coroutine-closures.md +++ b/src/doc/rustc-dev-guide/src/coroutine-closures.md @@ -36,7 +36,7 @@ We later force all of the *closure args* to be captured by-value[^l3], but we do [^l3]: -### `rustc_middle::ty` Representation +### `rustc_middle::ty` representation For the purposes of keeping the implementation mostly future-compatible (i.e. with gen `|| {}` and `async gen || {}`), most of this section calls async closures "coroutine-closures". @@ -87,7 +87,7 @@ To most easily construct the `Coroutine` that a coroutine-closure returns, you c Most of the args to that function will be components that you can get out of the `CoroutineArgs`, except for the `goal_kind: ClosureKind` which controls which flavor of coroutine to return based off of the `ClosureKind` passed in -- i.e. it will prepare the by-ref coroutine if `ClosureKind::Fn | ClosureKind::FnMut`, and the by-move coroutine if `ClosureKind::FnOnce`. -### Trait Hierarchy +### Trait hierarchy We introduce a parallel hierarchy of `Fn*` traits that are implemented for . The motivation for the introduction was covered in a blog post: [Async Closures](https://hackmd.io/@compiler-errors/async-closures). From cf4274ef65b4202be1911415ea72c847fdfa31c9 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 16:08:35 +0200 Subject: [PATCH 65/71] replace a needless capitatlisation --- src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md index 7396f0aba9e89..8bacf5ed2ec28 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md @@ -53,7 +53,7 @@ A new diagnostic item can be added with these two steps: [`rustc_span::symbol::sym`]. To add your newly-created diagnostic item, simply open the module file, - and add the name (In this case `Cat`) at the correct point in the list. + and add the name, in this case `Cat`, at the correct point in the list. Now you can create a pull request with your changes. :tada: From 22ac34b83af17fa92a9f9a98b9947c217727a3b3 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 16:09:32 +0200 Subject: [PATCH 66/71] sembr src/diagnostics/diagnostic-items.md --- .../src/diagnostics/diagnostic-items.md | 73 +++++++++---------- 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md index 8bacf5ed2ec28..41e6829f8edf3 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md @@ -1,19 +1,20 @@ # Diagnostic Items -While writing lints it's common to check for specific types, traits and -functions. This raises the question on how to check for these. Types can be -checked by their complete type path. However, this requires hard coding paths -and can lead to misclassifications in some edge cases. To counteract this, -rustc has introduced diagnostic items that are used to identify types via -[`Symbol`]s. +While writing lints it's common to check for specific types, traits and functions. +This raises the question on how to check for these. +Types can be checked by their complete type path. +However, this requires hard coding paths and can lead to misclassifications in some edge cases. +To counteract this, +rustc has introduced diagnostic items that are used to identify types via [`Symbol`]s. ## Finding diagnostic items Diagnostic items are added to items inside `rustc`/`std`/`core`/`alloc` with the -`rustc_diagnostic_item` attribute. The item for a specific type can be found by +`rustc_diagnostic_item` attribute. +The item for a specific type can be found by opening the source code in the documentation and looking for this attribute. -Note that it's often added with the `cfg_attr` attribute to avoid compilation -errors during tests. A definition often looks like this: +Note that it's often added with the `cfg_attr` attribute to avoid compilation errors during tests. +A definition often looks like this: ```rs // This is the diagnostic item for this type vvvvvvv @@ -32,12 +33,13 @@ please use the diagnostic item of the item and reference A new diagnostic item can be added with these two steps: -1. Find the target item inside the Rust repo. Now add the diagnostic item as a - string via the `rustc_diagnostic_item` attribute. This can sometimes cause - compilation errors while running tests. These errors can be avoided by using +1. Find the target item inside the Rust repo. + Now add the diagnostic item as a string via the `rustc_diagnostic_item` attribute. + This can sometimes cause compilation errors while running tests. + These errors can be avoided by using the `cfg_attr` attribute with the `not(test)` condition (it's fine adding - then for all `rustc_diagnostic_item` attributes as a preventive manner). At - the end, it should look like this: + then for all `rustc_diagnostic_item` attributes as a preventive manner). + At the end, it should look like this: ```rs // This will be the new diagnostic item vvv @@ -49,13 +51,13 @@ A new diagnostic item can be added with these two steps: [*Naming Conventions*](#naming-conventions). 2. - Diagnostic items in code are accessed via symbols in - [`rustc_span::symbol::sym`]. + Diagnostic items in code are accessed via symbols in [`rustc_span::symbol::sym`]. To add your newly-created diagnostic item, simply open the module file, and add the name, in this case `Cat`, at the correct point in the list. -Now you can create a pull request with your changes. :tada: +Now you can create a pull request with your changes. +:tada: > NOTE: > When using diagnostic items in other projects like Clippy, @@ -67,19 +69,15 @@ Diagnostic items don't have a naming convention yet. Following are some guidelines that should be used in future, but might differ from existing names: -* Types, traits, and enums are named using UpperCamelCase - (Examples: `Iterator` and `HashMap`) +* Types, traits, and enums are named using UpperCamelCase (Examples: `Iterator` and `HashMap`) * For type names that are used multiple times, like `Writer`, it's good to choose a more precise name, - maybe by adding the module to it - (Example: `IoWriter`) + maybe by adding the module to it (Example: `IoWriter`) * Associated items should not get their own diagnostic items, - but instead be accessed indirectly by the diagnostic item - of the type they're originating from. + but instead be accessed indirectly by the diagnostic item of the type they're originating from. * Freestanding functions like `std::mem::swap()` should be named using - `snake_case` with one important (export) module as a prefix - (Examples: `mem_swap` and `cmp_max`) + `snake_case` with one important (export) module as a prefix (Examples: `mem_swap` and `cmp_max`) * Modules should usually not have a diagnostic item attached to them. Diagnostic items were added to avoid the usage of paths, and using them on modules would therefore most likely be counterproductive. @@ -87,11 +85,12 @@ but might differ from existing names: ## Using diagnostic items In rustc, diagnostic items are looked up via [`Symbol`]s from inside the -[`rustc_span::symbol::sym`] module. These can then be mapped to [`DefId`]s +[`rustc_span::symbol::sym`] module. +These can then be mapped to [`DefId`]s using [`TyCtxt::get_diagnostic_item()`] or checked if they match a [`DefId`] -using [`TyCtxt::is_diagnostic_item()`]. When mapping from a diagnostic item to -a [`DefId`], the method will return a `Option`. This can be `None` if -either the symbol isn't a diagnostic item or the type is not registered, for +using [`TyCtxt::is_diagnostic_item()`]. +When mapping from a diagnostic item to a [`DefId`], the method will return a `Option`. +This can be `None` if either the symbol isn't a diagnostic item or the type is not registered, for instance when compiling with `#[no_std]`. All the following examples are based on [`DefId`]s and their usage. @@ -130,22 +129,20 @@ fn is_diag_trait_item( ### Associated Types Associated types of diagnostic items can be accessed indirectly by first -getting the [`DefId`] of the trait and then calling -[`TyCtxt::associated_items()`]. This returns an [`AssocItems`] object which can -be used for further checks. Checkout -[`clippy_utils::ty::get_iterator_item_ty()`] for an example usage of this. +getting the [`DefId`] of the trait and then calling [`TyCtxt::associated_items()`]. +This returns an [`AssocItems`] object which can be used for further checks. +Checkout [`clippy_utils::ty::get_iterator_item_ty()`] for an example usage of this. ### Usage in Clippy Clippy tries to use diagnostic items where possible and has developed some -wrapper and utility functions. Please also refer to its documentation when -using diagnostic items in Clippy. (See [*Common tools for writing -lints*][clippy-Common-tools-for-writing-lints].) +wrapper and utility functions. +Please also refer to its documentation when using diagnostic items in Clippy. +(See [*Common tools for writing lints*][clippy-Common-tools-for-writing-lints].) ## Related issues -These are probably only interesting to people -who really want to take a deep dive into the topic :) +These are probably only interesting to people who really want to take a deep dive into the topic :) * [rust#60966]: The Rust PR that introduced diagnostic items * [rust-clippy#5393]: Clippy's tracking issue for moving away from hard coded paths to From d8fca8c4d2c9af4e6f33102cacf978857ba896f4 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 16:12:04 +0200 Subject: [PATCH 67/71] missing pauses --- src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md index 41e6829f8edf3..0b55e35d2d6b5 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md @@ -1,6 +1,6 @@ # Diagnostic Items -While writing lints it's common to check for specific types, traits and functions. +While writing lints, it's common to check for specific types, traits, and functions. This raises the question on how to check for these. Types can be checked by their complete type path. However, this requires hard coding paths and can lead to misclassifications in some edge cases. From ee0aa9a7be7f09453ea35e6038038b0e169eb546 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 16:12:41 +0200 Subject: [PATCH 68/71] less playful Does not play well with sembr tool, since lines are split on the dot --- src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md | 1 - 1 file changed, 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md index 0b55e35d2d6b5..424abd3e5ba30 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md @@ -57,7 +57,6 @@ A new diagnostic item can be added with these two steps: and add the name, in this case `Cat`, at the correct point in the list. Now you can create a pull request with your changes. -:tada: > NOTE: > When using diagnostic items in other projects like Clippy, From 16bc3b8c38fc6cef5286a7ba063f8b15d5924470 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 16:16:14 +0200 Subject: [PATCH 69/71] lighter markup --- src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md index 424abd3e5ba30..61d571a5d8cd9 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md +++ b/src/doc/rustc-dev-guide/src/diagnostics/diagnostic-items.md @@ -137,7 +137,7 @@ Checkout [`clippy_utils::ty::get_iterator_item_ty()`] for an example usage of th Clippy tries to use diagnostic items where possible and has developed some wrapper and utility functions. Please also refer to its documentation when using diagnostic items in Clippy. -(See [*Common tools for writing lints*][clippy-Common-tools-for-writing-lints].) +(See [*Common tools for writing lints*].) ## Related issues @@ -157,6 +157,6 @@ These are probably only interesting to people who really want to take a deep div [`TyCtxt::associated_items()`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/context/struct.TyCtxt.html#method.associated_items [`AssocItems`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/assoc/struct.AssocItems.html [`clippy_utils::ty::get_iterator_item_ty()`]: https://github.com/rust-lang/rust-clippy/blob/305177342fbc622c0b3cb148467bab4b9524c934/clippy_utils/src/ty.rs#L55-L72 -[clippy-Common-tools-for-writing-lints]: https://doc.rust-lang.org/nightly/clippy/development/common_tools_writing_lints.html +[*Common tools for writing lints*]: https://doc.rust-lang.org/nightly/clippy/development/common_tools_writing_lints.html [rust#60966]: https://github.com/rust-lang/rust/pull/60966 [rust-clippy#5393]: https://github.com/rust-lang/rust-clippy/issues/5393 From 7bc6a3f4793527fea9634af716261cda203e4f56 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 16:16:48 +0200 Subject: [PATCH 70/71] sembr src/rustbot.md --- src/doc/rustc-dev-guide/src/rustbot.md | 31 ++++++++++++++------------ 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/rustbot.md b/src/doc/rustc-dev-guide/src/rustbot.md index 88b831697ffa1..ed0c0e14caa47 100644 --- a/src/doc/rustc-dev-guide/src/rustbot.md +++ b/src/doc/rustc-dev-guide/src/rustbot.md @@ -2,8 +2,8 @@ `@rustbot` (also known as `triagebot`) is a utility robot that is mostly used to allow any contributor to achieve certain tasks that would normally require GitHub -membership to the `rust-lang` organization. Its most interesting features for -contributors to `rustc` are issue claiming and relabeling. +membership to the `rust-lang` organization. +Its most interesting features for contributors to `rustc` are issue claiming and relabeling. ## Issue claiming @@ -24,27 +24,29 @@ If you want to unassign from an issue, `@rustbot` has a different command: ## Issue relabeling -Changing labels for an issue or PR is also normally reserved for members of the -organization. However, `@rustbot` allows you to relabel an issue yourself, only -with a few restrictions. This is mostly useful in two cases: +Changing labels for an issue or PR is also normally reserved for members of the organization. +However, `@rustbot` allows you to relabel an issue yourself, only with a few restrictions. +This is mostly useful in two cases: **Helping with issue triage**: Rust's issue tracker has more than 5,000 open issues at the time of this writing, so labels are the most powerful tool that we -have to keep it as tidy as possible. You don't need to spend hours in the issue tracker +have to keep it as tidy as possible. +You don't need to spend hours in the issue tracker to triage issues, but if you open an issue, you should feel free to label it if you are comfortable with doing it yourself. -**Updating the status of a PR**: We use "status labels" to reflect the status of -PRs. For example, if your PR has merge conflicts, it will automatically be assigned -the `S-waiting-on-author`, and reviewers might not review it until you rebase your -PR. Once you do rebase your branch, you should change the labels yourself to remove -the `S-waiting-on-author` label and add back `S-waiting-on-review`. In this case, +**Updating the status of a PR**: We use "status labels" to reflect the status of PRs. +For example, if your PR has merge conflicts, it will automatically be assigned +the `S-waiting-on-author`, and reviewers might not review it until you rebase your PR. +Once you do rebase your branch, you should change the labels yourself to remove +the `S-waiting-on-author` label and add back `S-waiting-on-review`. +In this case, the `@rustbot` command will look like this: @rustbot label -S-waiting-on-author +S-waiting-on-review -The syntax for this command is pretty loose, so there are other variants of this -command invocation. There are also some shortcuts to update labels, +The syntax for this command is pretty loose, so there are other variants of this command invocation. +There are also some shortcuts to update labels, for instance `@rustbot ready` will do the same thing with above command. For more details, see [the docs page about labeling][labeling] and [shortcuts][shortcuts]. @@ -57,7 +59,8 @@ If you are interested in seeing what `@rustbot` is capable of, check out its [do which is meant as a reference for the bot and should be kept up to date every time the bot gets an upgrade. -`@rustbot` is maintained by the Release team. If you have any feedback regarding +`@rustbot` is maintained by the Release team. +If you have any feedback regarding existing commands or suggestions for new commands, feel free to reach out [on Zulip][zulip] or file an issue in [the triagebot repository][repo] From 72b569c9ba61ea6ae0345d9366218ba423d2566d Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 14 Jul 2026 16:19:47 +0200 Subject: [PATCH 71/71] lighter markup --- src/doc/rustc-dev-guide/src/rustbot.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/rustbot.md b/src/doc/rustc-dev-guide/src/rustbot.md index ed0c0e14caa47..11a2404c2aa6e 100644 --- a/src/doc/rustc-dev-guide/src/rustbot.md +++ b/src/doc/rustc-dev-guide/src/rustbot.md @@ -48,7 +48,7 @@ the `@rustbot` command will look like this: The syntax for this command is pretty loose, so there are other variants of this command invocation. There are also some shortcuts to update labels, for instance `@rustbot ready` will do the same thing with above command. -For more details, see [the docs page about labeling][labeling] and [shortcuts][shortcuts]. +For more details, see [the docs page about labeling][labeling] and [shortcuts]. [labeling]: https://forge.rust-lang.org/triagebot/labeling.html [shortcuts]: https://forge.rust-lang.org/triagebot/shortcuts.html @@ -62,8 +62,8 @@ bot gets an upgrade. `@rustbot` is maintained by the Release team. If you have any feedback regarding existing commands or suggestions for new commands, feel free to reach out -[on Zulip][zulip] or file an issue in [the triagebot repository][repo] +[on Zulip] or file an issue in [the triagebot repository]. [documentation]: https://forge.rust-lang.org/triagebot/index.html -[zulip]: https://rust-lang.zulipchat.com/#narrow/stream/224082-t-release.2Ftriagebot -[repo]: https://github.com/rust-lang/triagebot/ +[on zulip]: https://rust-lang.zulipchat.com/#narrow/stream/224082-t-release.2Ftriagebot +[the triagebot repository]: https://github.com/rust-lang/triagebot/