From 5dfe7842b5a8bd8abbd833f56dc9407100a827fa Mon Sep 17 00:00:00 2001 From: Qai Juang <237468078+qaijuang@users.noreply.github.com> Date: Fri, 1 May 2026 13:53:43 -0400 Subject: [PATCH 01/13] diagnostics: print HRTB binders before fn qualifiers --- .../src/error_reporting/infer/mod.rs | 55 ++++++++++--------- .../fn-pointer-mismatch-diagnostics.rs | 12 ++++ .../fn-pointer-mismatch-diagnostics.stderr | 51 ++++++++++++++++- 3 files changed, 90 insertions(+), 28 deletions(-) diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs index 21951cee6d5ab..887b0804c2296 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs @@ -779,29 +779,40 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { let (lt1, sig1) = get_lifetimes(sig1); let (lt2, sig2) = get_lifetimes(sig2); - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T + // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T let mut values = (DiagStyledString::normal("".to_string()), DiagStyledString::normal("".to_string())); - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - // ^^^^^^ - let safety = |fn_def, sig: ty::FnSig<'_>| match fn_def { - None => sig.safety().prefix_str(), + // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^^^^^^^^^^^^^ + let fn_item_prefix_and_safety = |fn_def, sig: ty::FnSig<'_>| match fn_def { + None => ("", sig.safety().prefix_str()), Some((did, _)) => { if self.tcx.codegen_fn_attrs(did).safe_target_features { - "#[target_features] " + ("#[target_features] ", "") } else { - sig.safety().prefix_str() + ("", sig.safety().prefix_str()) } } }; - let safety1 = safety(fn_def1, sig1); - let safety2 = safety(fn_def2, sig2); + let (prefix1, safety1) = fn_item_prefix_and_safety(fn_def1, sig1); + let (prefix2, safety2) = fn_item_prefix_and_safety(fn_def2, sig2); + values.0.push(prefix1, prefix1 != prefix2); + values.1.push(prefix2, prefix1 != prefix2); + + // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^^^ + let lifetime_diff = lt1 != lt2; + values.0.push(lt1, lifetime_diff); + values.1.push(lt2, lifetime_diff); + + // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^ values.0.push(safety1, safety1 != safety2); values.1.push(safety2, safety1 != safety2); - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - // ^^^^^^^^^^ + // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^^^^^ if sig1.abi() != ExternAbi::Rust { values.0.push(format!("extern {} ", sig1.abi()), sig1.abi() != sig2.abi()); } @@ -809,19 +820,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { values.1.push(format!("extern {} ", sig2.abi()), sig1.abi() != sig2.abi()); } - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - // ^^^^^^^^ - let lifetime_diff = lt1 != lt2; - values.0.push(lt1, lifetime_diff); - values.1.push(lt2, lifetime_diff); - - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - // ^^^ + // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^ values.0.push_normal("fn("); values.1.push_normal("fn("); - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - // ^^^^^ + // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^ let len1 = sig1.inputs().len(); let len2 = sig2.inputs().len(); if len1 == len2 { @@ -859,13 +864,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { values.1.push("...", !sig1.c_variadic()); } - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - // ^ + // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^ values.0.push_normal(")"); values.1.push_normal(")"); - // unsafe extern "C" for<'a> fn(&'a T) -> &'a T - // ^^^^^^^^ + // #[target_features] for<'a> unsafe extern "C" fn(&'a T) -> &'a T + // ^^^^^^^^ let output1 = sig1.output(); let output2 = sig2.output(); let (x1, x2) = self.cmp(output1, output2); diff --git a/tests/ui/mismatched_types/fn-pointer-mismatch-diagnostics.rs b/tests/ui/mismatched_types/fn-pointer-mismatch-diagnostics.rs index e28ca3e55b5ed..e32a4792367d5 100644 --- a/tests/ui/mismatched_types/fn-pointer-mismatch-diagnostics.rs +++ b/tests/ui/mismatched_types/fn-pointer-mismatch-diagnostics.rs @@ -10,7 +10,19 @@ fn foo(x: i32) -> u32 { 0 } +extern "C" fn extern_foo(_: &i32) {} + +unsafe extern "C" fn unsafe_extern_foo(_: &i32) {} + +fn rust_foo(_: &i32) {} + fn main() { let b: fn() -> u32 = bar; //~ ERROR mismatched types [E0308] let f: fn(i32) = foo; //~ ERROR mismatched types [E0308] + + // See https://github.com/rust-lang/rust/issues/151393 + let _: for<'a> fn(&'a i32) = extern_foo; //~ ERROR mismatched types [E0308] + let _: for<'a> fn(&'a i32) = unsafe_extern_foo; //~ ERROR mismatched types [E0308] + let _: for<'a> extern "C" fn(&'a i32) = rust_foo; //~ ERROR mismatched types [E0308] + let _: for<'a> unsafe extern "C" fn(&'a i32) = rust_foo; //~ ERROR mismatched types [E0308] } diff --git a/tests/ui/mismatched_types/fn-pointer-mismatch-diagnostics.stderr b/tests/ui/mismatched_types/fn-pointer-mismatch-diagnostics.stderr index 8d63f2ea2d3c0..7158143679465 100644 --- a/tests/ui/mismatched_types/fn-pointer-mismatch-diagnostics.stderr +++ b/tests/ui/mismatched_types/fn-pointer-mismatch-diagnostics.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/fn-pointer-mismatch-diagnostics.rs:14:26 + --> $DIR/fn-pointer-mismatch-diagnostics.rs:20:26 | LL | let b: fn() -> u32 = bar; | ----------- ^^^ expected fn pointer, found fn item @@ -10,7 +10,7 @@ LL | let b: fn() -> u32 = bar; found fn item `fn() -> () {bar}` error[E0308]: mismatched types - --> $DIR/fn-pointer-mismatch-diagnostics.rs:15:22 + --> $DIR/fn-pointer-mismatch-diagnostics.rs:21:22 | LL | let f: fn(i32) = foo; | ------- ^^^ expected fn pointer, found fn item @@ -20,6 +20,51 @@ LL | let f: fn(i32) = foo; = note: expected fn pointer `fn(_) -> ()` found fn item `fn(_) -> u32 {foo}` -error: aborting due to 2 previous errors +error[E0308]: mismatched types + --> $DIR/fn-pointer-mismatch-diagnostics.rs:24:34 + | +LL | let _: for<'a> fn(&'a i32) = extern_foo; + | ------------------- ^^^^^^^^^^ expected "Rust" fn, found "C" fn + | | + | expected due to this + | + = note: expected fn pointer `for<'a> fn(&'a _)` + found fn item `for<'a> extern "C" fn(&'a _) {extern_foo}` + +error[E0308]: mismatched types + --> $DIR/fn-pointer-mismatch-diagnostics.rs:25:34 + | +LL | let _: for<'a> fn(&'a i32) = unsafe_extern_foo; + | ------------------- ^^^^^^^^^^^^^^^^^ expected safe fn, found unsafe fn + | | + | expected due to this + | + = note: expected fn pointer `for<'a> fn(&'a _)` + found fn item `for<'a> unsafe extern "C" fn(&'a _) {unsafe_extern_foo}` + = note: unsafe functions cannot be coerced into safe function pointers + +error[E0308]: mismatched types + --> $DIR/fn-pointer-mismatch-diagnostics.rs:26:45 + | +LL | let _: for<'a> extern "C" fn(&'a i32) = rust_foo; + | ------------------------------ ^^^^^^^^ expected "C" fn, found "Rust" fn + | | + | expected due to this + | + = note: expected fn pointer `for<'a> extern "C" fn(&'a _)` + found fn item `for<'a> fn(&'a _) {rust_foo}` + +error[E0308]: mismatched types + --> $DIR/fn-pointer-mismatch-diagnostics.rs:27:52 + | +LL | let _: for<'a> unsafe extern "C" fn(&'a i32) = rust_foo; + | ------------------------------------- ^^^^^^^^ expected "C" fn, found "Rust" fn + | | + | expected due to this + | + = note: expected fn pointer `for<'a> unsafe extern "C" fn(&'a _)` + found fn item `for<'a> fn(&'a _) {rust_foo}` + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0308`. From 89005b2ad66888cd9bd4d5328ec0c66fdf587b1d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 4 May 2026 03:46:25 +0200 Subject: [PATCH 02/13] Update `askama` version to `0.16.0` --- Cargo.lock | 88 ++++++++++++++++++++----- src/ci/citool/Cargo.toml | 2 +- src/librustdoc/Cargo.toml | 2 +- src/tools/generate-copyright/Cargo.toml | 2 +- 4 files changed, 75 insertions(+), 19 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6b5491fb754de..f27030fe6d7b4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -174,11 +174,24 @@ checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" [[package]] name = "askama" -version = "0.15.4" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08e1676b346cadfec169374f949d7490fd80a24193d37d2afce0c047cf695e57" +checksum = "9b8246bcbf8eb97abef10c2d92166449680d41d55c0fc6978a91dec2e3619608" dependencies = [ - "askama_macros", + "askama_macros 0.15.6", + "itoa", + "percent-encoding", + "serde", + "serde_json", +] + +[[package]] +name = "askama" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf825125edd887a019d0a3a837dcc5499a68b0d034cc3eb594070c3e18addc" +dependencies = [ + "askama_macros 0.16.0", "itoa", "percent-encoding", "serde", @@ -187,11 +200,11 @@ dependencies = [ [[package]] name = "askama_derive" -version = "0.15.4" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7661ff56517787343f376f75db037426facd7c8d3049cef8911f1e75016f3a37" +checksum = "2f9670bc84a28bb3da91821ef74226949ab63f1265aff7c751634f1dd0e6f97c" dependencies = [ - "askama_parser", + "askama_parser 0.15.6", "basic-toml", "memchr", "proc-macro2", @@ -202,26 +215,66 @@ dependencies = [ "syn", ] +[[package]] +name = "askama_derive" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1c7065972a130eafa84215f21352ae15b4a7393da48c1f5e103904490736738" +dependencies = [ + "askama_parser 0.16.0", + "basic-toml", + "glob", + "memchr", + "proc-macro2", + "quote", + "rustc-hash 2.1.1", + "serde", + "serde_derive", + "syn", +] + [[package]] name = "askama_macros" -version = "0.15.4" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "713ee4dbfd1eb719c2dab859465b01fa1d21cb566684614a713a6b7a99a4e47b" +checksum = "f0756b45480437dded0565dfc568af62ccce146fb6cfe902e808ba86e445f44f" dependencies = [ - "askama_derive", + "askama_derive 0.15.6", +] + +[[package]] +name = "askama_macros" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e23b1d2c4bd39a41971f6124cef4cc6fd0540913ecb90919b69ab3bbe44ae1a" +dependencies = [ + "askama_derive 0.16.0", ] [[package]] name = "askama_parser" -version = "0.15.4" +version = "0.15.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d62d674238a526418b30c0def480d5beadb9d8964e7f38d635b03bf639c704c" +checksum = "5d0af3691ba3af77949c0b5a3925444b85cb58a0184cc7fec16c68ba2e7be868" dependencies = [ "rustc-hash 2.1.1", "serde", "serde_derive", "unicode-ident", - "winnow 0.7.13", + "winnow 1.0.0", +] + +[[package]] +name = "askama_parser" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7db09fde9143e7ac4513358fb32ee32847125b63b18ea715afd487956da715da" +dependencies = [ + "rustc-hash 2.1.1", + "serde", + "serde_derive", + "unicode-ident", + "winnow 1.0.0", ] [[package]] @@ -651,7 +704,7 @@ name = "clippy" version = "0.1.97" dependencies = [ "anstream", - "askama", + "askama 0.15.6", "cargo_metadata 0.23.1", "clippy_config", "clippy_lints", @@ -756,7 +809,7 @@ checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" dependencies = [ "serde", "termcolor", - "unicode-width 0.1.14", + "unicode-width 0.2.2", ] [[package]] @@ -1498,7 +1551,7 @@ name = "generate-copyright" version = "0.1.0" dependencies = [ "anyhow", - "askama", + "askama 0.16.0", "cargo_metadata 0.21.0", "serde", "serde_json", @@ -4826,7 +4879,7 @@ name = "rustdoc" version = "0.0.0" dependencies = [ "arrayvec", - "askama", + "askama 0.16.0", "base64", "expect-test", "indexmap", @@ -6624,6 +6677,9 @@ name = "winnow" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" +dependencies = [ + "memchr", +] [[package]] name = "winsplit" diff --git a/src/ci/citool/Cargo.toml b/src/ci/citool/Cargo.toml index f97d5e807fcc8..4e954f77c081e 100644 --- a/src/ci/citool/Cargo.toml +++ b/src/ci/citool/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] anyhow = "1" -askama = "0.15.4" +askama = "0.16.0" clap = { version = "4.5", features = ["derive"] } csv = "1" diff = "0.1" diff --git a/src/librustdoc/Cargo.toml b/src/librustdoc/Cargo.toml index 440d54b007a19..c06b4857e4903 100644 --- a/src/librustdoc/Cargo.toml +++ b/src/librustdoc/Cargo.toml @@ -10,7 +10,7 @@ path = "lib.rs" [dependencies] # tidy-alphabetical-start arrayvec = { version = "0.7", default-features = false } -askama = { version = "0.15.4", default-features = false, features = ["alloc", "config", "derive"] } +askama = { version = "0.16.0", default-features = false, features = ["alloc", "config", "derive"] } base64 = "0.21.7" indexmap = { version = "2", features = ["serde"] } itertools = "0.12" diff --git a/src/tools/generate-copyright/Cargo.toml b/src/tools/generate-copyright/Cargo.toml index 17b5c727964fe..91236ff6c6040 100644 --- a/src/tools/generate-copyright/Cargo.toml +++ b/src/tools/generate-copyright/Cargo.toml @@ -8,7 +8,7 @@ description = "Produces a manifest of all the copyrighted materials in the Rust [dependencies] anyhow = "1.0.65" -askama = "0.15.4" +askama = "0.16.0" cargo_metadata = "0.21" serde = { version = "1.0.147", features = ["derive"] } serde_json = "1.0.85" From 08aca77199fdaf95d43e831a5002dbb86581d8d0 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 4 May 2026 04:03:50 +0200 Subject: [PATCH 03/13] Regen `proc_macro_dep` --- src/bootstrap/src/utils/proc_macro_deps.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/src/utils/proc_macro_deps.rs b/src/bootstrap/src/utils/proc_macro_deps.rs index fee9b12aca069..94f8243ae1f68 100644 --- a/src/bootstrap/src/utils/proc_macro_deps.rs +++ b/src/bootstrap/src/utils/proc_macro_deps.rs @@ -22,6 +22,7 @@ pub static CRATES: &[&str] = &[ "fnv", "foldhash", "generic-array", + "glob", "hashbrown", "heck", "ident_case", From cf636ec649c3a5b4bb12e343433d716118225a92 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 30 Apr 2026 21:38:02 +0300 Subject: [PATCH 04/13] resolve: Factor out reporting of "cannot reexport" errors into a separate function --- compiler/rustc_resolve/src/imports.rs | 117 ++++++++++++++------------ 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 360c14afa8ba6..c7bad48c24f9a 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -9,7 +9,9 @@ use rustc_attr_parsing::AttributeParser; use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_errors::codes::*; -use rustc_errors::{Applicability, Diagnostic, MultiSpan, pluralize, struct_span_code_err}; +use rustc_errors::{ + Applicability, BufferedEarlyLint, Diagnostic, MultiSpan, pluralize, struct_span_code_err, +}; use rustc_hir::Attribute; use rustc_hir::attrs::AttributeKind; use rustc_hir::attrs::diagnostic::{CustomDiagnostic, Directive, FormatArgs}; @@ -19,6 +21,7 @@ use rustc_middle::metadata::{AmbigModChild, ModChild, Reexport}; use rustc_middle::span_bug; use rustc_middle::ty::{TyCtxt, Visibility}; use rustc_session::errors::feature_err; +use rustc_session::lint::LintId; use rustc_session::lint::builtin::{ AMBIGUOUS_GLOB_REEXPORTS, EXPORTED_PRIVATE_DEPENDENCIES, HIDDEN_GLOB_REEXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE, REDUNDANT_IMPORTS, UNUSED_IMPORTS, @@ -1515,7 +1518,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut reexport_error = None; let mut any_successful_reexport = false; - let mut crate_private_reexport = false; self.per_ns(|this, ns| { let Some(binding) = bindings[ns].get().decl().map(|b| b.import_source()) else { return; @@ -1523,11 +1525,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if import.vis.greater_than(binding.vis(), this.tcx) { reexport_error = Some((ns, binding)); - if let Visibility::Restricted(binding_def_id) = binding.vis() - && binding_def_id.is_top_level_module() - { - crate_private_reexport = true; - } } else { any_successful_reexport = true; } @@ -1536,55 +1533,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // All namespaces must be re-exported with extra visibility for an error to occur. if !any_successful_reexport { let (ns, binding) = reexport_error.unwrap(); - if let Some(extern_crate_id) = - pub_use_of_private_extern_crate_hack(import.summary(), binding) - { - let extern_crate_sp = self.tcx.source_span(self.local_def_id(extern_crate_id)); - self.lint_buffer.buffer_lint( - PUB_USE_OF_PRIVATE_EXTERN_CRATE, - import_id, - import.span, - crate::errors::PrivateExternCrateReexport { - ident, - sugg: extern_crate_sp.shrink_to_lo(), - }, - ); - } else if ns == TypeNS { - let err = if crate_private_reexport { - self.dcx() - .create_err(CannotBeReexportedCratePublicNS { span: import.span, ident }) - } else { - self.dcx().create_err(CannotBeReexportedPrivateNS { span: import.span, ident }) - }; - err.emit(); - } else { - let mut err = if crate_private_reexport { - self.dcx() - .create_err(CannotBeReexportedCratePublic { span: import.span, ident }) - } else { - self.dcx().create_err(CannotBeReexportedPrivate { span: import.span, ident }) - }; - - match binding.kind { - DeclKind::Def(Res::Def(DefKind::Macro(_), def_id)) - // exclude decl_macro - if self.get_macro_by_def_id(def_id).macro_rules => - { - err.subdiagnostic( ConsiderAddingMacroExport { - span: binding.span, - }); - err.subdiagnostic( ConsiderMarkingAsPubCrate { - vis_span: import.vis_span, - }); - } - _ => { - err.subdiagnostic( ConsiderMarkingAsPub { - span: import.span, - ident, - }); - } - } - err.emit(); + if let Some(lint) = self.report_cannot_reexport(import, binding, ident, ns) { + self.lint_buffer.add_early_lint(lint); } } @@ -1613,6 +1563,61 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None } + fn report_cannot_reexport( + &self, + import: Import<'ra>, + decl: Decl<'ra>, + ident: Ident, + ns: Namespace, + ) -> Option { + let crate_private_reexport = match decl.vis() { + Visibility::Restricted(def_id) if def_id.is_top_level_module() => true, + _ => false, + }; + + if let Some(extern_crate_id) = pub_use_of_private_extern_crate_hack(import.summary(), decl) + { + let ImportKind::Single { id, .. } = import.kind else { unreachable!() }; + let sugg = self.tcx.source_span(self.local_def_id(extern_crate_id)).shrink_to_lo(); + let diagnostic = crate::errors::PrivateExternCrateReexport { ident, sugg }; + return Some(BufferedEarlyLint { + lint_id: LintId::of(PUB_USE_OF_PRIVATE_EXTERN_CRATE), + node_id: id, + span: Some(import.span.into()), + diagnostic: diagnostic.into(), + }); + } else if ns == TypeNS { + let err = if crate_private_reexport { + self.dcx().create_err(CannotBeReexportedCratePublicNS { span: import.span, ident }) + } else { + self.dcx().create_err(CannotBeReexportedPrivateNS { span: import.span, ident }) + }; + err.emit(); + } else { + let mut err = if crate_private_reexport { + self.dcx().create_err(CannotBeReexportedCratePublic { span: import.span, ident }) + } else { + self.dcx().create_err(CannotBeReexportedPrivate { span: import.span, ident }) + }; + + match decl.kind { + // exclude decl_macro + DeclKind::Def(Res::Def(DefKind::Macro(_), def_id)) + if self.get_macro_by_def_id(def_id).macro_rules => + { + err.subdiagnostic(ConsiderAddingMacroExport { span: decl.span }); + err.subdiagnostic(ConsiderMarkingAsPubCrate { vis_span: import.vis_span }); + } + _ => { + err.subdiagnostic(ConsiderMarkingAsPub { span: import.span, ident }); + } + } + err.emit(); + } + + None + } + pub(crate) fn check_for_redundant_imports(&mut self, import: Import<'ra>) -> bool { // This function is only called for single imports. let ImportKind::Single { source, target, ref decls, id, .. } = import.kind else { From 65e5bd6610c21a416280248f9f33f4580a327a30 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 30 Apr 2026 21:41:43 +0300 Subject: [PATCH 05/13] resolve: Catch "cannot reexport" errors from macros 2.0 better --- compiler/rustc_resolve/src/imports.rs | 31 +++++++++++++++++-- tests/ui/hygiene/privacy-early.rs | 1 + tests/ui/hygiene/privacy-early.stderr | 22 ++++++++++++- .../private-from-decl-macro.fail.stderr | 26 ++++++++++++++-- tests/ui/imports/private-from-decl-macro.rs | 3 +- 5 files changed, 75 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index c7bad48c24f9a..b43b0a490f07e 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -787,6 +787,29 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let resolution = resolution.borrow(); let Some(binding) = resolution.best_decl() else { continue }; + // Report "cannot reexport" errors for exotic cases involving macros 2.0 + // privacy bending or invariant-breaking code under deprecation lints. + for decl in [resolution.non_glob_decl, resolution.glob_decl] { + if let Some(decl) = decl + && let DeclKind::Import { source_decl, import } = decl.kind + { + // The source entity is too private to be reexported + // with the given import declaration's visibility. + let ord = source_decl.vis().partial_cmp(decl.vis(), self.tcx); + if matches!(ord, None | Some(Ordering::Less)) { + let ident = match import.kind { + ImportKind::Single { source, .. } => source, + _ => key.ident.orig(resolution.orig_ident_span), + }; + if let Some(lint) = + self.report_cannot_reexport(import, source_decl, ident, key.ns) + { + self.lint_buffer.add_early_lint(lint); + } + } + } + } + if let DeclKind::Import { import, .. } = binding.kind && let Some(amb_binding) = binding.ambiguity.get() && binding.res() != Res::Err @@ -1519,18 +1542,20 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let mut reexport_error = None; let mut any_successful_reexport = false; self.per_ns(|this, ns| { - let Some(binding) = bindings[ns].get().decl().map(|b| b.import_source()) else { + let Some(binding) = bindings[ns].get().decl() else { return; }; if import.vis.greater_than(binding.vis(), this.tcx) { - reexport_error = Some((ns, binding)); + // In isolation, a declaration like this is not an error, but if *all* 1-3 + // declarations introduced by the import are more private than the import item's + // nominal visibility, then it's an error. + reexport_error = Some((ns, binding.import_source())); } else { any_successful_reexport = true; } }); - // All namespaces must be re-exported with extra visibility for an error to occur. if !any_successful_reexport { let (ns, binding) = reexport_error.unwrap(); if let Some(lint) = self.report_cannot_reexport(import, binding, ident, ns) { diff --git a/tests/ui/hygiene/privacy-early.rs b/tests/ui/hygiene/privacy-early.rs index 07680a7b9b22d..a279e5cd42651 100644 --- a/tests/ui/hygiene/privacy-early.rs +++ b/tests/ui/hygiene/privacy-early.rs @@ -8,6 +8,7 @@ mod foo { pub macro m() { use f as g; //~ ERROR `f` is private, and cannot be re-exported + //~| ERROR `f` is private, and cannot be re-exported f!(); } } diff --git a/tests/ui/hygiene/privacy-early.stderr b/tests/ui/hygiene/privacy-early.stderr index 1c2f9ff5405f4..556c6e3fe02ca 100644 --- a/tests/ui/hygiene/privacy-early.stderr +++ b/tests/ui/hygiene/privacy-early.stderr @@ -17,6 +17,26 @@ LL | foo::m!(); | --------- in this macro invocation = note: this error originates in the macro `foo::m` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 1 previous error +error[E0364]: `f` is private, and cannot be re-exported + --> $DIR/privacy-early.rs:10:13 + | +LL | use f as g; + | ^^^^^^ +... +LL | foo::m!(); + | --------- in this macro invocation + | +note: consider marking `f` as `pub` in the imported module + --> $DIR/privacy-early.rs:10:13 + | +LL | use f as g; + | ^^^^^^ +... +LL | foo::m!(); + | --------- in this macro invocation + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: this error originates in the macro `foo::m` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0364`. diff --git a/tests/ui/imports/private-from-decl-macro.fail.stderr b/tests/ui/imports/private-from-decl-macro.fail.stderr index e09c193f5e6ba..a4090ef5dcfd1 100644 --- a/tests/ui/imports/private-from-decl-macro.fail.stderr +++ b/tests/ui/imports/private-from-decl-macro.fail.stderr @@ -1,5 +1,24 @@ +error[E0364]: `S` is private, and cannot be re-exported + --> $DIR/private-from-decl-macro.rs:18:13 + | +LL | use crate::m::*; + | ^^^^^^^^^^^ +... +LL | crate::m::mac_glob!(); + | --------------------- in this macro invocation + | +note: consider marking `S` as `pub` in the imported module + --> $DIR/private-from-decl-macro.rs:18:13 + | +LL | use crate::m::*; + | ^^^^^^^^^^^ +... +LL | crate::m::mac_glob!(); + | --------------------- in this macro invocation + = note: this error originates in the macro `crate::m::mac_glob` (in Nightly builds, run with -Z macro-backtrace for more info) + error[E0423]: expected value, found struct `S` - --> $DIR/private-from-decl-macro.rs:27:17 + --> $DIR/private-from-decl-macro.rs:28:17 | LL | pub struct S {} | --------------- `S` defined here @@ -22,6 +41,7 @@ LL - let s = S; LL + let s = s; | -error: aborting due to 1 previous error +error: aborting due to 2 previous errors -For more information about this error, try `rustc --explain E0423`. +Some errors have detailed explanations: E0364, E0423. +For more information about an error, try `rustc --explain E0364`. diff --git a/tests/ui/imports/private-from-decl-macro.rs b/tests/ui/imports/private-from-decl-macro.rs index cdb3bb40ef7b5..ff37665b96862 100644 --- a/tests/ui/imports/private-from-decl-macro.rs +++ b/tests/ui/imports/private-from-decl-macro.rs @@ -14,7 +14,8 @@ mod m { } pub macro mac_glob() { - use crate::m::*; + #[cfg(fail)] + use crate::m::*; //[fail]~ ERROR `S` is private, and cannot be re-exported } } From 21c58d8ad9e2d87068a6de35012e1625a018f1c0 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 30 Apr 2026 22:24:22 +0300 Subject: [PATCH 06/13] resolve: Harden some invariant checks for visibilities --- compiler/rustc_resolve/src/imports.rs | 21 ++++++++++++++++----- compiler/rustc_resolve/src/lib.rs | 2 ++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index b43b0a490f07e..e63ab9f21a6ad 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -276,6 +276,8 @@ impl<'ra> ImportData<'ra> { vis: self.vis, nearest_parent_mod: self.parent_scope.module.nearest_parent_mod().expect_local(), is_single: matches!(self.kind, ImportKind::Single { .. }), + priv_macro_use: matches!(self.kind, ImportKind::MacroUse { warn_private: true }), + span: self.span, } } } @@ -384,9 +386,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) -> Visibility { assert!(import.vis.is_accessible_from(import.nearest_parent_mod, self.tcx)); let decl_vis = if min { decl.min_vis() } else { decl.vis() }; - if decl_vis.partial_cmp(import.vis, self.tcx) == Some(Ordering::Less) + let ord = decl_vis.partial_cmp(import.vis, self.tcx); + let extern_crate_hack = pub_use_of_private_extern_crate_hack(import, decl).is_some(); + if ord == Some(Ordering::Less) && decl_vis.is_accessible_from(import.nearest_parent_mod, self.tcx) - && pub_use_of_private_extern_crate_hack(import, decl).is_none() + && !extern_crate_hack { // Imported declaration is less visible than the import, but is still visible // from the current module, use the declaration's visibility. @@ -394,12 +398,19 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } else { // Good case - imported declaration is more visible than the import, or the same, // use the import's visibility. + // // Bad case - imported declaration is too private for the current module. // It doesn't matter what visibility we choose here (except in the `PRIVATE_MACRO_USE` - // and `PUB_USE_OF_PRIVATE_EXTERN_CRATE` cases), because either some error will be - // reported, or the import declaration will be thrown away (unfortunately cannot use - // delayed bug here for this reason). + // and `PUB_USE_OF_PRIVATE_EXTERN_CRATE` cases), because an error will be reported. // Use import visibility to keep the all declaration visibilities in a module ordered. + if !min + && matches!(ord, None | Some(Ordering::Less)) + && !extern_crate_hack + && !import.priv_macro_use + { + let msg = format!("cannot extend visibility from {decl_vis:?} to {:?}", import.vis); + self.dcx().span_delayed_bug(import.span, msg); + } import.vis } } diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index b15e4b1b72774..654c1d75314ff 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -2741,6 +2741,8 @@ struct ImportSummary { vis: Visibility, nearest_parent_mod: LocalDefId, is_single: bool, + priv_macro_use: bool, + span: Span, } /// Invariant: if `Finalize` is used, expansion and import resolution must be complete. From fadb74734df6917044d83ec6f063a708be05e470 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Mon, 4 May 2026 19:58:34 +0300 Subject: [PATCH 07/13] rust-analyzer: Fix `pub_use_of_private_extern_crate` lint --- src/tools/rust-analyzer/crates/hir-def/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs index 9a7fbc812f875..22c9073a581fc 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs @@ -16,6 +16,7 @@ extern crate rustc_parse_format; extern crate ra_ap_rustc_parse_format as rustc_parse_format; extern crate ra_ap_rustc_abi as rustc_abi; +pub extern crate ra_ap_rustc_abi as layout; pub mod db; @@ -47,7 +48,6 @@ pub mod import_map; pub mod visibility; use intern::{Interned, Symbol}; -pub use rustc_abi as layout; use thin_vec::ThinVec; pub use crate::signatures::LocalFieldId; From 45b945963797e3fe9a03638c54ae87656ef1eb5e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 4 May 2026 23:37:19 +0200 Subject: [PATCH 08/13] Update `askama` version to `0.16.0` in `clippy` --- Cargo.lock | 64 ++++--------------------------------- src/tools/clippy/Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 59 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index f27030fe6d7b4..e61f036cdfe90 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -172,56 +172,26 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "askama" -version = "0.15.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b8246bcbf8eb97abef10c2d92166449680d41d55c0fc6978a91dec2e3619608" -dependencies = [ - "askama_macros 0.15.6", - "itoa", - "percent-encoding", - "serde", - "serde_json", -] - [[package]] name = "askama" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf825125edd887a019d0a3a837dcc5499a68b0d034cc3eb594070c3e18addc" dependencies = [ - "askama_macros 0.16.0", + "askama_macros", "itoa", "percent-encoding", "serde", "serde_json", ] -[[package]] -name = "askama_derive" -version = "0.15.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f9670bc84a28bb3da91821ef74226949ab63f1265aff7c751634f1dd0e6f97c" -dependencies = [ - "askama_parser 0.15.6", - "basic-toml", - "memchr", - "proc-macro2", - "quote", - "rustc-hash 2.1.1", - "serde", - "serde_derive", - "syn", -] - [[package]] name = "askama_derive" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1c7065972a130eafa84215f21352ae15b4a7393da48c1f5e103904490736738" dependencies = [ - "askama_parser 0.16.0", + "askama_parser", "basic-toml", "glob", "memchr", @@ -233,35 +203,13 @@ dependencies = [ "syn", ] -[[package]] -name = "askama_macros" -version = "0.15.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0756b45480437dded0565dfc568af62ccce146fb6cfe902e808ba86e445f44f" -dependencies = [ - "askama_derive 0.15.6", -] - [[package]] name = "askama_macros" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e23b1d2c4bd39a41971f6124cef4cc6fd0540913ecb90919b69ab3bbe44ae1a" dependencies = [ - "askama_derive 0.16.0", -] - -[[package]] -name = "askama_parser" -version = "0.15.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0af3691ba3af77949c0b5a3925444b85cb58a0184cc7fec16c68ba2e7be868" -dependencies = [ - "rustc-hash 2.1.1", - "serde", - "serde_derive", - "unicode-ident", - "winnow 1.0.0", + "askama_derive", ] [[package]] @@ -704,7 +652,7 @@ name = "clippy" version = "0.1.97" dependencies = [ "anstream", - "askama 0.15.6", + "askama", "cargo_metadata 0.23.1", "clippy_config", "clippy_lints", @@ -1551,7 +1499,7 @@ name = "generate-copyright" version = "0.1.0" dependencies = [ "anyhow", - "askama 0.16.0", + "askama", "cargo_metadata 0.21.0", "serde", "serde_json", @@ -4879,7 +4827,7 @@ name = "rustdoc" version = "0.0.0" dependencies = [ "arrayvec", - "askama 0.16.0", + "askama", "base64", "expect-test", "indexmap", diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml index 234478eadbf96..a87fdeaf99c5c 100644 --- a/src/tools/clippy/Cargo.toml +++ b/src/tools/clippy/Cargo.toml @@ -42,7 +42,7 @@ walkdir = "2.3" filetime = "0.2.9" itertools = "0.12" pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] } -askama = { version = "0.15.4", default-features = false, features = ["alloc", "config", "derive"] } +askama = { version = "0.16.0", default-features = false, features = ["alloc", "config", "derive"] } [dev-dependencies.toml] version = "0.9.7" From c89c81227e591fda0a43d498ea122db2ef0c5f71 Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Mon, 4 May 2026 18:30:02 +0200 Subject: [PATCH 09/13] generic_const_args: allow paths to non type consts --- compiler/rustc_ast_passes/src/feature_gate.rs | 23 +++++++ .../src/hir_ty_lowering/mod.rs | 9 ++- compiler/rustc_middle/src/ty/context.rs | 4 ++ .../src/ty/context/impl_interner.rs | 3 + .../src/solve/eval_ctxt/mod.rs | 38 ++++++++++++ .../src/solve/normalizes_to/anon_const.rs | 16 +---- .../src/solve/normalizes_to/free_alias.rs | 21 ++++--- .../src/solve/normalizes_to/inherent.rs | 24 +++++--- .../src/solve/normalizes_to/mod.rs | 25 +++++--- .../rustc_trait_selection/src/traits/mod.rs | 5 +- .../rustc_trait_selection/src/traits/wf.rs | 2 +- compiler/rustc_type_ir/src/inherent.rs | 2 + compiler/rustc_type_ir/src/interner.rs | 1 + compiler/rustc_type_ir/src/predicate.rs | 22 ++++++- .../rustc-dev-guide/src/feature-gate-check.md | 2 + ...-on-failed-eval-with-vars-fail.next.stderr | 48 +++++++++++++++ ...s-on-failed-eval-with-vars-fail.old.stderr | 10 +++ ...ambiguous-on-failed-eval-with-vars-fail.rs | 61 +++++++++++++++++++ .../gca/ambiguous-on-failed-eval-with-vars.rs | 43 +++++++++++++ .../gca/basic-different-definitions.rs | 1 + tests/ui/const-generics/gca/basic.rs | 1 + tests/ui/const-generics/gca/coherence-fail.rs | 1 + .../const-generics/gca/coherence-fail.stderr | 6 +- tests/ui/const-generics/gca/coherence-ok.rs | 1 + .../const-generics/gca/generic-free-const.rs | 19 ++++++ .../const-generics/gca/generic-param-rhs.rs | 1 + .../gca/generic-param-rhs.stderr | 2 +- .../gca/non-type-equality-fail.rs | 44 +++++++++++++ .../gca/non-type-equality-fail.stderr | 25 ++++++++ .../gca/non-type-equality-ok.rs | 47 ++++++++++++++ .../gca/path-to-non-type-const.rs | 35 +++++++++++ ...h-to-non-type-inherent-associated-const.rs | 28 +++++++++ ...-non-type-inherent-associated-const.stderr | 24 ++++++++ .../const-generics/gca/require-next-solver.rs | 4 ++ .../gca/require-next-solver.stderr | 10 +++ .../ui/const-generics/gca/rhs-but-not-root.rs | 1 + .../gca/rhs-but-not-root.stderr | 2 +- 37 files changed, 566 insertions(+), 45 deletions(-) create mode 100644 tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.next.stderr create mode 100644 tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.old.stderr create mode 100644 tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.rs create mode 100644 tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars.rs create mode 100644 tests/ui/const-generics/gca/generic-free-const.rs create mode 100644 tests/ui/const-generics/gca/non-type-equality-fail.rs create mode 100644 tests/ui/const-generics/gca/non-type-equality-fail.stderr create mode 100644 tests/ui/const-generics/gca/non-type-equality-ok.rs create mode 100644 tests/ui/const-generics/gca/path-to-non-type-const.rs create mode 100644 tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs create mode 100644 tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.stderr create mode 100644 tests/ui/const-generics/gca/require-next-solver.rs create mode 100644 tests/ui/const-generics/gca/require-next-solver.stderr diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 2b08906a77434..aa91f78fbb35d 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -464,6 +464,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { check_incompatible_features(sess, features); check_dependent_features(sess, features); check_new_solver_banned_features(sess, features); + check_features_requiring_new_solver(sess, features); let mut visitor = PostExpansionVisitor { sess, features }; @@ -739,3 +740,25 @@ fn check_new_solver_banned_features(sess: &Session, features: &Features) { }); } } + +fn check_features_requiring_new_solver(sess: &Session, features: &Features) { + if sess.opts.unstable_opts.next_solver.globally { + return; + } + + // Require the new solver with GCA, because the old solver can't implement GCA correctly as it + // does not support normalization obligations for free and inherent consts. + if let Some(gca_span) = features + .enabled_lang_features() + .iter() + .find(|feat| feat.gate_name == sym::generic_const_args) + .map(|feat| feat.attr_sp) + { + #[allow(rustc::symbol_intern_string_literal)] + sess.dcx().emit_err(errors::MissingDependentFeatures { + parent_span: gca_span, + parent: sym::generic_const_args, + missing: String::from("-Znext-solver=globally"), + }); + } +} 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 473a32bac03a9..5a86e8186a5aa 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -3064,7 +3064,14 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { span: Span, ) -> Result<(), ErrorGuaranteed> { let tcx = self.tcx(); - if tcx.is_type_const(def_id) { + // FIXME(gca): Intentionally disallowing paths to inherent associated non-type constants + // until a refactoring for how generic args for IACs are represented has been landed. + let is_inherent_assoc_const = tcx.def_kind(def_id) + == DefKind::AssocConst { is_type_const: false } + && tcx.def_kind(tcx.parent(def_id)) == DefKind::Impl { of_trait: false }; + if tcx.is_type_const(def_id) + || tcx.features().generic_const_args() && !is_inherent_assoc_const + { Ok(()) } else { let mut err = self.dcx().struct_span_err( diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index d1048a65a7be0..1f776d8803af3 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -106,6 +106,10 @@ impl<'tcx> rustc_type_ir::inherent::Features> for &'tcx rustc_featu self.generic_const_exprs() } + fn generic_const_args(self) -> bool { + self.generic_const_args() + } + fn coroutine_clone(self) -> bool { self.coroutine_clone() } diff --git a/compiler/rustc_middle/src/ty/context/impl_interner.rs b/compiler/rustc_middle/src/ty/context/impl_interner.rs index 6f6317d53d97b..bacddb6808290 100644 --- a/compiler/rustc_middle/src/ty/context/impl_interner.rs +++ b/compiler/rustc_middle/src/ty/context/impl_interner.rs @@ -172,6 +172,9 @@ impl<'tcx> Interner for TyCtxt<'tcx> { fn type_of_opaque_hir_typeck(self, def_id: LocalDefId) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { self.type_of_opaque_hir_typeck(def_id) } + fn is_type_const(self, def_id: DefId) -> bool { + self.is_type_const(def_id) + } fn const_of_item(self, def_id: DefId) -> ty::EarlyBinder<'tcx, Const<'tcx>> { self.const_of_item(def_id) } 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 615cc9e8f81d2..a9d36e425195f 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 @@ -1196,6 +1196,44 @@ where self.delegate.evaluate_const(param_env, uv) } + pub(super) fn evaluate_const_and_instantiate_normalizes_to_term( + &mut self, + goal: Goal>, + uv: ty::UnevaluatedConst, + ) -> QueryResult { + match self.evaluate_const(goal.param_env, uv) { + Some(evaluated) => { + self.instantiate_normalizes_to_term(goal, evaluated.into()); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + None if self.cx().features().generic_const_args() => { + // HACK(khyperia): calling `resolve_vars_if_possible` here shouldn't be necessary, + // `try_evaluate_const` calls `resolve_vars_if_possible` already. However, we want + // to check `has_non_region_infer` against the type with vars resolved (i.e. check + // if there are vars we failed to resolve), so we need to call it again here. + // Perhaps we could split EvaluateConstErr::HasGenericsOrInfers into HasGenerics and + // HasInfers or something, make evaluate_const return that, and make this branch be + // based on that, rather than checking `has_non_region_infer`. + if self.resolve_vars_if_possible(uv).has_non_region_infer() { + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } else { + // We do not instantiate to the `uv` passed in, but rather + // `goal.predicate.alias`. The `uv` passed in might correspond to the `impl` + // form of a constant (with generic arguments corresponding to the impl block), + // however, we want to structurally instantiate to the original, non-rebased, + // trait `Self` form of the constant (with generic arguments being the trait + // `Self` type). + self.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) + } + } + None => { + // Legacy behavior: always treat as ambiguous + self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + } + } + } + pub(super) fn is_transmutable( &mut self, src: I::Ty, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs index b3703639d99ef..72e8d1be5915e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/anon_const.rs @@ -2,7 +2,7 @@ use rustc_type_ir::{self as ty, Interner}; use tracing::instrument; use crate::delegate::SolverDelegate; -use crate::solve::{Certainty, EvalCtxt, Goal, QueryResult}; +use crate::solve::{EvalCtxt, Goal, QueryResult}; impl EvalCtxt<'_, D> where @@ -14,17 +14,7 @@ where &mut self, goal: Goal>, ) -> QueryResult { - if let Some(normalized_const) = self.evaluate_const( - goal.param_env, - ty::UnevaluatedConst::new( - goal.predicate.alias.def_id().try_into().unwrap(), - goal.predicate.alias.args, - ), - ) { - self.instantiate_normalizes_to_term(goal, normalized_const.into()); - self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - } else { - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) - } + let uv = goal.predicate.alias.expect_ct(self.cx()); + self.evaluate_const_and_instantiate_normalizes_to_term(goal, uv) } } diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs index f3004e4c8105b..44fe2913b73de 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/free_alias.rs @@ -30,13 +30,20 @@ where .map(|pred| goal.with(cx, pred)), ); - let actual = if free_alias.kind(cx).is_type() { - cx.type_of(free_alias.def_id()).instantiate(cx, free_alias.args).skip_norm_wip().into() - } else { - cx.const_of_item(free_alias.def_id()) - .instantiate(cx, free_alias.args) - .skip_norm_wip() - .into() + let actual = match free_alias.kind(cx) { + ty::AliasTermKind::FreeTy { def_id } => { + cx.type_of(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into() + } + ty::AliasTermKind::FreeConst { def_id } if cx.is_type_const(def_id) => { + cx.const_of_item(def_id).instantiate(cx, free_alias.args).skip_norm_wip().into() + } + ty::AliasTermKind::FreeConst { .. } => { + return self.evaluate_const_and_instantiate_normalizes_to_term( + goal, + free_alias.expect_ct(cx), + ); + } + kind => panic!("expected free alias, found {kind:?}"), }; self.instantiate_normalizes_to_term(goal, actual); diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs index 4d62407fe2995..00b5fd7fdcbce 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/inherent.rs @@ -52,13 +52,23 @@ where .map(|pred| goal.with(cx, pred)), ); - let normalized = if inherent.kind(cx).is_type() { - cx.type_of(inherent.def_id()).instantiate(cx, inherent_args).skip_norm_wip().into() - } else { - cx.const_of_item(inherent.def_id()) - .instantiate(cx, inherent_args) - .skip_norm_wip() - .into() + let normalized = match inherent.kind(cx) { + ty::AliasTermKind::InherentTy { def_id } => { + cx.type_of(def_id).instantiate(cx, inherent_args).skip_norm_wip().into() + } + ty::AliasTermKind::InherentConst { def_id } if cx.is_type_const(def_id) => { + cx.const_of_item(def_id).instantiate(cx, inherent_args).skip_norm_wip().into() + } + ty::AliasTermKind::InherentConst { .. } => { + // FIXME(gca): This is dead code at the moment. It should eventually call + // self.evaluate_const like projected consts do in consider_impl_candidate in + // normalizes_to/mod.rs. However, how generic args are represented for IACs is up in + // the air right now. + // Will self.evaluate_const eventually take the inherent_args or the impl_args form + // of args? It might be either. + panic!("References to inherent associated consts should have been blocked"); + } + kind => panic!("expected inherent alias, found {kind:?}"), }; self.instantiate_normalizes_to_term(goal, normalized); self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index d93b7843b2251..688c7fb2c2370 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -383,19 +383,30 @@ where // Finally we construct the actual value of the associated type. let term = match goal.predicate.alias.kind(cx) { - ty::AliasTermKind::ProjectionTy { .. } => { - cx.type_of(target_item_def_id).map_bound(|ty| ty.into()) + ty::AliasTermKind::ProjectionTy { .. } => cx + .type_of(target_item_def_id) + .instantiate(cx, target_args) + .skip_norm_wip() + .into(), + ty::AliasTermKind::ProjectionConst { .. } + if cx.is_type_const(target_item_def_id) => + { + cx.const_of_item(target_item_def_id) + .instantiate(cx, target_args) + .skip_norm_wip() + .into() } ty::AliasTermKind::ProjectionConst { .. } => { - cx.const_of_item(target_item_def_id).map_bound(|ct| ct.into()) + let uv = ty::UnevaluatedConst::new( + target_item_def_id.try_into().unwrap(), + target_args, + ); + return ecx.evaluate_const_and_instantiate_normalizes_to_term(goal, uv); } kind => panic!("expected projection, found {kind:?}"), }; - ecx.instantiate_normalizes_to_term( - goal, - term.instantiate(cx, target_args).skip_norm_wip(), - ); + ecx.instantiate_normalizes_to_term(goal, term); ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }) } diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index e0eeca23c6d46..a834dbd1604f8 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -701,7 +701,10 @@ pub fn try_evaluate_const<'tcx>( // logic does not go through type system normalization. If it did this would // be a backwards compatibility problem as we do not enforce "syntactic" non- // usage of generic parameters like we do here. - if uv.args.has_non_region_param() || uv.args.has_non_region_infer() { + if uv.args.has_non_region_param() + || uv.args.has_non_region_infer() + || uv.args.has_non_region_placeholders() + { return Err(EvaluateConstErr::HasGenericsOrInfers); } diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index 51173500f4718..fdf32d32e9058 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -1065,7 +1065,7 @@ impl<'a, 'tcx> TypeVisitor> for WfPredicates<'a, 'tcx> { ty::ConstKind::Unevaluated(uv) => { if !c.has_escaping_bound_vars() { // Skip type consts as mGCA doesn't support evaluatable clauses - if !tcx.is_type_const(uv.def) { + if !tcx.is_type_const(uv.def) && !tcx.features().generic_const_args() { let predicate = ty::Binder::dummy(ty::PredicateKind::Clause( ty::ClauseKind::ConstEvaluatable(c), )); diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 3539215072ad9..f63361f5968d2 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -629,6 +629,8 @@ pub trait ParamEnv: Copy + Debug + Hash + Eq + TypeFoldable { pub trait Features: Copy { fn generic_const_exprs(self) -> bool; + fn generic_const_args(self) -> bool; + fn coroutine_clone(self) -> bool; fn feature_bound_holds_in_crate(self, symbol: I::Symbol) -> bool; diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index fba43b9cffbe2..03573012177d7 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -203,6 +203,7 @@ pub trait Interner: fn type_of(self, def_id: Self::DefId) -> ty::EarlyBinder; fn type_of_opaque_hir_typeck(self, def_id: Self::LocalDefId) -> ty::EarlyBinder; + fn is_type_const(self, def_id: Self::DefId) -> bool; fn const_of_item(self, def_id: Self::DefId) -> ty::EarlyBinder; fn anon_const_kind(self, def_id: Self::DefId) -> ty::AnonConstKind; diff --git a/compiler/rustc_type_ir/src/predicate.rs b/compiler/rustc_type_ir/src/predicate.rs index 90240e241bc90..7b815e61cf09e 100644 --- a/compiler/rustc_type_ir/src/predicate.rs +++ b/compiler/rustc_type_ir/src/predicate.rs @@ -726,16 +726,32 @@ impl AliasTerm { AliasTermKind::InherentTy { def_id } => AliasTyKind::Inherent { def_id }, AliasTermKind::OpaqueTy { def_id } => AliasTyKind::Opaque { def_id }, AliasTermKind::FreeTy { def_id } => AliasTyKind::Free { def_id }, - AliasTermKind::InherentConst { .. } + kind @ (AliasTermKind::InherentConst { .. } | AliasTermKind::FreeConst { .. } | AliasTermKind::UnevaluatedConst { .. } - | AliasTermKind::ProjectionConst { .. } => { - panic!("Cannot turn `UnevaluatedConst` into `AliasTy`") + | AliasTermKind::ProjectionConst { .. }) => { + panic!("Cannot turn `{}` into `AliasTy`", kind.descr()) } }; ty::AliasTy { kind, args: self.args, _use_alias_ty_new_instead: () } } + pub fn expect_ct(self, interner: I) -> ty::UnevaluatedConst { + let def_id = match self.kind(interner) { + AliasTermKind::InherentConst { def_id } + | AliasTermKind::FreeConst { def_id } + | AliasTermKind::UnevaluatedConst { def_id } + | AliasTermKind::ProjectionConst { def_id } => def_id, + kind @ (AliasTermKind::ProjectionTy { .. } + | AliasTermKind::InherentTy { .. } + | AliasTermKind::OpaqueTy { .. } + | AliasTermKind::FreeTy { .. }) => { + panic!("Cannot turn `{}` into `UnevaluatedConst`", kind.descr()) + } + }; + ty::UnevaluatedConst { def: def_id.try_into().unwrap(), args: self.args } + } + // FIXME: remove this function (access the field instead) pub fn kind(self, _interner: I) -> AliasTermKind { self.kind diff --git a/src/doc/rustc-dev-guide/src/feature-gate-check.md b/src/doc/rustc-dev-guide/src/feature-gate-check.md index 038a14ac070e1..0b4fc0cd680c0 100644 --- a/src/doc/rustc-dev-guide/src/feature-gate-check.md +++ b/src/doc/rustc-dev-guide/src/feature-gate-check.md @@ -69,6 +69,8 @@ in `check_crate` and its AST visitor. (declared in `rustc_feature::INCOMPATIBLE_FEATURES`) are not used together. - `check_new_solver_banned_features`: Bans features incompatible with compiler mode for the next trait solver. +- `check_features_requiring_new_solver`: Requires the new trait solver for + features incompatible with the old solver. - **Parser-gated spans**: Processes the `GatedSpans` recorded during parsing (see [Checking `GatedSpans`](#checking-gatedspans)). diff --git a/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.next.stderr b/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.next.stderr new file mode 100644 index 0000000000000..f609dcab752af --- /dev/null +++ b/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.next.stderr @@ -0,0 +1,48 @@ +error[E0284]: type annotations needed for `([(); _], [(); 10])` + --> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:29:9 + | +LL | let (mut arr, mut arr_with_weird_len) = free(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------ type must be known at this point + | +note: required by a const generic parameter in `free` + --> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:24:9 + | +LL | fn free() -> ([(); N], [(); FREE::]) { + | ^^^^^^^^^^^^^^ required by this const generic parameter in `free` +help: consider giving this pattern a type, where the value of const parameter `N` is specified + | +LL | let (mut arr, mut arr_with_weird_len): ([_; N], _) = free(); + | +++++++++++++ + +error[E0271]: type mismatch resolving `10 == 2` + --> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:35:45 + | +LL | let (mut arr, mut arr_with_weird_len) = free(); + | ^^^^^^ types differ + +error[E0284]: type annotations needed for `([(); _], [(); 10])` + --> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:46:9 + | +LL | let (mut arr, mut arr_with_weird_len) = proj(); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ------ type must be known at this point + | +note: required by a const generic parameter in `proj` + --> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:41:9 + | +LL | fn proj() -> ([(); N], [(); ::PROJ::]) { + | ^^^^^^^^^^^^^^ required by this const generic parameter in `proj` +help: consider giving this pattern a type, where the value of const parameter `N` is specified + | +LL | let (mut arr, mut arr_with_weird_len): ([_; N], _) = proj(); + | +++++++++++++ + +error[E0271]: type mismatch resolving `10 == 2` + --> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:52:45 + | +LL | let (mut arr, mut arr_with_weird_len) = proj(); + | ^^^^^^ types differ + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0271, E0284. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.old.stderr b/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.old.stderr new file mode 100644 index 0000000000000..f27badafa2ca7 --- /dev/null +++ b/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.old.stderr @@ -0,0 +1,10 @@ +error: `generic_const_args` requires -Znext-solver=globally to be enabled + --> $DIR/ambiguous-on-failed-eval-with-vars-fail.rs:9:12 + | +LL | #![feature(generic_const_args)] + | ^^^^^^^^^^^^^^^^^^ + | + = help: enable all of these features + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.rs b/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.rs new file mode 100644 index 0000000000000..72952940b1c12 --- /dev/null +++ b/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars-fail.rs @@ -0,0 +1,61 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +// (`test_free_mismatch` is quite difficult to implement in the old solver, so make sure this test +// runs on the old solver, just in case someone attempts to implement GCA for the old solver and +// removes the restriction that -Znext-solver must be enabled) + +#![feature(generic_const_items)] +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +//[old]~^ ERROR next-solver +#![expect(incomplete_features)] + +const FREE: usize = 10; + +trait Trait { + const PROJ: usize; +} +struct S; + +impl Trait for S { + const PROJ: usize = 10; +} + +fn free() -> ([(); N], [(); FREE::]) { + loop {} +} + +fn test_free() { + let (mut arr, mut arr_with_weird_len) = free(); + //[next]~^ ERROR type annotations needed + arr_with_weird_len = [(); 10]; +} + +fn test_free_mismatch() { + let (mut arr, mut arr_with_weird_len) = free(); + //[next]~^ ERROR type mismatch resolving `10 == 2` + arr_with_weird_len = [(); 2]; + arr = [(); 10]; +} + +fn proj() -> ([(); N], [(); ::PROJ::]) { + loop {} +} + +fn test_proj() { + let (mut arr, mut arr_with_weird_len) = proj(); + //[next]~^ ERROR type annotations needed + arr_with_weird_len = [(); 10]; +} + +fn test_proj_mismatch() { + let (mut arr, mut arr_with_weird_len) = proj(); + //[next]~^ ERROR type mismatch resolving `10 == 2` + arr_with_weird_len = [(); 2]; + arr = [(); 10]; +} + +fn main() { + test_free(); + test_proj(); +} diff --git a/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars.rs b/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars.rs new file mode 100644 index 0000000000000..4be1b036d6946 --- /dev/null +++ b/tests/ui/const-generics/gca/ambiguous-on-failed-eval-with-vars.rs @@ -0,0 +1,43 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +#![feature(generic_const_items)] +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![expect(incomplete_features)] + +const FREE: usize = 10; + +trait Trait { + const PROJ: usize; +} +struct S; + +impl Trait for S { + const PROJ: usize = 10; +} + +fn free() -> ([(); N], [(); FREE::]) { + loop {} +} + +fn test_free() { + let (mut arr, mut arr_with_weird_len) = free(); + arr_with_weird_len = [(); 10]; + arr = [(); 10]; +} + +fn proj() -> ([(); N], [(); ::PROJ::]) { + loop {} +} + +fn test_proj() { + let (mut arr, mut arr_with_weird_len) = proj(); + arr_with_weird_len = [(); 10]; + arr = [(); 10]; +} + +fn main() { + test_free(); + test_proj(); +} diff --git a/tests/ui/const-generics/gca/basic-different-definitions.rs b/tests/ui/const-generics/gca/basic-different-definitions.rs index d96c718617d24..ec26f26630a57 100644 --- a/tests/ui/const-generics/gca/basic-different-definitions.rs +++ b/tests/ui/const-generics/gca/basic-different-definitions.rs @@ -1,4 +1,5 @@ //@ check-pass +//@ compile-flags: -Znext-solver #![feature(generic_const_items)] #![feature(min_generic_const_args)] diff --git a/tests/ui/const-generics/gca/basic.rs b/tests/ui/const-generics/gca/basic.rs index e80540d621de5..40c28c3d38c0e 100644 --- a/tests/ui/const-generics/gca/basic.rs +++ b/tests/ui/const-generics/gca/basic.rs @@ -1,4 +1,5 @@ //@ check-pass +//@ compile-flags: -Znext-solver #![feature(generic_const_items)] #![feature(min_generic_const_args)] diff --git a/tests/ui/const-generics/gca/coherence-fail.rs b/tests/ui/const-generics/gca/coherence-fail.rs index 1b181d792d362..079c8cf63e144 100644 --- a/tests/ui/const-generics/gca/coherence-fail.rs +++ b/tests/ui/const-generics/gca/coherence-fail.rs @@ -1,3 +1,4 @@ +//@ compile-flags: -Znext-solver #![feature(generic_const_items, min_generic_const_args, generic_const_args)] #![expect(incomplete_features)] diff --git a/tests/ui/const-generics/gca/coherence-fail.stderr b/tests/ui/const-generics/gca/coherence-fail.stderr index e8122c1b832f1..d180654cc84a6 100644 --- a/tests/ui/const-generics/gca/coherence-fail.stderr +++ b/tests/ui/const-generics/gca/coherence-fail.stderr @@ -1,5 +1,5 @@ error[E0119]: conflicting implementations of trait `Trait1` for type `[(); 2]` - --> $DIR/coherence-fail.rs:9:1 + --> $DIR/coherence-fail.rs:10:1 | LL | impl Trait1 for [(); FOO::<1>] {} | ------------------------------ first implementation here @@ -7,7 +7,7 @@ LL | impl Trait1 for [(); BAR::<1>] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `[(); 2]` error[E0119]: conflicting implementations of trait `Trait2` for type `[(); 1]` - --> $DIR/coherence-fail.rs:16:1 + --> $DIR/coherence-fail.rs:17:1 | LL | impl Trait2 for [(); DIV2::<2>] {} | ------------------------------- first implementation here @@ -15,7 +15,7 @@ LL | impl Trait2 for [(); DIV2::<3>] {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `[(); 1]` error[E0119]: conflicting implementations of trait `Trait3` for type `[(); 2]` - --> $DIR/coherence-fail.rs:25:1 + --> $DIR/coherence-fail.rs:26:1 | LL | impl Trait3 for [(); ADD1::<1>] {} | ------------------------------- first implementation here diff --git a/tests/ui/const-generics/gca/coherence-ok.rs b/tests/ui/const-generics/gca/coherence-ok.rs index 447f25bfdc36b..38899badf5f03 100644 --- a/tests/ui/const-generics/gca/coherence-ok.rs +++ b/tests/ui/const-generics/gca/coherence-ok.rs @@ -1,4 +1,5 @@ //@ check-pass +//@ compile-flags: -Znext-solver #![feature(generic_const_items, min_generic_const_args, generic_const_args)] #![expect(incomplete_features)] diff --git a/tests/ui/const-generics/gca/generic-free-const.rs b/tests/ui/const-generics/gca/generic-free-const.rs new file mode 100644 index 0000000000000..e7816728d461e --- /dev/null +++ b/tests/ui/const-generics/gca/generic-free-const.rs @@ -0,0 +1,19 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![feature(generic_const_items)] +#![expect(incomplete_features)] + +const ADD1: usize = N + 1; + +fn a() -> [usize; ADD1::] { + [ADD1::; ADD1::] +} + +fn main() { + let _: [(); ADD1::<1>] = [(); ADD1::<1>]; + let _: [(); ADD1::<{ ADD1::<1> }>] = [(); ADD1::<2>]; + a::<2>(); +} diff --git a/tests/ui/const-generics/gca/generic-param-rhs.rs b/tests/ui/const-generics/gca/generic-param-rhs.rs index ed4467d6f0039..ea6e2d7398b57 100644 --- a/tests/ui/const-generics/gca/generic-param-rhs.rs +++ b/tests/ui/const-generics/gca/generic-param-rhs.rs @@ -1,3 +1,4 @@ +//@ compile-flags: -Znext-solver #![feature(min_generic_const_args, generic_const_args)] #![expect(incomplete_features)] diff --git a/tests/ui/const-generics/gca/generic-param-rhs.stderr b/tests/ui/const-generics/gca/generic-param-rhs.stderr index acf3a5b21a855..ca0ce899b8b0d 100644 --- a/tests/ui/const-generics/gca/generic-param-rhs.stderr +++ b/tests/ui/const-generics/gca/generic-param-rhs.stderr @@ -1,5 +1,5 @@ error: generic parameters in const blocks are only allowed as the direct value of a `type const` - --> $DIR/generic-param-rhs.rs:6:19 + --> $DIR/generic-param-rhs.rs:7:19 | LL | foo::(); | ^ diff --git a/tests/ui/const-generics/gca/non-type-equality-fail.rs b/tests/ui/const-generics/gca/non-type-equality-fail.rs new file mode 100644 index 0000000000000..235ab37f8b570 --- /dev/null +++ b/tests/ui/const-generics/gca/non-type-equality-fail.rs @@ -0,0 +1,44 @@ +//@ compile-flags: -Znext-solver + +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![expect(incomplete_features)] + +trait Trait { + const PROJECTED_A: usize; + const PROJECTED_B: usize; +} + +struct StructImpl; +struct GenericStructImpl; + +impl Trait for StructImpl { + const PROJECTED_A: usize = 1; + const PROJECTED_B: usize = 1; +} + +impl Trait for GenericStructImpl { + const PROJECTED_A: usize = N; + const PROJECTED_B: usize = N; +} + +const FREE_A: usize = 1; +const FREE_B: usize = 1; + +struct Struct; + +fn f() { + let _: Struct<{ as Trait>::PROJECTED_A }> = + Struct::<{ as Trait>::PROJECTED_B }>; + //~^ ERROR mismatched types +} + +fn g() { + let _: Struct<{ T::PROJECTED_A }> = Struct::<{ T::PROJECTED_B }>; + //~^ ERROR mismatched types +} + +fn main() { + f::<2>(); + g::(); +} diff --git a/tests/ui/const-generics/gca/non-type-equality-fail.stderr b/tests/ui/const-generics/gca/non-type-equality-fail.stderr new file mode 100644 index 0000000000000..796efdcea93cc --- /dev/null +++ b/tests/ui/const-generics/gca/non-type-equality-fail.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/non-type-equality-fail.rs:32:9 + | +LL | let _: Struct<{ as Trait>::PROJECTED_A }> = + | -------------------------------------------------------- expected due to this +LL | Struct::<{ as Trait>::PROJECTED_B }>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + | + = note: expected struct `Struct< as Trait>::PROJECTED_A>` + found struct `Struct< as Trait>::PROJECTED_B>` + +error[E0308]: mismatched types + --> $DIR/non-type-equality-fail.rs:37:41 + | +LL | let _: Struct<{ T::PROJECTED_A }> = Struct::<{ T::PROJECTED_B }>; + | -------------------------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ types differ + | | + | expected due to this + | + = note: expected struct `Struct<::PROJECTED_A>` + found struct `Struct<::PROJECTED_B>` + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/const-generics/gca/non-type-equality-ok.rs b/tests/ui/const-generics/gca/non-type-equality-ok.rs new file mode 100644 index 0000000000000..443fbc46bd8a0 --- /dev/null +++ b/tests/ui/const-generics/gca/non-type-equality-ok.rs @@ -0,0 +1,47 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![feature(generic_const_items)] +#![expect(incomplete_features)] + +trait Trait { + const PROJECTED_A: usize; + const PROJECTED_B: usize; +} + +struct StructImpl; +struct GenericStructImpl; + +impl Trait for StructImpl { + const PROJECTED_A: usize = 1; + const PROJECTED_B: usize = 1; +} + +impl Trait for GenericStructImpl { + const PROJECTED_A: usize = N; + const PROJECTED_B: usize = N; +} + +const FREE_A: usize = 1; +const FREE_B: usize = 1; + +struct Struct; + +fn f() { + let _: Struct<{ as Trait>::PROJECTED_A }> = + Struct::<{ as Trait>::PROJECTED_A }>; +} + +fn g() { + let _: Struct<{ T::PROJECTED_A }> = Struct::<{ T::PROJECTED_A }>; +} + +fn main() { + let _: Struct = Struct::; + let _: Struct<{ ::PROJECTED_A }> = + Struct::<{ ::PROJECTED_B }>; + let _: Struct<{ as Trait>::PROJECTED_A }> = + Struct::<{ as Trait>::PROJECTED_B }>; +} diff --git a/tests/ui/const-generics/gca/path-to-non-type-const.rs b/tests/ui/const-generics/gca/path-to-non-type-const.rs new file mode 100644 index 0000000000000..c36dbbcc578fa --- /dev/null +++ b/tests/ui/const-generics/gca/path-to-non-type-const.rs @@ -0,0 +1,35 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![expect(incomplete_features)] + +trait Trait { + const PROJECTED: usize; +} + +struct StructImpl; +struct GenericStructImpl; + +const FREE: usize = 1; + +impl Trait for StructImpl { + const PROJECTED: usize = 1; +} + +impl Trait for GenericStructImpl { + const PROJECTED: usize = A; +} + +struct Struct; + +fn f() { + let _ = Struct::<{ T::PROJECTED }>; +} + +fn main() { + let _ = Struct::; + let _ = Struct::<{ ::PROJECTED }>; + let _ = Struct::<{ as Trait>::PROJECTED }>; +} diff --git a/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs b/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs new file mode 100644 index 0000000000000..1b9164d4a2300 --- /dev/null +++ b/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.rs @@ -0,0 +1,28 @@ +//! This test should be part of path-to-non-type-const.rs, and should pass. However, we are holding +//! off on implementing paths to IACs until a refactoring of how IAC generics are represented. +//@ compile-flags: -Znext-solver + +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +#![feature(inherent_associated_types)] +#![expect(incomplete_features)] + +struct StructImpl; +struct GenericStructImpl; + +impl StructImpl { + const INHERENT: usize = 1; +} + +impl GenericStructImpl { + const INHERENT: usize = A; +} + +struct Struct; + +fn main() { + let _ = Struct::<{ StructImpl::INHERENT }>; + //~^ ERROR use of `const` in the type system not defined as `type const` + let _ = Struct::<{ GenericStructImpl::<2>::INHERENT }>; + //~^ ERROR use of `const` in the type system not defined as `type const` +} diff --git a/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.stderr b/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.stderr new file mode 100644 index 0000000000000..b5da53f0f1c8e --- /dev/null +++ b/tests/ui/const-generics/gca/path-to-non-type-inherent-associated-const.stderr @@ -0,0 +1,24 @@ +error: use of `const` in the type system not defined as `type const` + --> $DIR/path-to-non-type-inherent-associated-const.rs:24:24 + | +LL | let _ = Struct::<{ StructImpl::INHERENT }>; + | ^^^^^^^^^^^^^^^^^^^^ + | +help: add `type` before `const` for `StructImpl::INHERENT` + | +LL | type const INHERENT: usize = 1; + | ++++ + +error: use of `const` in the type system not defined as `type const` + --> $DIR/path-to-non-type-inherent-associated-const.rs:26:24 + | +LL | let _ = Struct::<{ GenericStructImpl::<2>::INHERENT }>; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +help: add `type` before `const` for `GenericStructImpl::::INHERENT` + | +LL | type const INHERENT: usize = A; + | ++++ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/const-generics/gca/require-next-solver.rs b/tests/ui/const-generics/gca/require-next-solver.rs new file mode 100644 index 0000000000000..ffe3141b0f28a --- /dev/null +++ b/tests/ui/const-generics/gca/require-next-solver.rs @@ -0,0 +1,4 @@ +#![feature(min_generic_const_args)] +#![feature(generic_const_args)] +//~^ ERROR next-solver +fn main() {} diff --git a/tests/ui/const-generics/gca/require-next-solver.stderr b/tests/ui/const-generics/gca/require-next-solver.stderr new file mode 100644 index 0000000000000..677c37610d842 --- /dev/null +++ b/tests/ui/const-generics/gca/require-next-solver.stderr @@ -0,0 +1,10 @@ +error: `generic_const_args` requires -Znext-solver=globally to be enabled + --> $DIR/require-next-solver.rs:2:12 + | +LL | #![feature(generic_const_args)] + | ^^^^^^^^^^^^^^^^^^ + | + = help: enable all of these features + +error: aborting due to 1 previous error + diff --git a/tests/ui/const-generics/gca/rhs-but-not-root.rs b/tests/ui/const-generics/gca/rhs-but-not-root.rs index 6c0337dbbd470..61762a2851356 100644 --- a/tests/ui/const-generics/gca/rhs-but-not-root.rs +++ b/tests/ui/const-generics/gca/rhs-but-not-root.rs @@ -1,3 +1,4 @@ +//@ compile-flags: -Znext-solver #![feature(generic_const_items)] #![feature(min_generic_const_args)] #![feature(generic_const_args)] diff --git a/tests/ui/const-generics/gca/rhs-but-not-root.stderr b/tests/ui/const-generics/gca/rhs-but-not-root.stderr index 6481407eedc42..06f5ae248ac14 100644 --- a/tests/ui/const-generics/gca/rhs-but-not-root.stderr +++ b/tests/ui/const-generics/gca/rhs-but-not-root.stderr @@ -1,5 +1,5 @@ error: generic parameters in const blocks are only allowed as the direct value of a `type const` - --> $DIR/rhs-but-not-root.rs:7:54 + --> $DIR/rhs-but-not-root.rs:8:54 | LL | type const FOO: usize = ID::; | ^ From 664177fdf14dbef89d8fdf0664f605c61ed86eb9 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Mon, 27 Apr 2026 09:19:56 +0200 Subject: [PATCH 10/13] Remove const closure hacks in libstd --- compiler/rustc_passes/src/stability.rs | 1 + library/alloc/src/lib.rs | 2 ++ library/alloc/src/raw_vec/mod.rs | 6 +---- library/alloctests/lib.rs | 2 ++ library/core/src/lib.rs | 1 + library/core/src/ops/try_trait.rs | 30 +++++---------------- library/core/src/slice/cmp.rs | 37 +++++++------------------- 7 files changed, 24 insertions(+), 55 deletions(-) diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index e41df43e34bd3..8b7d2d7601c7a 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -58,6 +58,7 @@ fn inherit_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { _ => false, } } + DefKind::Closure => true, _ => false, } } diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index d85a63999fe03..7b41023ff31bf 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -108,6 +108,7 @@ #![feature(const_heap)] #![feature(const_index)] #![feature(const_option_ops)] +#![feature(const_result_trait_fn)] #![feature(const_try)] #![feature(copied_into_inner)] #![feature(core_intrinsics)] @@ -173,6 +174,7 @@ #![feature(allocator_internals)] #![feature(allow_internal_unstable)] #![feature(cfg_sanitize)] +#![feature(const_closures)] #![feature(const_precise_live_drops)] #![feature(const_trait_impl)] #![feature(coroutine_trait)] diff --git a/library/alloc/src/raw_vec/mod.rs b/library/alloc/src/raw_vec/mod.rs index cabf6accf3b53..0309d63cce06d 100644 --- a/library/alloc/src/raw_vec/mod.rs +++ b/library/alloc/src/raw_vec/mod.rs @@ -898,9 +898,5 @@ const fn layout_array(cap: usize, elem_layout: Layout) -> Result Ok(layout), - Err(_) => Err(CapacityOverflow.into()), - } + elem_layout.repeat_packed(cap).map_err(const |_| CapacityOverflow.into()) } diff --git a/library/alloctests/lib.rs b/library/alloctests/lib.rs index e09d8495fdeac..936d60fdad73b 100644 --- a/library/alloctests/lib.rs +++ b/library/alloctests/lib.rs @@ -23,6 +23,7 @@ #![feature(const_destruct)] #![feature(const_heap)] #![feature(const_option_ops)] +#![feature(const_result_trait_fn)] #![feature(const_try)] #![feature(copied_into_inner)] #![feature(core_intrinsics)] @@ -54,6 +55,7 @@ // // Language features: // tidy-alphabetical-start +#![feature(const_closures)] #![feature(const_trait_impl)] #![feature(dropck_eyepatch)] #![feature(min_specialization)] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 3451b2976bb73..57ce51bb8c0ed 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -125,6 +125,7 @@ #![feature(cfg_target_has_atomic)] #![feature(cfg_target_has_atomic_equal_alignment)] #![feature(cfg_ub_checks)] +#![feature(const_closures)] #![feature(const_precise_live_drops)] #![feature(const_trait_impl)] #![feature(decl_macro)] diff --git a/library/core/src/ops/try_trait.rs b/library/core/src/ops/try_trait.rs index 9521213e1bc7e..aaa71786854da 100644 --- a/library/core/src/ops/try_trait.rs +++ b/library/core/src/ops/try_trait.rs @@ -1,4 +1,4 @@ -use crate::marker::{Destruct, PhantomData}; +use crate::marker::Destruct; use crate::ops::ControlFlow; /// The `?` operator and `try {}` blocks. @@ -398,25 +398,6 @@ pub(crate) type ChangeOutputType>, V> = /// Not currently planned to be exposed publicly, so just `pub(crate)`. #[repr(transparent)] pub(crate) struct NeverShortCircuit(pub T); -// FIXME(const-hack): replace with `|a| NeverShortCircuit(f(a))` when const closures added. -pub(crate) struct Wrapped T> { - f: F, - p: PhantomData<(T, A)>, -} -#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] -impl T + [const] Destruct> const FnOnce<(A,)> for Wrapped { - type Output = NeverShortCircuit; - - extern "rust-call" fn call_once(mut self, args: (A,)) -> Self::Output { - self.call_mut(args) - } -} -#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")] -impl T> const FnMut<(A,)> for Wrapped { - extern "rust-call" fn call_mut(&mut self, (args,): (A,)) -> Self::Output { - NeverShortCircuit((self.f)(args)) - } -} impl NeverShortCircuit { /// Wraps a unary function to produce one that wraps the output into a `NeverShortCircuit`. @@ -424,11 +405,14 @@ impl NeverShortCircuit { /// This is useful for implementing infallible functions in terms of the `try_` ones, /// without accidentally capturing extra generic parameters in a closure. #[inline] - pub(crate) const fn wrap_mut_1(f: F) -> Wrapped + #[rustc_const_unstable(feature = "const_array", issue = "147606")] + pub(crate) const fn wrap_mut_1( + mut f: F, + ) -> impl [const] FnMut(A) -> Self + [const] Destruct where - F: [const] FnMut(A) -> T, + F: [const] FnMut(A) -> T + [const] Destruct, { - Wrapped { f, p: PhantomData } + const move |a| NeverShortCircuit(f(a)) } #[inline] diff --git a/library/core/src/slice/cmp.rs b/library/core/src/slice/cmp.rs index 69a01eef88f97..6a10fc22c2e2c 100644 --- a/library/core/src/slice/cmp.rs +++ b/library/core/src/slice/cmp.rs @@ -3,7 +3,6 @@ use super::{from_raw_parts, memchr}; use crate::ascii; use crate::cmp::{self, BytewiseEq, Ordering}; -use crate::convert::Infallible; use crate::intrinsics::compare_bytes; use crate::marker::Destruct; use crate::mem::SizedTypeProperties; @@ -182,20 +181,12 @@ type AlwaysBreak = ControlFlow; #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] impl const SlicePartialOrd for A { default fn partial_compare(left: &[A], right: &[A]) -> Option { - // FIXME(const-hack): revert this to a const closure once possible - #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] - const fn elem_chain(a: &A, b: &A) -> ControlFlow> { - match PartialOrd::partial_cmp(a, b) { - Some(Ordering::Equal) => ControlFlow::Continue(()), - non_eq => ControlFlow::Break(non_eq), - } - } + let elem_chain = const |a, b| match PartialOrd::partial_cmp(a, b) { + Some(Ordering::Equal) => ControlFlow::Continue(()), + non_eq => ControlFlow::Break(non_eq), + }; - // FIXME(const-hack): revert this to a const closure once possible - #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] - const fn len_chain(a: &usize, b: &usize) -> ControlFlow, Infallible> { - ControlFlow::Break(usize::partial_cmp(a, b)) - } + let len_chain = const |a: &_, b: &_| ControlFlow::Break(usize::partial_cmp(a, b)); let AlwaysBreak::Break(b) = chaining_impl(left, right, elem_chain, len_chain); b @@ -293,20 +284,12 @@ const trait SliceOrd: Sized { #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] impl const SliceOrd for A { default fn compare(left: &[Self], right: &[Self]) -> Ordering { - // FIXME(const-hack): revert this to a const closure once possible - #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] - const fn elem_chain(a: &A, b: &A) -> ControlFlow { - match Ord::cmp(a, b) { - Ordering::Equal => ControlFlow::Continue(()), - non_eq => ControlFlow::Break(non_eq), - } - } + let elem_chain = const |a, b| match Ord::cmp(a, b) { + Ordering::Equal => ControlFlow::Continue(()), + non_eq => ControlFlow::Break(non_eq), + }; - // FIXME(const-hack): revert this to a const closure once possible - #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] - const fn len_chain(a: &usize, b: &usize) -> ControlFlow { - ControlFlow::Break(usize::cmp(a, b)) - } + let len_chain = const |a: &_, b: &_| ControlFlow::Break(usize::cmp(a, b)); let AlwaysBreak::Break(b) = chaining_impl(left, right, elem_chain, len_chain); b From a015eb119aa3c350afa365cf528f2811b648cf90 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 28 Apr 2026 10:14:24 +0200 Subject: [PATCH 11/13] Revert const hack and use const closure --- compiler/rustc_passes/src/stability.rs | 2 +- library/core/src/iter/traits/iterator.rs | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_passes/src/stability.rs b/compiler/rustc_passes/src/stability.rs index 8b7d2d7601c7a..420e74b130598 100644 --- a/compiler/rustc_passes/src/stability.rs +++ b/compiler/rustc_passes/src/stability.rs @@ -54,7 +54,7 @@ fn inherit_const_stability(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool { match def_kind { DefKind::AssocFn | DefKind::AssocTy | DefKind::AssocConst { .. } => { match tcx.def_kind(tcx.local_parent(def_id)) { - DefKind::Impl { .. } => true, + DefKind::Trait | DefKind::Impl { .. } => true, _ => false, } } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index c6a06c133a156..6dc748c568fcd 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -226,13 +226,11 @@ pub const trait Iterator { Self: Sized + [const] Destruct, Self::Item: [const] Destruct, { - // FIXME(const-hack): revert this to a const closure - #[rustc_const_unstable(feature = "const_iter", issue = "92476")] - #[rustc_inherit_overflow_checks] - const fn plus_one(accum: usize, _elem: T) -> usize { - accum + 1 - } - self.fold(0, plus_one) + self.fold( + 0, + #[rustc_inherit_overflow_checks] + const |accum, _elem| accum + 1, + ) } /// Consumes the iterator, returning the last element. From 23cea556aaeeca0fb3f3f52d69e96f1274314a79 Mon Sep 17 00:00:00 2001 From: Daria Sukhonina Date: Tue, 5 May 2026 12:39:51 +0300 Subject: [PATCH 12/13] Remove duplicate debug assert --- compiler/rustc_thread_pool/src/sleep/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/rustc_thread_pool/src/sleep/mod.rs b/compiler/rustc_thread_pool/src/sleep/mod.rs index aa6666092147a..cbaead52902dc 100644 --- a/compiler/rustc_thread_pool/src/sleep/mod.rs +++ b/compiler/rustc_thread_pool/src/sleep/mod.rs @@ -106,7 +106,6 @@ impl Sleep { let mut data = self.data.lock().unwrap(); debug_assert!(data.active_threads > 0); debug_assert!(data.blocked_threads < data.worker_count); - debug_assert!(data.active_threads > 0); data.active_threads -= 1; data.blocked_threads += 1; From ee350203e8b5033a3a70ac4042493faa6b65a8c4 Mon Sep 17 00:00:00 2001 From: cyrgani Date: Tue, 5 May 2026 10:56:28 +0000 Subject: [PATCH 13/13] Revert "remove `MethodReceiverExpr` special-casing" This reverts commit 5ad560f7ec96bd42f003abe9842abe6e5643817e and adds a regression test. --- compiler/rustc_expand/src/base.rs | 4 ---- compiler/rustc_expand/src/expand.rs | 19 ++++++++++++++----- .../ui/macros/auxiliary/semicolon-in-exprs.rs | 4 ++++ tests/ui/macros/semicolon-in-exprs.rs | 15 +++++++++++++++ 4 files changed, 33 insertions(+), 9 deletions(-) create mode 100644 tests/ui/macros/auxiliary/semicolon-in-exprs.rs create mode 100644 tests/ui/macros/semicolon-in-exprs.rs diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index bf574cf463cc0..7f1b58dd1de08 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -423,10 +423,6 @@ pub trait MacResult { None } - fn make_method_receiver_expr(self: Box) -> Option> { - self.make_expr() - } - /// Creates zero or more items. fn make_items(self: Box) -> Option; 1]>> { None diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index b6611e7e0ca0c..741c34e0304af 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -68,6 +68,7 @@ macro_rules! ast_fragments { /// Can also serve as an input and intermediate result for macro expansion operations. pub enum AstFragment { OptExpr(Option>), + MethodReceiverExpr(Box), $($Kind($AstTy),)* } @@ -75,6 +76,7 @@ macro_rules! ast_fragments { #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum AstFragmentKind { OptExpr, + MethodReceiverExpr, $($Kind,)* } @@ -82,6 +84,7 @@ macro_rules! ast_fragments { pub fn name(self) -> &'static str { match self { AstFragmentKind::OptExpr => "expression", + AstFragmentKind::MethodReceiverExpr => "expression", $(AstFragmentKind::$Kind => $kind_name,)* } } @@ -90,6 +93,8 @@ macro_rules! ast_fragments { match self { AstFragmentKind::OptExpr => result.make_expr().map(Some).map(AstFragment::OptExpr), + AstFragmentKind::MethodReceiverExpr => + result.make_expr().map(AstFragment::MethodReceiverExpr), $(AstFragmentKind::$Kind => result.$make_ast().map(AstFragment::$Kind),)* } } @@ -116,6 +121,13 @@ macro_rules! ast_fragments { } } + pub(crate) fn make_method_receiver_expr(self) -> Box { + match self { + AstFragment::MethodReceiverExpr(expr) => expr, + _ => panic!("AstFragment::make_method_receiver_expr called on the wrong kind of fragment"), + } + } + $(pub fn $make_ast(self) -> $AstTy { match self { AstFragment::$Kind(ast) => ast, @@ -134,6 +146,7 @@ macro_rules! ast_fragments { *opt_expr = vis.filter_map_expr(expr) } } + AstFragment::MethodReceiverExpr(expr) => vis.visit_method_receiver_expr(expr), $($(AstFragment::$Kind(ast) => vis.$visit_ast(ast),)?)* $($(AstFragment::$Kind(ast) => ast.flat_map_in_place(|ast| vis.$flat_map_ast_elt(ast, $($args)*)),)?)* @@ -144,6 +157,7 @@ macro_rules! ast_fragments { match self { AstFragment::OptExpr(Some(expr)) => try_visit!(visitor.visit_expr(expr)), AstFragment::OptExpr(None) => {} + AstFragment::MethodReceiverExpr(expr) => try_visit!(visitor.visit_method_receiver_expr(expr)), $($(AstFragment::$Kind(ast) => try_visit!(visitor.$visit_ast(ast)),)?)* $($(AstFragment::$Kind(ast) => walk_list!(visitor, $visit_ast_elt, &ast[..], $($args)*),)?)* } @@ -166,11 +180,6 @@ ast_fragments! { one fn visit_expr; fn make_expr; } - MethodReceiverExpr(Box) { - "expression"; - one fn visit_method_receiver_expr; - fn make_method_receiver_expr; - } Pat(Box) { "pattern"; one fn visit_pat; diff --git a/tests/ui/macros/auxiliary/semicolon-in-exprs.rs b/tests/ui/macros/auxiliary/semicolon-in-exprs.rs new file mode 100644 index 0000000000000..87a1c1786c1e4 --- /dev/null +++ b/tests/ui/macros/auxiliary/semicolon-in-exprs.rs @@ -0,0 +1,4 @@ +#[macro_export] +macro_rules! outer { + ($inner:ident) => { $inner![1, 2, 3]; }; +} diff --git a/tests/ui/macros/semicolon-in-exprs.rs b/tests/ui/macros/semicolon-in-exprs.rs new file mode 100644 index 0000000000000..1d6c9d895f067 --- /dev/null +++ b/tests/ui/macros/semicolon-in-exprs.rs @@ -0,0 +1,15 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/156084. +//! This test can probably be removed again once +//! `semicolon_in_expressions_from_macros` is a hard error. +//@ check-pass +//@ aux-build:semicolon-in-exprs.rs +//@ edition: 2021 + +extern crate semicolon_in_exprs; + +macro_rules! inner { + [$($x:expr),*] => { [$($x),*] }; +} +fn main() { + let _v: Vec = semicolon_in_exprs::outer!(inner).into_iter().collect(); +}