From f5a2bb6263ad332ceb2d9b3b82afe935259a7723 Mon Sep 17 00:00:00 2001 From: "Andrew V. Teylu" Date: Mon, 2 Mar 2026 20:28:16 +0000 Subject: [PATCH 01/13] Add hygiene annotations for tokens in macro_rules! bodies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `-Zunpretty=expanded,hygiene` was not printing syntax context annotations for identifiers and lifetimes inside `macro_rules!` bodies. These tokens are printed via `print_tt()` → `token_to_string_ext()`, which converts tokens to strings without calling `ann_post()`. This meant that macro-generated `macro_rules!` definitions with hygienic metavar parameters (e.g. multiple `$marg` distinguished only by hygiene) were printed with no way to tell them apart. This was fixed by adding a match on `token.kind` in `print_tt()` to call `ann_post()` for `Ident`, `NtIdent`, `Lifetime`, and `NtLifetime` tokens, matching how `print_ident()` and `print_lifetime()` already handle AST-level identifiers and lifetimes. Signed-off-by: Andrew V. Teylu --- compiler/rustc_ast_pretty/src/pprust/state.rs | 17 ++++++ .../hygiene/unpretty-debug-lifetimes.stdout | 8 +-- tests/ui/hygiene/unpretty-debug-metavars.rs | 25 +++++++++ .../ui/hygiene/unpretty-debug-metavars.stdout | 52 +++++++++++++++++++ tests/ui/hygiene/unpretty-debug.stdout | 10 +++- tests/ui/proc-macro/meta-macro-hygiene.stdout | 5 +- .../nonterminal-token-hygiene.stdout | 17 ++++-- 7 files changed, 122 insertions(+), 12 deletions(-) create mode 100644 tests/ui/hygiene/unpretty-debug-metavars.rs create mode 100644 tests/ui/hygiene/unpretty-debug-metavars.stdout diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index f4168301bc5df..7d963dd2037c5 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -735,6 +735,23 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere TokenTree::Token(token, spacing) => { let token_str = self.token_to_string_ext(token, convert_dollar_crate); self.word(token_str); + // Emit hygiene annotations for identity-bearing tokens, + // matching how print_ident() and print_lifetime() call ann_post(). + match token.kind { + token::Ident(name, _) => { + self.ann_post(Ident::new(name, token.span)); + } + token::NtIdent(ident, _) => { + self.ann_post(ident); + } + token::Lifetime(name, _) => { + self.ann_post(Ident::new(name, token.span)); + } + token::NtLifetime(ident, _) => { + self.ann_post(ident); + } + _ => {} + } if let token::DocComment(..) = token.kind { self.hardbreak() } diff --git a/tests/ui/hygiene/unpretty-debug-lifetimes.stdout b/tests/ui/hygiene/unpretty-debug-lifetimes.stdout index 28a5c70a02d79..689453326c0b5 100644 --- a/tests/ui/hygiene/unpretty-debug-lifetimes.stdout +++ b/tests/ui/hygiene/unpretty-debug-lifetimes.stdout @@ -7,15 +7,17 @@ // Don't break whenever Symbol numbering changes //@ normalize-stdout: "\d+#" -> "0#" -#![feature /* 0#0 */(decl_macro)] -#![feature /* 0#0 */(no_core)] +#![feature /* 0#0 */(decl_macro /* 0#0 */)] +#![feature /* 0#0 */(no_core /* 0#0 */)] #![no_core /* 0#0 */] macro lifetime_hygiene /* 0#0 */ { - ($f:ident<$a:lifetime>) => { fn $f<$a, 'a>() {} } + ($f /* 0#0 */:ident /* 0#0 */<$a /* 0#0 */:lifetime /* 0#0 */>) + => + { fn /* 0#0 */ $f /* 0#0 */<$a /* 0#0 */, 'a /* 0#0 */>() {} } } fn f /* 0#0 */<'a /* 0#0 */, 'a /* 0#1 */>() {} diff --git a/tests/ui/hygiene/unpretty-debug-metavars.rs b/tests/ui/hygiene/unpretty-debug-metavars.rs new file mode 100644 index 0000000000000..a0c47bec3ccf7 --- /dev/null +++ b/tests/ui/hygiene/unpretty-debug-metavars.rs @@ -0,0 +1,25 @@ +//@ check-pass +//@ compile-flags: -Zunpretty=expanded,hygiene + +// Regression test: metavar parameters in macro-generated macro_rules! +// definitions should have hygiene annotations so that textually identical +// `$marg` bindings are distinguishable by their syntax contexts. + +// Don't break whenever Symbol numbering changes +//@ normalize-stdout: "\d+#" -> "0#" + +#![feature(no_core)] +#![no_core] + +macro_rules! make_macro { + (@inner $name:ident ($dol:tt) $a:ident) => { + macro_rules! $name { + ($dol $a : expr, $dol marg : expr) => {} + } + }; + ($name:ident) => { + make_macro!{@inner $name ($) marg} + }; +} + +make_macro!(add2); diff --git a/tests/ui/hygiene/unpretty-debug-metavars.stdout b/tests/ui/hygiene/unpretty-debug-metavars.stdout new file mode 100644 index 0000000000000..307c5e1b8de97 --- /dev/null +++ b/tests/ui/hygiene/unpretty-debug-metavars.stdout @@ -0,0 +1,52 @@ +//@ check-pass +//@ compile-flags: -Zunpretty=expanded,hygiene + +// Regression test: metavar parameters in macro-generated macro_rules! +// definitions should have hygiene annotations so that textually identical +// `$marg` bindings are distinguishable by their syntax contexts. + +// Don't break whenever Symbol numbering changes +//@ normalize-stdout: "\d+#" -> "0#" + +#![feature /* 0#0 */(no_core /* 0#0 */)] +#![no_core /* 0#0 */] + +macro_rules! make_macro + /* + 0#0 + */ { + (@inner /* 0#0 */ $name /* 0#0 */:ident /* 0#0 + */($dol /* 0#0 */:tt /* 0#0 */) $a /* 0#0 */:ident /* 0#0 */) + => + { + macro_rules /* 0#0 */! $name /* 0#0 */ + { + ($dol /* 0#0 */ $a /* 0#0 */ : expr /* 0#0 */, $dol /* + 0#0 */ marg /* 0#0 */ : expr /* 0#0 */) => {} + } + }; ($name /* 0#0 */:ident /* 0#0 */) => + { + make_macro /* 0#0 + */!{@inner /* 0#0 */ $name /* 0#0 */($) marg /* 0#0 */} + }; +} +macro_rules! add2 + /* + 0#0 + */ { + ($ marg /* 0#1 */ : expr /* 0#2 */, $marg /* 0#2 */ : expr /* + 0#2 */) => {} +} + + +/* +Expansions: +crate0::{{expn0}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Root +crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "make_macro") +crate0::{{expn2}}: parent: crate0::{{expn1}}, call_site_ctxt: #1, def_site_ctxt: #0, kind: Macro(Bang, "make_macro") + +SyntaxContexts: +#0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque) +#1: parent: #0, outer_mark: (crate0::{{expn1}}, SemiOpaque) +#2: parent: #0, outer_mark: (crate0::{{expn2}}, SemiOpaque) +*/ diff --git a/tests/ui/hygiene/unpretty-debug.stdout b/tests/ui/hygiene/unpretty-debug.stdout index f35bd7a7cb2ca..ac6051e2d542e 100644 --- a/tests/ui/hygiene/unpretty-debug.stdout +++ b/tests/ui/hygiene/unpretty-debug.stdout @@ -5,10 +5,16 @@ //@ normalize-stdout: "\d+#" -> "0#" // minimal junk -#![feature /* 0#0 */(no_core)] +#![feature /* 0#0 */(no_core /* 0#0 */)] #![no_core /* 0#0 */] -macro_rules! foo /* 0#0 */ { ($x: ident) => { y + $x } } +macro_rules! foo + /* + 0#0 + */ { + ($x /* 0#0 */: ident /* 0#0 */) => + { y /* 0#0 */ + $x /* 0#0 */ } +} fn bar /* 0#0 */() { let x /* 0#0 */ = 1; diff --git a/tests/ui/proc-macro/meta-macro-hygiene.stdout b/tests/ui/proc-macro/meta-macro-hygiene.stdout index b5db9922b31a8..a03e567bd48aa 100644 --- a/tests/ui/proc-macro/meta-macro-hygiene.stdout +++ b/tests/ui/proc-macro/meta-macro-hygiene.stdout @@ -1,7 +1,7 @@ Def site: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) Input: TokenStream [Ident { ident: "$crate", span: $DIR/meta-macro-hygiene.rs:26:37: 26:43 (#3) }, Punct { ch: ':', spacing: Joint, span: $DIR/meta-macro-hygiene.rs:26:43: 26:44 (#3) }, Punct { ch: ':', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:26:44: 26:45 (#3) }, Ident { ident: "dummy", span: $DIR/meta-macro-hygiene.rs:26:45: 26:50 (#3) }, Punct { ch: '!', spacing: Alone, span: $DIR/meta-macro-hygiene.rs:26:50: 26:51 (#3) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/meta-macro-hygiene.rs:26:51: 26:53 (#3) }] Respanned: TokenStream [Ident { ident: "$crate", span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Punct { ch: ':', spacing: Joint, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Punct { ch: ':', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Ident { ident: "dummy", span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Punct { ch: '!', spacing: Alone, span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }, Group { delimiter: Parenthesis, stream: TokenStream [], span: $DIR/auxiliary/make-macro.rs:7:9: 7:56 (#4) }] -#![feature /* 0#0 */(prelude_import)] +#![feature /* 0#0 */(prelude_import /* 0#0 */)] //@ aux-build:make-macro.rs //@ proc-macro: meta-macro.rs //@ edition:2018 @@ -30,7 +30,8 @@ macro_rules! produce_it */ { () => { - meta_macro::print_def_site!($crate::dummy!()); + meta_macro /* 0#0 */::print_def_site /* 0#0 + */!($crate /* 0#0 */::dummy /* 0#0 */!()); // `print_def_site!` will respan the `$crate` identifier // with `Span::def_site()`. This should cause it to resolve // relative to `meta_macro`, *not* `make_macro` (despite diff --git a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout index e45abab03b4c4..61b55782e6e9d 100644 --- a/tests/ui/proc-macro/nonterminal-token-hygiene.stdout +++ b/tests/ui/proc-macro/nonterminal-token-hygiene.stdout @@ -20,7 +20,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ span: $DIR/nonterminal-token-hygiene.rs:23:27: 23:32 (#4), }, ] -#![feature /* 0#0 */(prelude_import)] +#![feature /* 0#0 */(prelude_import /* 0#0 */)] #![no_std /* 0#0 */] // Make sure that marks from declarative macros are applied to tokens in nonterminal. @@ -34,7 +34,7 @@ PRINT-BANG INPUT (DEBUG): TokenStream [ //@ proc-macro: test-macros.rs //@ edition: 2015 -#![feature /* 0#0 */(decl_macro)] +#![feature /* 0#0 */(decl_macro /* 0#0 */)] #![no_std /* 0#0 */] extern crate core /* 0#2 */; #[prelude_import /* 0#1 */] @@ -49,15 +49,22 @@ macro_rules! outer /* 0#0 */ { - ($item:item) => + ($item /* 0#0 */:item /* 0#0 */) => { - macro inner() { print_bang! { $item } } inner!(); + macro /* 0#0 */ inner /* 0#0 */() + { print_bang /* 0#0 */! { $item /* 0#0 */ } } inner /* 0#0 + */!(); }; } struct S /* 0#0 */; -macro inner /* 0#3 */ { () => { print_bang! { struct S; } } } +macro inner + /* + 0#3 + */ { + () => { print_bang /* 0#3 */! { struct /* 0#0 */ S /* 0#0 */; } } +} struct S /* 0#5 */; // OK, not a duplicate definition of `S` From bf6db4f345799e1f922b3109a7278d8be11f3058 Mon Sep 17 00:00:00 2001 From: "Andrew V. Teylu" Date: Tue, 3 Mar 2026 13:06:55 +0000 Subject: [PATCH 02/13] Add regression tests for token hygiene annotations in macro bodies Add `unpretty-debug-shadow` test covering macro body tokens that reference a shadowed variable, and simplify the `unpretty-debug-metavars` test macro. Signed-off-by: Andrew V. Teylu --- tests/ui/hygiene/unpretty-debug-metavars.rs | 7 +++-- .../ui/hygiene/unpretty-debug-metavars.stdout | 7 +++-- tests/ui/hygiene/unpretty-debug-shadow.rs | 20 +++++++++++++ tests/ui/hygiene/unpretty-debug-shadow.stdout | 30 +++++++++++++++++++ 4 files changed, 58 insertions(+), 6 deletions(-) create mode 100644 tests/ui/hygiene/unpretty-debug-shadow.rs create mode 100644 tests/ui/hygiene/unpretty-debug-shadow.stdout diff --git a/tests/ui/hygiene/unpretty-debug-metavars.rs b/tests/ui/hygiene/unpretty-debug-metavars.rs index a0c47bec3ccf7..41bf75cd0d98b 100644 --- a/tests/ui/hygiene/unpretty-debug-metavars.rs +++ b/tests/ui/hygiene/unpretty-debug-metavars.rs @@ -1,9 +1,10 @@ //@ check-pass //@ compile-flags: -Zunpretty=expanded,hygiene -// Regression test: metavar parameters in macro-generated macro_rules! -// definitions should have hygiene annotations so that textually identical -// `$marg` bindings are distinguishable by their syntax contexts. +// Regression test for token hygiene annotations in -Zunpretty=expanded,hygiene +// Previously, metavar parameters in macro-generated macro_rules! definitions +// were missing hygiene annotations, making identical `$marg` bindings +// indistinguishable. // Don't break whenever Symbol numbering changes //@ normalize-stdout: "\d+#" -> "0#" diff --git a/tests/ui/hygiene/unpretty-debug-metavars.stdout b/tests/ui/hygiene/unpretty-debug-metavars.stdout index 307c5e1b8de97..89658bc909a13 100644 --- a/tests/ui/hygiene/unpretty-debug-metavars.stdout +++ b/tests/ui/hygiene/unpretty-debug-metavars.stdout @@ -1,9 +1,10 @@ //@ check-pass //@ compile-flags: -Zunpretty=expanded,hygiene -// Regression test: metavar parameters in macro-generated macro_rules! -// definitions should have hygiene annotations so that textually identical -// `$marg` bindings are distinguishable by their syntax contexts. +// Regression test for token hygiene annotations in -Zunpretty=expanded,hygiene +// Previously, metavar parameters in macro-generated macro_rules! definitions +// were missing hygiene annotations, making identical `$marg` bindings +// indistinguishable. // Don't break whenever Symbol numbering changes //@ normalize-stdout: "\d+#" -> "0#" diff --git a/tests/ui/hygiene/unpretty-debug-shadow.rs b/tests/ui/hygiene/unpretty-debug-shadow.rs new file mode 100644 index 0000000000000..2aa68c8aec110 --- /dev/null +++ b/tests/ui/hygiene/unpretty-debug-shadow.rs @@ -0,0 +1,20 @@ +//@ check-pass +//@ compile-flags: -Zunpretty=expanded,hygiene + +// Regression test for token hygiene annotations in -Zunpretty=expanded,hygiene +// Previously, tokens in macro_rules! bodies were missing hygiene annotations, +// making it impossible to see how a macro's reference to a shadowed variable +// is distinguished from the shadowing binding. + +// Don't break whenever Symbol numbering changes +//@ normalize-stdout: "\d+#" -> "0#" + +#![feature(no_core)] +#![no_core] + +fn f() { + let x = 0; + macro_rules! use_x { () => { x }; } + let x = 1; + use_x!(); +} diff --git a/tests/ui/hygiene/unpretty-debug-shadow.stdout b/tests/ui/hygiene/unpretty-debug-shadow.stdout new file mode 100644 index 0000000000000..36076b6a968fe --- /dev/null +++ b/tests/ui/hygiene/unpretty-debug-shadow.stdout @@ -0,0 +1,30 @@ +//@ check-pass +//@ compile-flags: -Zunpretty=expanded,hygiene + +// Regression test for token hygiene annotations in -Zunpretty=expanded,hygiene +// Previously, tokens in macro_rules! bodies were missing hygiene annotations, +// making it impossible to see how a macro's reference to a shadowed variable +// is distinguished from the shadowing binding. + +// Don't break whenever Symbol numbering changes +//@ normalize-stdout: "\d+#" -> "0#" + +#![feature /* 0#0 */(no_core /* 0#0 */)] +#![no_core /* 0#0 */] + +fn f /* 0#0 */() { + let x /* 0#0 */ = 0; + macro_rules! use_x /* 0#0 */ { () => { x /* 0#0 */ }; } + let x /* 0#0 */ = 1; + x /* 0#1 */; +} + +/* +Expansions: +crate0::{{expn0}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Root +crate0::{{expn1}}: parent: crate0::{{expn0}}, call_site_ctxt: #0, def_site_ctxt: #0, kind: Macro(Bang, "use_x") + +SyntaxContexts: +#0: parent: #0, outer_mark: (crate0::{{expn0}}, Opaque) +#1: parent: #0, outer_mark: (crate0::{{expn1}}, SemiOpaque) +*/ From 7ec77fd4d5cb035b81a8477c50763c727ceb09c1 Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 15 Mar 2026 20:29:39 +0900 Subject: [PATCH 03/13] Fix invalid suggestion on `for-loops-over-fallibles` --- .../src/for_loops_over_fallibles.rs | 2 ++ .../macro-iterator-next.rs | 21 ++++++++++++++++ .../macro-iterator-next.stderr | 24 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 tests/ui/lint/for-loops-over-falibles/macro-iterator-next.rs create mode 100644 tests/ui/lint/for-loops-over-falibles/macro-iterator-next.stderr diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index fe95a682c6376..24220e035b1dd 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -77,6 +77,8 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { }; let sub = if let Some(recv) = extract_iterator_next_call(cx, arg) + && recv.span.can_be_used_for_suggestions() + && recv.span.between(arg_span.shrink_to_hi()).can_be_used_for_suggestions() && let Ok(recv_snip) = cx.sess().source_map().span_to_snippet(recv.span) { ForLoopsOverFalliblesLoopSub::RemoveNext { diff --git a/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.rs b/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.rs new file mode 100644 index 0000000000000..0fe88734099c7 --- /dev/null +++ b/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.rs @@ -0,0 +1,21 @@ +// This test ensures that the `for-loops-over-fallibles` lint doesn't suggest +// removing `next`. +// ref. + +#![forbid(for_loops_over_fallibles)] +//~^ NOTE: the lint level is defined here + +fn main() { + macro_rules! mac { + (next $e:expr) => { + $e.iter().next() + }; + } + + for _ in mac!(next [1, 2]) {} + //~^ ERROR: for loop over an `Option`. This is more readably written as an `if let` statement + //~| NOTE: in this expansion of desugaring of `for` loop + //~| NOTE: in this expansion of desugaring of `for` loop + //~| HELP: to check pattern in a loop use `while let` + //~| HELP: consider using `if let` to clear intent +} diff --git a/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.stderr b/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.stderr new file mode 100644 index 0000000000000..36848fffa4394 --- /dev/null +++ b/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.stderr @@ -0,0 +1,24 @@ +error: for loop over an `Option`. This is more readably written as an `if let` statement + --> $DIR/macro-iterator-next.rs:15:14 + | +LL | for _ in mac!(next [1, 2]) {} + | ^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/macro-iterator-next.rs:5:11 + | +LL | #![forbid(for_loops_over_fallibles)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ +help: to check pattern in a loop use `while let` + | +LL - for _ in mac!(next [1, 2]) {} +LL + while let Some(_) = mac!(next [1, 2]) {} + | +help: consider using `if let` to clear intent + | +LL - for _ in mac!(next [1, 2]) {} +LL + if let Some(_) = mac!(next [1, 2]) {} + | + +error: aborting due to 1 previous error + From 924374c75b6ec99c5d95847f6c54554d8d590716 Mon Sep 17 00:00:00 2001 From: Redddy Date: Tue, 17 Mar 2026 06:30:04 +0000 Subject: [PATCH 04/13] mGCA: Lower const generic args to infer when needed --- compiler/rustc_ast_lowering/src/lib.rs | 10 +++-- .../const-generics/mgca/braced-const-infer.rs | 9 ++++ .../mgca/braced-const-infer.stderr | 9 ++++ .../mgca/macro-const-arg-infer.rs | 20 +++++++++ .../mgca/macro-const-arg-infer.stderr | 44 +++++++++++++++++++ 5 files changed, 89 insertions(+), 3 deletions(-) create mode 100644 tests/ui/const-generics/mgca/braced-const-infer.rs create mode 100644 tests/ui/const-generics/mgca/braced-const-infer.stderr create mode 100644 tests/ui/const-generics/mgca/macro-const-arg-infer.rs create mode 100644 tests/ui/const-generics/mgca/macro-const-arg-infer.stderr diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index e8092b540ec87..7a0537b0e7c6e 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1269,9 +1269,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { } GenericArg::Type(self.lower_ty_alloc(ty, itctx).try_as_ambig_ty().unwrap()) } - ast::GenericArg::Const(ct) => GenericArg::Const( - self.lower_anon_const_to_const_arg_and_alloc(ct).try_as_ambig_ct().unwrap(), - ), + ast::GenericArg::Const(ct) => { + let ct = self.lower_anon_const_to_const_arg_and_alloc(ct); + match ct.try_as_ambig_ct() { + Some(ct) => GenericArg::Const(ct), + None => GenericArg::Infer(hir::InferArg { hir_id: ct.hir_id, span: ct.span }), + } + } } } diff --git a/tests/ui/const-generics/mgca/braced-const-infer.rs b/tests/ui/const-generics/mgca/braced-const-infer.rs new file mode 100644 index 0000000000000..9b007d338d1da --- /dev/null +++ b/tests/ui/const-generics/mgca/braced-const-infer.rs @@ -0,0 +1,9 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/153198 +#![feature(min_generic_const_args)] +#![allow(incomplete_features, rust_2021_compatibility)] + +trait Trait {} + +impl dyn Trait<{_}> {} //~ ERROR: the placeholder `_` is not allowed within types on item signatures + +fn main() {} diff --git a/tests/ui/const-generics/mgca/braced-const-infer.stderr b/tests/ui/const-generics/mgca/braced-const-infer.stderr new file mode 100644 index 0000000000000..d8ffee4cd0e8f --- /dev/null +++ b/tests/ui/const-generics/mgca/braced-const-infer.stderr @@ -0,0 +1,9 @@ +error[E0121]: the placeholder `_` is not allowed within types on item signatures for implementations + --> $DIR/braced-const-infer.rs:7:17 + | +LL | impl dyn Trait<{_}> {} + | ^ not allowed in type signatures + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0121`. diff --git a/tests/ui/const-generics/mgca/macro-const-arg-infer.rs b/tests/ui/const-generics/mgca/macro-const-arg-infer.rs new file mode 100644 index 0000000000000..017c63d8b0db5 --- /dev/null +++ b/tests/ui/const-generics/mgca/macro-const-arg-infer.rs @@ -0,0 +1,20 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/153198 +#![feature(min_generic_const_args)] +#![allow(incomplete_features)] +macro_rules! y { + ( $($matcher:tt)*) => { + _ //~ ERROR: the placeholder `_` is not allowed within types on item signatures + }; +} + +struct A; //~ ERROR: type parameter `T` is never used + +const y: A< + { + y! { + x + } + }, +> = 1; //~ ERROR: mismatched types + +fn main() {} diff --git a/tests/ui/const-generics/mgca/macro-const-arg-infer.stderr b/tests/ui/const-generics/mgca/macro-const-arg-infer.stderr new file mode 100644 index 0000000000000..406206c59e18f --- /dev/null +++ b/tests/ui/const-generics/mgca/macro-const-arg-infer.stderr @@ -0,0 +1,44 @@ +error[E0392]: type parameter `T` is never used + --> $DIR/macro-const-arg-infer.rs:10:10 + | +LL | struct A; + | ^ 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[E0308]: mismatched types + --> $DIR/macro-const-arg-infer.rs:18:5 + | +LL | const y: A< + | __________- +LL | | { +LL | | y! { +LL | | x +LL | | } +LL | | }, +LL | | > = 1; + | | - ^ expected `A<_>`, found integer + | |_| + | expected because of the type of the constant + | + = note: expected struct `A<_>` + found type `{integer}` + +error[E0121]: the placeholder `_` is not allowed within types on item signatures for constants + --> $DIR/macro-const-arg-infer.rs:6:9 + | +LL | _ + | ^ not allowed in type signatures +... +LL | / y! { +LL | | x +LL | | } + | |_________- in this macro invocation + | + = note: this error originates in the macro `y` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 3 previous errors + +Some errors have detailed explanations: E0121, E0308, E0392. +For more information about an error, try `rustc --explain E0121`. From 11810b8dd9d5ea1dad36eb5f6c77fa5d95871a81 Mon Sep 17 00:00:00 2001 From: human9000 Date: Sun, 8 Mar 2026 10:49:21 +0500 Subject: [PATCH 05/13] fix inference variables leaking into HIR const lowering logic --- .../src/hir_ty_lowering/mod.rs | 6 ++++++ .../mgca/infer-vars-in-const-args.rs | 16 ++++++++++++++ .../mgca/infer-vars-in-const-args.stderr | 21 +++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 tests/ui/const-generics/mgca/infer-vars-in-const-args.rs create mode 100644 tests/ui/const-generics/mgca/infer-vars-in-const-args.stderr diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index bf97bfb1ebbce..00791ed194c52 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2874,6 +2874,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, ) -> Const<'tcx> { let tcx = self.tcx(); + + if ty.has_infer() { + let e = self.dcx().span_err(span, "inference variables are not supported in constants"); + return ty::Const::new_error(tcx, e); + } + if let LitKind::Err(guar) = *kind { return ty::Const::new_error(tcx, guar); } diff --git a/tests/ui/const-generics/mgca/infer-vars-in-const-args.rs b/tests/ui/const-generics/mgca/infer-vars-in-const-args.rs new file mode 100644 index 0000000000000..cbc59d461694f --- /dev/null +++ b/tests/ui/const-generics/mgca/infer-vars-in-const-args.rs @@ -0,0 +1,16 @@ +#![allow(incomplete_features)] +#![feature(adt_const_params, min_generic_const_args, generic_const_parameter_types)] + +fn main() { + foo::<_, { 2 }>(); + //~^ ERROR: inference variables are not supported in constants + let _: PC<_, { 42 }> = PC { a: 1, b: 1 }; + //~^ ERROR: inference variables are not supported in constants +} + +struct PC { +//~^ ERROR: `T` can't be used as a const parameter type [E0741] + a: T, +} + +fn foo() {} diff --git a/tests/ui/const-generics/mgca/infer-vars-in-const-args.stderr b/tests/ui/const-generics/mgca/infer-vars-in-const-args.stderr new file mode 100644 index 0000000000000..0f64582c1cef6 --- /dev/null +++ b/tests/ui/const-generics/mgca/infer-vars-in-const-args.stderr @@ -0,0 +1,21 @@ +error[E0741]: `T` can't be used as a const parameter type + --> $DIR/infer-vars-in-const-args.rs:11:23 + | +LL | struct PC { + | ^ + +error: inference variables are not supported in constants + --> $DIR/infer-vars-in-const-args.rs:5:16 + | +LL | foo::<_, { 2 }>(); + | ^ + +error: inference variables are not supported in constants + --> $DIR/infer-vars-in-const-args.rs:7:20 + | +LL | let _: PC<_, { 42 }> = PC { a: 1, b: 1 }; + | ^^ + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0741`. From 5113488163ac3fdbb2adb665308b0bd3f524c44f Mon Sep 17 00:00:00 2001 From: human9000 Date: Tue, 17 Mar 2026 09:36:08 +0500 Subject: [PATCH 06/13] Improve `ty::Infer` handling during const literal lowering Co-authored-by: BoxyUwU --- .../src/hir_ty_lowering/mod.rs | 15 +++++++------ compiler/rustc_middle/src/ty/consts/lit.rs | 5 ++++- .../src/builder/expr/as_constant.rs | 5 ++++- compiler/rustc_mir_build/src/thir/constant.rs | 22 ++++++++++--------- .../rustc_mir_build/src/thir/pattern/mod.rs | 2 +- compiler/rustc_ty_utils/src/consts.rs | 3 ++- ...> infer-vars-in-const-args-conflicting.rs} | 9 ++++++-- ...fer-vars-in-const-args-conflicting.stderr} | 10 ++++----- .../mgca/infer-vars-in-const-args-correct.rs | 22 +++++++++++++++++++ 9 files changed, 65 insertions(+), 28 deletions(-) rename tests/ui/const-generics/mgca/{infer-vars-in-const-args.rs => infer-vars-in-const-args-conflicting.rs} (52%) rename tests/ui/const-generics/mgca/{infer-vars-in-const-args.stderr => infer-vars-in-const-args-conflicting.stderr} (58%) create mode 100644 tests/ui/const-generics/mgca/infer-vars-in-const-args-correct.rs diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 00791ed194c52..9396bf6352c59 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -2875,10 +2875,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ) -> Const<'tcx> { let tcx = self.tcx(); - if ty.has_infer() { - let e = self.dcx().span_err(span, "inference variables are not supported in constants"); - return ty::Const::new_error(tcx, e); - } + let ty = if !ty.has_infer() { Some(ty) } else { None }; if let LitKind::Err(guar) = *kind { return ty::Const::new_error(tcx, guar); @@ -2911,16 +2908,20 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { }; let lit_input = match expr.kind { - hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: lit.node, ty, neg: false }), + hir::ExprKind::Lit(lit) => { + Some(LitToConstInput { lit: lit.node, ty: Some(ty), neg: false }) + } hir::ExprKind::Unary(hir::UnOp::Neg, expr) => match expr.kind { - hir::ExprKind::Lit(lit) => Some(LitToConstInput { lit: lit.node, ty, neg: true }), + hir::ExprKind::Lit(lit) => { + Some(LitToConstInput { lit: lit.node, ty: Some(ty), neg: true }) + } _ => None, }, _ => None, }; lit_input.and_then(|l| { - if const_lit_matches_ty(tcx, &l.lit, l.ty, l.neg) { + if const_lit_matches_ty(tcx, &l.lit, ty, l.neg) { tcx.at(expr.span) .lit_to_const(l) .map(|value| ty::Const::new_value(tcx, value.valtree, value.ty)) diff --git a/compiler/rustc_middle/src/ty/consts/lit.rs b/compiler/rustc_middle/src/ty/consts/lit.rs index be6dfb20e0433..7be225a9e9215 100644 --- a/compiler/rustc_middle/src/ty/consts/lit.rs +++ b/compiler/rustc_middle/src/ty/consts/lit.rs @@ -10,7 +10,10 @@ pub struct LitToConstInput<'tcx> { /// The absolute value of the resultant constant. pub lit: LitKind, /// The type of the constant. - pub ty: Ty<'tcx>, + /// + /// `None` is used by const generics when the type of the constant is unknown, e.g. + /// if there are inference variables + pub ty: Option>, /// If the constant is negative. pub neg: bool, } diff --git a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs index ad6c1f7dce5b8..c67d99a8eb7de 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_constant.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_constant.rs @@ -50,7 +50,8 @@ pub(crate) fn as_constant_inner<'tcx>( match *kind { ExprKind::Literal { lit, neg } => { - let const_ = lit_to_mir_constant(tcx, LitToConstInput { lit: lit.node, ty, neg }); + let const_ = + lit_to_mir_constant(tcx, LitToConstInput { lit: lit.node, ty: Some(ty), neg }); ConstOperand { span, user_ty: None, const_ } } @@ -109,6 +110,8 @@ pub(crate) fn as_constant_inner<'tcx>( fn lit_to_mir_constant<'tcx>(tcx: TyCtxt<'tcx>, lit_input: LitToConstInput<'tcx>) -> Const<'tcx> { let LitToConstInput { lit, ty, neg } = lit_input; + let ty = ty.expect("type of literal must be known at this point"); + if let Err(guar) = ty.error_reported() { return Const::Ty(Ty::new_error(tcx, guar), ty::Const::new_error(tcx, guar)); } diff --git a/compiler/rustc_mir_build/src/thir/constant.rs b/compiler/rustc_mir_build/src/thir/constant.rs index b4eedb15033c8..019af24613541 100644 --- a/compiler/rustc_mir_build/src/thir/constant.rs +++ b/compiler/rustc_mir_build/src/thir/constant.rs @@ -31,25 +31,27 @@ pub(crate) fn lit_to_const<'tcx>( .unwrap_or_else(|| bug!("expected to create ScalarInt from uint {:?}", result)) }; - let (valtree, valtree_ty) = match (lit, expected_ty.kind()) { + let (valtree, valtree_ty) = match (lit, expected_ty.map(|ty| ty.kind())) { (ast::LitKind::Str(s, _), _) => { let str_bytes = s.as_str().as_bytes(); let valtree_ty = Ty::new_imm_ref(tcx, tcx.lifetimes.re_static, tcx.types.str_); (ty::ValTree::from_raw_bytes(tcx, str_bytes), valtree_ty) } - (ast::LitKind::ByteStr(byte_sym, _), ty::Ref(_, inner_ty, _)) + (ast::LitKind::ByteStr(byte_sym, _), Some(ty::Ref(_, inner_ty, _))) if let ty::Slice(ty) | ty::Array(ty, _) = inner_ty.kind() && let ty::Uint(UintTy::U8) = ty.kind() => { - (ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty) + (ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty.unwrap()) } - (ast::LitKind::ByteStr(byte_sym, _), ty::Slice(inner_ty) | ty::Array(inner_ty, _)) - if tcx.features().deref_patterns() - && let ty::Uint(UintTy::U8) = inner_ty.kind() => + ( + ast::LitKind::ByteStr(byte_sym, _), + Some(ty::Slice(inner_ty) | ty::Array(inner_ty, _)), + ) if tcx.features().deref_patterns() + && let ty::Uint(UintTy::U8) = inner_ty.kind() => { // Byte string literal patterns may have type `[u8]` or `[u8; N]` if `deref_patterns` is // enabled, in order to allow, e.g., `deref!(b"..."): Vec`. - (ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty) + (ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()), expected_ty.unwrap()) } (ast::LitKind::ByteStr(byte_sym, _), _) => { let valtree = ty::ValTree::from_raw_bytes(tcx, byte_sym.as_byte_str()); @@ -79,11 +81,11 @@ pub(crate) fn lit_to_const<'tcx>( trunc(if neg { u128::wrapping_neg(n.get()) } else { n.get() }, i.to_unsigned()); (ty::ValTree::from_scalar_int(tcx, scalar_int), Ty::new_int(tcx, i)) } - (ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), ty::Uint(ui)) if !neg => { + (ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), Some(ty::Uint(ui))) if !neg => { let scalar_int = trunc(n.get(), *ui); (ty::ValTree::from_scalar_int(tcx, scalar_int), Ty::new_uint(tcx, *ui)) } - (ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), ty::Int(i)) => { + (ast::LitKind::Int(n, ast::LitIntType::Unsuffixed), Some(ty::Int(i))) => { // Unsigned "negation" has the same bitwise effect as signed negation, // which gets the result we want without additional casts. let scalar_int = @@ -101,7 +103,7 @@ pub(crate) fn lit_to_const<'tcx>( let bits = parse_float_into_scalar(n, fty, neg)?; (ty::ValTree::from_scalar_int(tcx, bits), Ty::new_float(tcx, fty)) } - (ast::LitKind::Float(n, ast::LitFloatType::Unsuffixed), ty::Float(fty)) => { + (ast::LitKind::Float(n, ast::LitFloatType::Unsuffixed), Some(ty::Float(fty))) => { let bits = parse_float_into_scalar(n, *fty, neg)?; (ty::ValTree::from_scalar_int(tcx, bits), Ty::new_float(tcx, *fty)) } diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 67cde0e2c8866..fec2fb9ca5719 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -696,7 +696,7 @@ impl<'tcx> PatCtxt<'tcx> { // patterns to `str`, and byte-string literal patterns to `[u8; N]` or `[u8]`. let pat_ty = self.typeck_results.node_type(pat.hir_id); - let lit_input = LitToConstInput { lit: lit.node, ty: pat_ty, neg: *negated }; + let lit_input = LitToConstInput { lit: lit.node, ty: Some(pat_ty), neg: *negated }; let constant = const_lit_matches_ty(self.tcx, &lit.node, pat_ty, *negated) .then(|| self.tcx.at(expr.span).lit_to_const(lit_input)) .flatten() diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs index 1d444079f8968..49e0bdde37870 100644 --- a/compiler/rustc_ty_utils/src/consts.rs +++ b/compiler/rustc_ty_utils/src/consts.rs @@ -58,7 +58,8 @@ fn recurse_build<'tcx>( } &ExprKind::Literal { lit, neg } => { let sp = node.span; - match tcx.at(sp).lit_to_const(LitToConstInput { lit: lit.node, ty: node.ty, neg }) { + match tcx.at(sp).lit_to_const(LitToConstInput { lit: lit.node, ty: Some(node.ty), neg }) + { Some(value) => ty::Const::new_value(tcx, value.valtree, value.ty), None => ty::Const::new_misc_error(tcx), } diff --git a/tests/ui/const-generics/mgca/infer-vars-in-const-args.rs b/tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.rs similarity index 52% rename from tests/ui/const-generics/mgca/infer-vars-in-const-args.rs rename to tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.rs index cbc59d461694f..eeb76683eedf1 100644 --- a/tests/ui/const-generics/mgca/infer-vars-in-const-args.rs +++ b/tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.rs @@ -1,11 +1,16 @@ +//! This test ensures compilation failure when trying to pass literals +//! without explicitly stated type as inference variables in generic arguments. +//! +//! See https://github.com/rust-lang/rust/pull/153557 + #![allow(incomplete_features)] #![feature(adt_const_params, min_generic_const_args, generic_const_parameter_types)] fn main() { foo::<_, { 2 }>(); - //~^ ERROR: inference variables are not supported in constants + //~^ ERROR: type annotations needed for the literal let _: PC<_, { 42 }> = PC { a: 1, b: 1 }; - //~^ ERROR: inference variables are not supported in constants + //~^ ERROR: type annotations needed for the literal } struct PC { diff --git a/tests/ui/const-generics/mgca/infer-vars-in-const-args.stderr b/tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.stderr similarity index 58% rename from tests/ui/const-generics/mgca/infer-vars-in-const-args.stderr rename to tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.stderr index 0f64582c1cef6..a4b13f41aef07 100644 --- a/tests/ui/const-generics/mgca/infer-vars-in-const-args.stderr +++ b/tests/ui/const-generics/mgca/infer-vars-in-const-args-conflicting.stderr @@ -1,17 +1,17 @@ error[E0741]: `T` can't be used as a const parameter type - --> $DIR/infer-vars-in-const-args.rs:11:23 + --> $DIR/infer-vars-in-const-args-conflicting.rs:16:23 | LL | struct PC { | ^ -error: inference variables are not supported in constants - --> $DIR/infer-vars-in-const-args.rs:5:16 +error: type annotations needed for the literal + --> $DIR/infer-vars-in-const-args-conflicting.rs:10:16 | LL | foo::<_, { 2 }>(); | ^ -error: inference variables are not supported in constants - --> $DIR/infer-vars-in-const-args.rs:7:20 +error: type annotations needed for the literal + --> $DIR/infer-vars-in-const-args-conflicting.rs:12:20 | LL | let _: PC<_, { 42 }> = PC { a: 1, b: 1 }; | ^^ diff --git a/tests/ui/const-generics/mgca/infer-vars-in-const-args-correct.rs b/tests/ui/const-generics/mgca/infer-vars-in-const-args-correct.rs new file mode 100644 index 0000000000000..f1c0e0b2b4c30 --- /dev/null +++ b/tests/ui/const-generics/mgca/infer-vars-in-const-args-correct.rs @@ -0,0 +1,22 @@ +//! This test ensures no errors are emitted when lowering literals with +//! explicitly stated types and inference variables in the type of the const +//! generic parameter. +//! +//! See https://github.com/rust-lang/rust/pull/153557 + +//@check-pass + +#![allow(incomplete_features)] +#![feature(adt_const_params, + min_generic_const_args, + generic_const_parameter_types, + unsized_const_params +)] + +use std::marker::ConstParamTy_; + +fn main() { + foo::<_, 2_i32>(); +} + +fn foo() {} From b4c1677fff43d9ea04002de85aef77915092be7d Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 15 Mar 2026 20:34:25 +0900 Subject: [PATCH 07/13] Do not lint `for-loops-over-fallibles` on external macros --- .../rustc_lint/src/for_loops_over_fallibles.rs | 5 +++++ tests/crashes/147973.rs | 14 -------------- .../auxiliary/external-macro-issue-148114.rs | 13 +++++++++++++ .../external-macro-issue-148114.rs | 14 ++++++++++++++ 4 files changed, 32 insertions(+), 14 deletions(-) delete mode 100644 tests/crashes/147973.rs create mode 100644 tests/ui/lint/for-loops-over-falibles/auxiliary/external-macro-issue-148114.rs create mode 100644 tests/ui/lint/for-loops-over-falibles/external-macro-issue-148114.rs diff --git a/compiler/rustc_lint/src/for_loops_over_fallibles.rs b/compiler/rustc_lint/src/for_loops_over_fallibles.rs index 24220e035b1dd..a7c5943c250b8 100644 --- a/compiler/rustc_lint/src/for_loops_over_fallibles.rs +++ b/compiler/rustc_lint/src/for_loops_over_fallibles.rs @@ -49,6 +49,11 @@ impl<'tcx> LateLintPass<'tcx> for ForLoopsOverFallibles { fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { let Some((pat, arg)) = extract_for_loop(expr) else { return }; + // Do not put suggestions for external macros. + if pat.span.from_expansion() { + return; + } + let arg_span = arg.span.source_callsite(); let ty = cx.typeck_results().expr_ty(arg); diff --git a/tests/crashes/147973.rs b/tests/crashes/147973.rs deleted file mode 100644 index 7271c54846f15..0000000000000 --- a/tests/crashes/147973.rs +++ /dev/null @@ -1,14 +0,0 @@ -// This is part of series of regression tests for some diagnostics ICEs encountered in the wild with -// suggestions having overlapping parts under https://github.com/rust-lang/rust/pull/146121. -// This is one MCVE from the beta crater run regressions from issue 147973. - -//@ needs-rustc-debug-assertions -//@ known-bug: #147973 - -//@ aux-build: overlapping_spans_helper.rs -extern crate overlapping_spans_helper; - -fn main() { - let _name = Some(1); - overlapping_spans_helper::do_loop!(_name); -} diff --git a/tests/ui/lint/for-loops-over-falibles/auxiliary/external-macro-issue-148114.rs b/tests/ui/lint/for-loops-over-falibles/auxiliary/external-macro-issue-148114.rs new file mode 100644 index 0000000000000..68eff33cb3f10 --- /dev/null +++ b/tests/ui/lint/for-loops-over-falibles/auxiliary/external-macro-issue-148114.rs @@ -0,0 +1,13 @@ +#[macro_export] +macro_rules! identity { + ($x:ident) => { + $x + }; +} + +#[macro_export] +macro_rules! do_loop { + ($x:ident) => { + for $crate::identity!($x) in $x {} + }; +} diff --git a/tests/ui/lint/for-loops-over-falibles/external-macro-issue-148114.rs b/tests/ui/lint/for-loops-over-falibles/external-macro-issue-148114.rs new file mode 100644 index 0000000000000..ca3ac3b9a8647 --- /dev/null +++ b/tests/ui/lint/for-loops-over-falibles/external-macro-issue-148114.rs @@ -0,0 +1,14 @@ +//@ check-pass +//@ aux-build:external-macro-issue-148114.rs + +// This test ensures we do not trigger the lint on external macros +// ref. + +#![deny(for_loops_over_fallibles)] + +extern crate external_macro_issue_148114 as dep; + +fn main() { + let _name = Some(1); + dep::do_loop!(_name); +} From cfe18cfd37e3aefe0a9a653d3faac825c3a7868a Mon Sep 17 00:00:00 2001 From: Yuki Okushi Date: Sun, 15 Mar 2026 20:35:16 +0900 Subject: [PATCH 08/13] Correct dir name --- .../auxiliary/external-macro-issue-148114.rs | 0 .../external-macro-issue-148114.rs | 0 .../macro-issue-140747.rs | 0 .../macro-issue-140747.stderr | 0 .../macro-iterator-next.rs | 0 .../macro-iterator-next.stderr | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/lint/{for-loops-over-falibles => for-loops-over-fallibles}/auxiliary/external-macro-issue-148114.rs (100%) rename tests/ui/lint/{for-loops-over-falibles => for-loops-over-fallibles}/external-macro-issue-148114.rs (100%) rename tests/ui/lint/{for-loops-over-falibles => for-loops-over-fallibles}/macro-issue-140747.rs (100%) rename tests/ui/lint/{for-loops-over-falibles => for-loops-over-fallibles}/macro-issue-140747.stderr (100%) rename tests/ui/lint/{for-loops-over-falibles => for-loops-over-fallibles}/macro-iterator-next.rs (100%) rename tests/ui/lint/{for-loops-over-falibles => for-loops-over-fallibles}/macro-iterator-next.stderr (100%) diff --git a/tests/ui/lint/for-loops-over-falibles/auxiliary/external-macro-issue-148114.rs b/tests/ui/lint/for-loops-over-fallibles/auxiliary/external-macro-issue-148114.rs similarity index 100% rename from tests/ui/lint/for-loops-over-falibles/auxiliary/external-macro-issue-148114.rs rename to tests/ui/lint/for-loops-over-fallibles/auxiliary/external-macro-issue-148114.rs diff --git a/tests/ui/lint/for-loops-over-falibles/external-macro-issue-148114.rs b/tests/ui/lint/for-loops-over-fallibles/external-macro-issue-148114.rs similarity index 100% rename from tests/ui/lint/for-loops-over-falibles/external-macro-issue-148114.rs rename to tests/ui/lint/for-loops-over-fallibles/external-macro-issue-148114.rs diff --git a/tests/ui/lint/for-loops-over-falibles/macro-issue-140747.rs b/tests/ui/lint/for-loops-over-fallibles/macro-issue-140747.rs similarity index 100% rename from tests/ui/lint/for-loops-over-falibles/macro-issue-140747.rs rename to tests/ui/lint/for-loops-over-fallibles/macro-issue-140747.rs diff --git a/tests/ui/lint/for-loops-over-falibles/macro-issue-140747.stderr b/tests/ui/lint/for-loops-over-fallibles/macro-issue-140747.stderr similarity index 100% rename from tests/ui/lint/for-loops-over-falibles/macro-issue-140747.stderr rename to tests/ui/lint/for-loops-over-fallibles/macro-issue-140747.stderr diff --git a/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.rs b/tests/ui/lint/for-loops-over-fallibles/macro-iterator-next.rs similarity index 100% rename from tests/ui/lint/for-loops-over-falibles/macro-iterator-next.rs rename to tests/ui/lint/for-loops-over-fallibles/macro-iterator-next.rs diff --git a/tests/ui/lint/for-loops-over-falibles/macro-iterator-next.stderr b/tests/ui/lint/for-loops-over-fallibles/macro-iterator-next.stderr similarity index 100% rename from tests/ui/lint/for-loops-over-falibles/macro-iterator-next.stderr rename to tests/ui/lint/for-loops-over-fallibles/macro-iterator-next.stderr From 38aa32217bdfab5bf585bf2b6c5bf54a053e4810 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 18 Mar 2026 08:32:52 +0100 Subject: [PATCH 09/13] borrowck/type_check: remove helper left-over from unsized locals --- compiler/rustc_borrowck/src/type_check/mod.rs | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 9aa8f7a2067a5..47f14dc3df62e 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -374,10 +374,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { self.body } - fn unsized_feature_enabled(&self) -> bool { - self.tcx().features().unsized_fn_params() - } - /// Equate the inferred type and the annotated type for user type annotations #[instrument(skip(self), level = "debug")] fn check_user_type_annotations(&mut self) { @@ -660,7 +656,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { ); } - if !self.unsized_feature_enabled() { + if !self.tcx().features().unsized_fn_params() { let trait_ref = ty::TraitRef::new( tcx, tcx.require_lang_item(LangItem::Sized, self.last_span), @@ -936,9 +932,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } } - // When `unsized_fn_params` is enabled, only function calls - // and nullary ops are checked in `check_call_dest`. - if !self.unsized_feature_enabled() { + // When `unsized_fn_params` is enabled, this is checked in `check_call_dest`, + // and `hir_typeck` still forces all non-argument locals to be sized (i.e., we don't + // fully re-check what was already checked on HIR). + if !self.tcx().features().unsized_fn_params() { match self.body.local_kind(local) { LocalKind::ReturnPointer | LocalKind::Arg => { // return values of normal functions are required to be @@ -1953,8 +1950,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } // When `unsized_fn_params` is not enabled, - // this check is done at `check_local`. - if self.unsized_feature_enabled() { + // this check is done at `visit_local_decl`. + if self.tcx().features().unsized_fn_params() { let span = term.source_info.span; self.ensure_place_sized(dest_ty, span); } From 6b0fecf582cc6788d0f246a2d7e5a7471462c506 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Wed, 18 Mar 2026 09:37:35 +0000 Subject: [PATCH 10/13] merge `regions-outlives-nominal-type-*` tests into one file --- ...s-outlives-nominal-type-enum-region-rev.rs | 20 ----- ...gions-outlives-nominal-type-enum-region.rs | 20 ----- ...ons-outlives-nominal-type-enum-type-rev.rs | 20 ----- ...regions-outlives-nominal-type-enum-type.rs | 20 ----- ...outlives-nominal-type-struct-region-rev.rs | 20 ----- ...ons-outlives-nominal-type-struct-region.rs | 20 ----- ...s-outlives-nominal-type-struct-type-rev.rs | 20 ----- ...gions-outlives-nominal-type-struct-type.rs | 20 ----- .../regions/regions-outlives-nominal-type.rs | 80 +++++++++++++++++++ 9 files changed, 80 insertions(+), 160 deletions(-) delete mode 100644 tests/ui/regions/regions-outlives-nominal-type-enum-region-rev.rs delete mode 100644 tests/ui/regions/regions-outlives-nominal-type-enum-region.rs delete mode 100644 tests/ui/regions/regions-outlives-nominal-type-enum-type-rev.rs delete mode 100644 tests/ui/regions/regions-outlives-nominal-type-enum-type.rs delete mode 100644 tests/ui/regions/regions-outlives-nominal-type-struct-region-rev.rs delete mode 100644 tests/ui/regions/regions-outlives-nominal-type-struct-region.rs delete mode 100644 tests/ui/regions/regions-outlives-nominal-type-struct-type-rev.rs delete mode 100644 tests/ui/regions/regions-outlives-nominal-type-struct-type.rs create mode 100644 tests/ui/regions/regions-outlives-nominal-type.rs diff --git a/tests/ui/regions/regions-outlives-nominal-type-enum-region-rev.rs b/tests/ui/regions/regions-outlives-nominal-type-enum-region-rev.rs deleted file mode 100644 index 3d4a305078843..0000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-enum-region-rev.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod rev_variant_struct_region { - struct Foo<'a> { - x: fn(&'a i32), - } - enum Bar<'a,'b> { - V(&'a Foo<'b>) - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type-enum-region.rs b/tests/ui/regions/regions-outlives-nominal-type-enum-region.rs deleted file mode 100644 index 2e0d1b36ca59e..0000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-enum-region.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod variant_struct_region { - struct Foo<'a> { - x: &'a i32, - } - enum Bar<'a,'b> { - V(&'a Foo<'b>) - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type-enum-type-rev.rs b/tests/ui/regions/regions-outlives-nominal-type-enum-type-rev.rs deleted file mode 100644 index baf7874bc1a61..0000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-enum-type-rev.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod variant_struct_type { - struct Foo { - x: fn(T) - } - enum Bar<'a,'b> { - V(&'a Foo<&'b i32>) - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type-enum-type.rs b/tests/ui/regions/regions-outlives-nominal-type-enum-type.rs deleted file mode 100644 index b8392c967b10a..0000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-enum-type.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod variant_struct_type { - struct Foo { - x: T - } - enum Bar<'a,'b> { - V(&'a Foo<&'b i32>) - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type-struct-region-rev.rs b/tests/ui/regions/regions-outlives-nominal-type-struct-region-rev.rs deleted file mode 100644 index 6a50248cb70b0..0000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-struct-region-rev.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod rev_variant_struct_region { - struct Foo<'a> { - x: fn(&'a i32), - } - struct Bar<'a,'b> { - f: &'a Foo<'b> - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type-struct-region.rs b/tests/ui/regions/regions-outlives-nominal-type-struct-region.rs deleted file mode 100644 index 17564bcbf269f..0000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-struct-region.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod variant_struct_region { - struct Foo<'a> { - x: &'a i32, - } - struct Bar<'a,'b> { - f: &'a Foo<'b> - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type-struct-type-rev.rs b/tests/ui/regions/regions-outlives-nominal-type-struct-type-rev.rs deleted file mode 100644 index 33961de7d6a45..0000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-struct-type-rev.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod rev_variant_struct_type { - struct Foo { - x: fn(T) - } - struct Bar<'a,'b> { - f: &'a Foo<&'b i32> - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type-struct-type.rs b/tests/ui/regions/regions-outlives-nominal-type-struct-type.rs deleted file mode 100644 index c5238086fc05d..0000000000000 --- a/tests/ui/regions/regions-outlives-nominal-type-struct-type.rs +++ /dev/null @@ -1,20 +0,0 @@ -// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its -// arguments (like `'a`) outlive `'b`. -// -// Rule OutlivesNominalType from RFC 1214. - -//@ check-pass - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -mod variant_struct_type { - struct Foo { - x: T - } - struct Bar<'a,'b> { - f: &'a Foo<&'b i32> - } -} - -fn main() { } diff --git a/tests/ui/regions/regions-outlives-nominal-type.rs b/tests/ui/regions/regions-outlives-nominal-type.rs new file mode 100644 index 0000000000000..177d064042dd8 --- /dev/null +++ b/tests/ui/regions/regions-outlives-nominal-type.rs @@ -0,0 +1,80 @@ +// Test that a nominal type (like `Foo<'a>`) outlives `'b` if its +// arguments (like `'a`) outlive `'b`. +// +// Rule OutlivesNominalType from RFC 1214. + +//@ check-pass + +mod variant_enum_region { + struct Foo<'a> { + x: &'a i32, + } + enum Bar<'a, 'b> { + V(&'a Foo<'b>), + } +} + +mod rev_variant_enum_region { + struct Foo<'a> { + x: fn(&'a i32), + } + enum Bar<'a, 'b> { + V(&'a Foo<'b>), + } +} + +mod variant_enum_type { + struct Foo { + x: T, + } + enum Bar<'a, 'b> { + V(&'a Foo<&'b i32>), + } +} + +mod rev_variant_enum_type { + struct Foo { + x: fn(T), + } + enum Bar<'a, 'b> { + V(&'a Foo<&'b i32>), + } +} + +mod variant_struct_region { + struct Foo<'a> { + x: &'a i32, + } + struct Bar<'a, 'b> { + f: &'a Foo<'b>, + } +} + +mod rev_variant_struct_region { + struct Foo<'a> { + x: fn(&'a i32), + } + struct Bar<'a, 'b> { + f: &'a Foo<'b>, + } +} + +mod variant_struct_type { + struct Foo { + x: T, + } + struct Bar<'a, 'b> { + f: &'a Foo<&'b i32>, + } +} + +mod rev_variant_struct_type { + struct Foo { + x: fn(T), + } + struct Bar<'a, 'b> { + f: &'a Foo<&'b i32>, + } +} + +fn main() {} From c8a2c46eda3d8a8cc0dbe92002543ab5867586a3 Mon Sep 17 00:00:00 2001 From: Jynn Nelson Date: Mon, 16 Mar 2026 14:33:39 +0100 Subject: [PATCH 11/13] bootstrap: Optionally print a backtrace if a command fails --- src/bootstrap/src/utils/exec.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs index b264c961b6593..f9b0d2dbb2095 100644 --- a/src/bootstrap/src/utils/exec.rs +++ b/src/bootstrap/src/utils/exec.rs @@ -7,6 +7,7 @@ //! relevant to command execution in the bootstrap process. This includes settings such as //! dry-run mode, verbosity level, and failure behavior. +use std::backtrace::{Backtrace, BacktraceStatus}; use std::collections::HashMap; use std::ffi::{OsStr, OsString}; use std::fmt::{Debug, Formatter}; @@ -930,6 +931,16 @@ Executed at: {executed_at}"#, if stderr.captures() { writeln!(error_message, "\n--- STDERR vvv\n{}", output.stderr().trim()).unwrap(); } + let backtrace = if exec_ctx.verbosity > 1 { + Backtrace::force_capture() + } else if matches!(command.failure_behavior, BehaviorOnFailure::Ignore) { + Backtrace::disabled() + } else { + Backtrace::capture() + }; + if matches!(backtrace.status(), BacktraceStatus::Captured) { + writeln!(error_message, "\n--- BACKTRACE vvv\n{backtrace}").unwrap(); + } match command.failure_behavior { BehaviorOnFailure::DelayFail => { From a085b68fbdb3680ec73756841f0c9cccd420fb05 Mon Sep 17 00:00:00 2001 From: xonx <119700621+xonx4l@users.noreply.github.com> Date: Wed, 18 Mar 2026 16:39:40 +0000 Subject: [PATCH 12/13] Add bootstrap step for stdarch-verify * add bootstrap for stdarch-verify * update bootstrap snapshot * make default * update snapshots --- src/bootstrap/src/core/build_steps/test.rs | 52 +++++++++++++++++++ .../builder/cli_paths/snapshots/x_test.snap | 3 ++ .../cli_paths/snapshots/x_test_library.snap | 3 ++ .../snapshots/x_test_skip_coverage.snap | 3 ++ .../snapshots/x_test_skip_tests.snap | 3 ++ src/bootstrap/src/core/builder/mod.rs | 1 + 6 files changed, 65 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 7be45c5fa9d40..397bb7054a617 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -903,6 +903,58 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the } } +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct StdarchVerify { + build_compiler: Compiler, + target: TargetSelection, +} + +impl Step for StdarchVerify { + type Output = (); + const IS_HOST: bool = true; + + fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> { + run.path("library/stdarch/crates/stdarch-verify") + } + + fn is_default_step(_builder: &Builder<'_>) -> bool { + true + } + + fn make_run(run: RunConfig<'_>) { + let builder = run.builder; + let build_compiler = get_compiler_to_test(builder, run.target); + builder.ensure(StdarchVerify { build_compiler, target: run.target }); + } + + fn run(self, builder: &Builder<'_>) { + let build_compiler = self.build_compiler; + let target = self.target; + + builder.std(build_compiler, target); + + let cargo = tool::prepare_tool_cargo( + builder, + build_compiler, + Mode::ToolStd, + target, + Kind::Test, + "library/stdarch/crates/stdarch-verify", + SourceType::InTree, + &[], + ); + + run_cargo_test( + cargo, + &[], + &["stdarch-verify".to_string()], + Some("stdarch-verify"), + target, + builder, + ); + } +} + #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Clippy { compilers: RustcPrivateCompilers, diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap index ac2f315d39d96..e4c467161ba1a 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test.snap @@ -182,6 +182,9 @@ expression: test [Test] test::RustcBook targets: [x86_64-unknown-linux-gnu] - Set({test::src/doc/rustc}) +[Test] test::StdarchVerify + targets: [x86_64-unknown-linux-gnu] + - Set({test::library/stdarch/crates/stdarch-verify}) [Test] test::RustdocJSStd targets: [x86_64-unknown-linux-gnu] - Suite(test::tests/rustdoc-js-std) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_library.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_library.snap index dfc397597a877..f97bb839c1e73 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_library.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_library.snap @@ -18,3 +18,6 @@ expression: test library - Set({test::library/sysroot}) - Set({test::library/test}) - Set({test::library/unwind}) +[Test] test::StdarchVerify + targets: [x86_64-unknown-linux-gnu] + - Set({test::library/stdarch/crates/stdarch-verify}) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap index 09adbb0041ae6..5ff4ea089a539 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_coverage.snap @@ -181,6 +181,9 @@ expression: test --skip=coverage [Test] test::RustcBook targets: [x86_64-unknown-linux-gnu] - Set({test::src/doc/rustc}) +[Test] test::StdarchVerify + targets: [x86_64-unknown-linux-gnu] + - Set({test::library/stdarch/crates/stdarch-verify}) [Test] test::RustdocJSStd targets: [x86_64-unknown-linux-gnu] - Suite(test::tests/rustdoc-js-std) diff --git a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap index b5fccfcb966bb..fbbe2d5979a7b 100644 --- a/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap +++ b/src/bootstrap/src/core/builder/cli_paths/snapshots/x_test_skip_tests.snap @@ -145,6 +145,9 @@ expression: test --skip=tests [Test] test::RustcBook targets: [x86_64-unknown-linux-gnu] - Set({test::src/doc/rustc}) +[Test] test::StdarchVerify + targets: [x86_64-unknown-linux-gnu] + - Set({test::library/stdarch/crates/stdarch-verify}) [Test] test::RustdocTheme targets: [x86_64-unknown-linux-gnu] - Set({test::src/tools/rustdoc-themes}) diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs index ae91b20406295..222c4f39341f6 100644 --- a/src/bootstrap/src/core/builder/mod.rs +++ b/src/bootstrap/src/core/builder/mod.rs @@ -894,6 +894,7 @@ impl<'a> Builder<'a> { test::CargoMiri, test::Clippy, test::CompiletestTest, + test::StdarchVerify, test::CrateRunMakeSupport, test::CrateBuildHelper, test::RustdocJSStd, From 49bb371ca74904400330e065dd847b6b9390b2a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 11 Mar 2026 16:39:03 +0000 Subject: [PATCH 13/13] When single impl can satisfy inference error, suggest type When encountering an inference error where a return type must be known, like when calling `Iterator::sum::()` without specifying `T`, look for all `T` that would satisfy `Sum`. If only one, suggest it. ``` error[E0283]: type annotations needed --> $DIR/cannot-infer-iterator-sum-return-type.rs:4:9 | LL | let sum = v | ^^^ ... LL | .sum(); // `sum::` needs `T` to be specified | --- type must be known at this point | = note: cannot satisfy `_: Sum` help: the trait `Sum` is implemented for `i32` --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL | = note: in this macro invocation note: required by a bound in `std::iter::Iterator::sum` --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL = note: this error originates in the macro `integer_sum_product` (in Nightly builds, run with -Z macro-backtrace for more info) help: consider giving `sum` an explicit type, where the type for type parameter `S` is specified | LL | let sum: i32 = v | +++++ ``` --- .../error_reporting/infer/need_type_info.rs | 64 ++++++++++++++++--- .../src/error_reporting/traits/ambiguity.rs | 27 +++++++- .../traits/fulfillment_errors.rs | 10 +++ .../const-generics/issues/issue-83249.stderr | 6 +- ...annot-infer-iterator-sum-return-type.fixed | 13 ++++ .../cannot-infer-iterator-sum-return-type.rs | 13 ++++ ...nnot-infer-iterator-sum-return-type.stderr | 26 ++++++++ 7 files changed, 145 insertions(+), 14 deletions(-) create mode 100644 tests/ui/inference/cannot-infer-iterator-sum-return-type.fixed create mode 100644 tests/ui/inference/cannot-infer-iterator-sum-return-type.rs create mode 100644 tests/ui/inference/cannot-infer-iterator-sum-return-type.stderr diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index 5cb8922574b61..7fcaea3a629f1 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -158,6 +158,7 @@ impl UnderspecifiedArgKind { struct ClosureEraser<'a, 'tcx> { infcx: &'a InferCtxt<'tcx>, + depth: usize, } impl<'a, 'tcx> ClosureEraser<'a, 'tcx> { @@ -172,7 +173,8 @@ impl<'a, 'tcx> TypeFolder> for ClosureEraser<'a, 'tcx> { } fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind() { + self.depth += 1; + let ty = match ty.kind() { ty::Closure(_, args) => { // For a closure type, we turn it into a function pointer so that it gets rendered // as `fn(args) -> Ret`. @@ -233,9 +235,15 @@ impl<'a, 'tcx> TypeFolder> for ClosureEraser<'a, 'tcx> { // its type parameters. ty.super_fold_with(self) } - // We don't have an unknown type parameter anywhere, replace with `_`. + // We are in the top-level type, not one of its type parameters. Name it with its + // parameters replaced. + _ if self.depth == 1 => ty.super_fold_with(self), + // We don't have an unknown type parameter anywhere, and we are in a type parameter. + // Replace with `_`. _ => self.new_infer(), - } + }; + self.depth -= 1; + ty } fn fold_const(&mut self, c: ty::Const<'tcx>) -> ty::Const<'tcx> { @@ -287,7 +295,7 @@ fn ty_to_string<'tcx>( let ty = infcx.resolve_vars_if_possible(ty); // We use `fn` ptr syntax for closures, but this only works when the closure does not capture // anything. We also remove all type parameters that are fully known to the type system. - let ty = ty.fold_with(&mut ClosureEraser { infcx }); + let ty = ty.fold_with(&mut ClosureEraser { infcx, depth: 0 }); match (ty.kind(), called_method_def_id) { // We don't want the regular output for `fn`s because it includes its path in @@ -467,6 +475,25 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { term: Term<'tcx>, error_code: TypeAnnotationNeeded, should_label_span: bool, + ) -> Diag<'a> { + self.emit_inference_failure_err_with_type_hint( + body_def_id, + failure_span, + term, + error_code, + should_label_span, + None, + ) + } + + pub fn emit_inference_failure_err_with_type_hint( + &self, + body_def_id: LocalDefId, + failure_span: Span, + term: Term<'tcx>, + error_code: TypeAnnotationNeeded, + should_label_span: bool, + ty: Option>, ) -> Diag<'a> { let term = self.resolve_vars_if_possible(term); let arg_data = self @@ -479,7 +506,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return self.bad_inference_failure_err(failure_span, arg_data, error_code); }; - let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term); + let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term, ty); if let Some(body) = self.tcx.hir_maybe_body_owned_by( self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(), ) { @@ -779,10 +806,20 @@ impl<'tcx> InferSourceKind<'tcx> { | InferSourceKind::ClosureReturn { ty, .. } => { if ty.is_closure() { ("closure", closure_as_fn_str(infcx, ty), long_ty_path) - } else if !ty.is_ty_or_numeric_infer() { - ("normal", infcx.tcx.short_string(ty, &mut long_ty_path), long_ty_path) - } else { + } else if ty.is_ty_or_numeric_infer() + || ty.is_primitive() + || matches!( + ty.kind(), + ty::Adt(_, args) + if args.types().count() == 0 && args.consts().count() == 0 + ) + { + // `ty` is either `_`, a primitive type like `u32` or a type with no type or + // const parameters. We will not mention the type in the main inference error + // message. ("other", String::new(), long_ty_path) + } else { + ("normal", infcx.tcx.short_string(ty, &mut long_ty_path), long_ty_path) } } // FIXME: We should be able to add some additional info here. @@ -815,6 +852,7 @@ struct FindInferSourceVisitor<'a, 'tcx> { typeck_results: &'a TypeckResults<'tcx>, target: Term<'tcx>, + ty: Option>, attempt: usize, infer_source_cost: usize, @@ -826,12 +864,14 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { tecx: &'a TypeErrCtxt<'a, 'tcx>, typeck_results: &'a TypeckResults<'tcx>, target: Term<'tcx>, + ty: Option>, ) -> Self { FindInferSourceVisitor { tecx, typeck_results, target, + ty, attempt: 0, infer_source_cost: usize::MAX, @@ -1213,7 +1253,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { fn visit_local(&mut self, local: &'tcx LetStmt<'tcx>) { intravisit::walk_local(self, local); - if let Some(ty) = self.opt_node_type(local.hir_id) { + if let Some(mut ty) = self.opt_node_type(local.hir_id) { if self.generic_arg_contains_target(ty.into()) { fn get_did( typeck_results: &TypeckResults<'_>, @@ -1241,7 +1281,11 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> { _ => None, } } - + if let Some(t) = self.ty + && ty.has_infer() + { + ty = t; + } if let LocalSource::Normal = local.source && local.ty.is_none() { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 7de8891196d85..7f5ed9ecb6d11 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -246,12 +246,37 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .find(|s| s.has_non_region_infer()); let mut err = if let Some(term) = term { - self.emit_inference_failure_err( + let candidates: Vec<_> = self + .tcx + .all_impls(trait_pred.def_id()) + .filter_map(|def_id| { + let imp = self.tcx.impl_trait_header(def_id); + if imp.polarity != ty::ImplPolarity::Positive + || !self.tcx.is_user_visible_dep(def_id.krate) + { + return None; + } + let imp = imp.trait_ref.skip_binder(); + if imp + .with_replaced_self_ty(self.tcx, trait_pred.skip_binder().self_ty()) + == trait_pred.skip_binder().trait_ref + { + Some(imp.self_ty()) + } else { + None + } + }) + .collect(); + self.emit_inference_failure_err_with_type_hint( obligation.cause.body_id, span, term, TypeAnnotationNeeded::E0283, true, + match &candidates[..] { + [candidate] => Some(*candidate), + _ => None, + }, ) } else { struct_span_code_err!( diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d5383fd4d0831..6bf08170bcf7e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -2251,6 +2251,16 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if candidates.is_empty() { return false; } + let mut specific_candidates = candidates.clone(); + specific_candidates.retain(|(tr, _)| { + tr.with_replaced_self_ty(self.tcx, trait_pred.skip_binder().self_ty()) + == trait_pred.skip_binder().trait_ref + }); + if !specific_candidates.is_empty() { + // We have found a subset of impls that fully satisfy the expected trait, only + // mention those types. + candidates = specific_candidates; + } if let &[(cand, def_id)] = &candidates[..] { if self.tcx.is_diagnostic_item(sym::FromResidual, cand.def_id) && !self.tcx.features().enabled(sym::try_trait_v2) diff --git a/tests/ui/const-generics/issues/issue-83249.stderr b/tests/ui/const-generics/issues/issue-83249.stderr index 2668348613a40..0f00f70700f52 100644 --- a/tests/ui/const-generics/issues/issue-83249.stderr +++ b/tests/ui/const-generics/issues/issue-83249.stderr @@ -17,10 +17,10 @@ note: required by a bound in `foo` | LL | fn foo(_: [u8; T::N]) -> T { | ^^^ required by this bound in `foo` -help: consider giving this pattern a type +help: consider giving this pattern a type, where the type for type parameter `T` is specified | -LL | let _: /* Type */ = foo([0; 1]); - | ++++++++++++ +LL | let _: u8 = foo([0; 1]); + | ++++ error: aborting due to 1 previous error diff --git a/tests/ui/inference/cannot-infer-iterator-sum-return-type.fixed b/tests/ui/inference/cannot-infer-iterator-sum-return-type.fixed new file mode 100644 index 0000000000000..9a494391f0e4f --- /dev/null +++ b/tests/ui/inference/cannot-infer-iterator-sum-return-type.fixed @@ -0,0 +1,13 @@ +//@ run-rustfix +fn main() { + let v = vec![1, 2]; + let sum: i32 = v //~ ERROR: type annotations needed + .iter() + .map(|val| *val) + .sum(); // `sum::` needs `T` to be specified + // In this case any integer would fit, but we resolve to `i32` because that's what `{integer}` + // got coerced to. If the user needs further hinting that they can change the integer type, that + // can come from other suggestions. (#100802) + let bool = sum > 0; + assert_eq!(bool, true); +} diff --git a/tests/ui/inference/cannot-infer-iterator-sum-return-type.rs b/tests/ui/inference/cannot-infer-iterator-sum-return-type.rs new file mode 100644 index 0000000000000..013a2f9148586 --- /dev/null +++ b/tests/ui/inference/cannot-infer-iterator-sum-return-type.rs @@ -0,0 +1,13 @@ +//@ run-rustfix +fn main() { + let v = vec![1, 2]; + let sum = v //~ ERROR: type annotations needed + .iter() + .map(|val| *val) + .sum(); // `sum::` needs `T` to be specified + // In this case any integer would fit, but we resolve to `i32` because that's what `{integer}` + // got coerced to. If the user needs further hinting that they can change the integer type, that + // can come from other suggestions. (#100802) + let bool = sum > 0; + assert_eq!(bool, true); +} diff --git a/tests/ui/inference/cannot-infer-iterator-sum-return-type.stderr b/tests/ui/inference/cannot-infer-iterator-sum-return-type.stderr new file mode 100644 index 0000000000000..594b6f0181db0 --- /dev/null +++ b/tests/ui/inference/cannot-infer-iterator-sum-return-type.stderr @@ -0,0 +1,26 @@ +error[E0283]: type annotations needed + --> $DIR/cannot-infer-iterator-sum-return-type.rs:4:9 + | +LL | let sum = v + | ^^^ +... +LL | .sum(); // `sum::` needs `T` to be specified + | --- type must be known at this point + | + = note: the type must implement `Sum` +help: the trait `Sum` is implemented for `i32` + --> $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL + ::: $SRC_DIR/core/src/iter/traits/accum.rs:LL:COL + | + = note: in this macro invocation +note: required by a bound in `std::iter::Iterator::sum` + --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL + = note: this error originates in the macro `integer_sum_product` (in Nightly builds, run with -Z macro-backtrace for more info) +help: consider giving `sum` an explicit type, where the type for type parameter `S` is specified + | +LL | let sum: i32 = v + | +++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0283`.