From cf636ec649c3a5b4bb12e343433d716118225a92 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 30 Apr 2026 21:38:02 +0300 Subject: [PATCH 1/6] 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 2/6] 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 3/6] 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 4/6] 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 9606b0bd7708d4d52dc45a7770d2ebcd3a2ec51d Mon Sep 17 00:00:00 2001 From: mehdiakiki Date: Sun, 5 Apr 2026 19:59:30 -0400 Subject: [PATCH 5/6] Add rlib digest to identify Rust object files --- compiler/rustc_codegen_gcc/src/back/lto.rs | 6 +- compiler/rustc_codegen_llvm/src/back/lto.rs | 6 +- .../rustc_codegen_ssa/src/back/archive.rs | 10 ++- compiler/rustc_codegen_ssa/src/back/link.rs | 69 +++++++++++++------ compiler/rustc_codegen_ssa/src/back/mod.rs | 1 + .../rustc_codegen_ssa/src/back/rmeta_link.rs | 56 +++++++++++++++ compiler/rustc_codegen_ssa/src/lib.rs | 19 +---- 7 files changed, 121 insertions(+), 46 deletions(-) create mode 100644 compiler/rustc_codegen_ssa/src/back/rmeta_link.rs diff --git a/compiler/rustc_codegen_gcc/src/back/lto.rs b/compiler/rustc_codegen_gcc/src/back/lto.rs index 401d4c244d5a0..4a46c59e81f0c 100644 --- a/compiler/rustc_codegen_gcc/src/back/lto.rs +++ b/compiler/rustc_codegen_gcc/src/back/lto.rs @@ -24,9 +24,10 @@ use std::path::{Path, PathBuf}; use gccjit::OutputKind; use object::read::archive::ArchiveFile; use rustc_codegen_ssa::back::lto::SerializedModule; +use rustc_codegen_ssa::back::rmeta_link; use rustc_codegen_ssa::back::write::{CodegenContext, FatLtoInput, SharedEmitter}; use rustc_codegen_ssa::traits::*; -use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind, looks_like_rust_object_file}; +use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::SelfProfilerRef; use rustc_errors::{DiagCtxt, DiagCtxtHandle}; @@ -63,6 +64,7 @@ fn prepare_lto(each_linked_rlib_for_lto: &[PathBuf], dcx: DiagCtxtHandle<'_>) -> let archive_data = unsafe { Mmap::map(File::open(path).expect("couldn't open rlib")).expect("couldn't map rlib") }; + let metadata_link = rmeta_link::read_from_data(&archive_data, path).unwrap(); let archive = ArchiveFile::parse(&*archive_data).expect("wanted an rlib"); let obj_files = archive .members() @@ -71,7 +73,7 @@ fn prepare_lto(each_linked_rlib_for_lto: &[PathBuf], dcx: DiagCtxtHandle<'_>) -> .ok() .and_then(|c| std::str::from_utf8(c.name()).ok().map(|name| (name.trim(), c))) }) - .filter(|&(name, _)| looks_like_rust_object_file(name)); + .filter(|&(name, _)| metadata_link.rust_object_files.iter().any(|f| f == name)); for (name, child) in obj_files { info!("adding bitcode from {}", name); let path = tmp_path.path().join(name); diff --git a/compiler/rustc_codegen_llvm/src/back/lto.rs b/compiler/rustc_codegen_llvm/src/back/lto.rs index 09863961c9d69..f17cfe2103f93 100644 --- a/compiler/rustc_codegen_llvm/src/back/lto.rs +++ b/compiler/rustc_codegen_llvm/src/back/lto.rs @@ -8,11 +8,12 @@ use std::{io, iter, slice}; use object::read::archive::ArchiveFile; use object::{Object, ObjectSection}; use rustc_codegen_ssa::back::lto::{SerializedModule, ThinModule, ThinShared}; +use rustc_codegen_ssa::back::rmeta_link; use rustc_codegen_ssa::back::write::{ CodegenContext, FatLtoInput, SharedEmitter, TargetMachineFactoryFn, ThinLtoInput, }; use rustc_codegen_ssa::traits::*; -use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind, looks_like_rust_object_file}; +use rustc_codegen_ssa::{CompiledModule, ModuleCodegen, ModuleKind}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::profiling::SelfProfilerRef; @@ -96,6 +97,7 @@ fn prepare_lto( .expect("couldn't map rlib") }; let archive = ArchiveFile::parse(&*archive_data).expect("wanted an rlib"); + let metadata_link = rmeta_link::read(&archive, &archive_data, &path).unwrap(); let obj_files = archive .members() .filter_map(|child| { @@ -103,7 +105,7 @@ fn prepare_lto( .ok() .and_then(|c| std::str::from_utf8(c.name()).ok().map(|name| (name.trim(), c))) }) - .filter(|&(name, _)| looks_like_rust_object_file(name)); + .filter(|&(name, _)| metadata_link.rust_object_files.iter().any(|f| f == name)); for (name, child) in obj_files { info!("adding bitcode from {}", name); match get_bitcode_slice_from_object_data( diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 3f12e857391b2..baafd0719f361 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -21,6 +21,7 @@ use rustc_target::spec::Arch; use tracing::trace; use super::metadata::{create_compressed_metadata_file, search_for_section}; +use super::rmeta_link::{self, RmetaLink}; use crate::common; // Public for ArchiveBuilderBuilder::extract_bundled_libs pub use crate::errors::ExtractBundledLibsError; @@ -314,7 +315,7 @@ pub trait ArchiveBuilder { fn add_archive( &mut self, archive: &Path, - skip: Box bool + 'static>, + skip: Option) -> bool + 'static>>, ) -> io::Result<()>; fn build(self: Box, output: &Path) -> bool; @@ -402,7 +403,7 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { fn add_archive( &mut self, archive_path: &Path, - mut skip: Box bool + 'static>, + mut skip: Option) -> bool + 'static>>, ) -> io::Result<()> { let mut archive_path = archive_path.to_path_buf(); if self.sess.target.llvm_target.contains("-apple-macosx") @@ -418,13 +419,16 @@ impl<'a> ArchiveBuilder for ArArchiveBuilder<'a> { let archive_map = unsafe { Mmap::map(File::open(&archive_path)?)? }; let archive = ArchiveFile::parse(&*archive_map) .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; + let metadata_link = + skip.as_ref().and_then(|_| rmeta_link::read(&archive, &archive_map, &archive_path)); let archive_index = self.src_archives.len(); for entry in archive.members() { let entry = entry.map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; let file_name = String::from_utf8(entry.name().to_vec()) .map_err(|err| io::Error::new(io::ErrorKind::InvalidData, err))?; - if !skip(&file_name) { + let drop = skip.as_mut().is_some_and(|f| f(&file_name, metadata_link.as_ref())); + if !drop { if entry.is_thin() { let member_path = archive_path.parent().unwrap().join(Path::new(&file_name)); self.entries.push((file_name.into_bytes(), ArchiveEntry::File(member_path))); diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index cb22aac4e952d..afd76ebd4d9cd 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -58,12 +58,9 @@ use super::command::Command; use super::linker::{self, Linker}; use super::metadata::{MetadataPosition, create_wrapper_file}; use super::rpath::{self, RPathConfig}; -use super::{apple, versioned_llvm_target}; +use super::{apple, rmeta_link, versioned_llvm_target}; use crate::base::needs_allocator_shim_for_linking; -use crate::{ - CodegenLintLevels, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors, - looks_like_rust_object_file, -}; +use crate::{CodegenLintLevels, CompiledModule, CompiledModules, CrateInfo, NativeLib, errors}; pub fn ensure_removed(dcx: DiagCtxtHandle<'_>, path: &Path) { if let Err(e) = fs::remove_file(path) { @@ -307,6 +304,27 @@ fn link_rlib<'a>( ) -> Box { let mut ab = archive_builder_builder.new_archive_builder(sess); + // Pre-compute the list of Rust object filenames and materialize the rmeta-link + // wrapper file before any `add_file` calls. This lets the rmeta-link member be + // placed immediately after metadata in the archive, so consumers can find + // it without iterating every archive member. + let rust_object_files: Vec = compiled_modules + .modules + .iter() + .filter_map(|m| m.object.as_ref()) + .map(|obj| obj.file_name().unwrap().to_str().unwrap().to_string()) + .collect(); + + let metadata_link_file = if matches!(flavor, RlibFlavor::Normal) { + let metadata_link = rmeta_link::RmetaLink { rust_object_files }; + let metadata_link_data = metadata_link.encode(); + let (wrapper, _) = + create_wrapper_file(sess, rmeta_link::SECTION.to_string(), &metadata_link_data); + Some(emit_wrapper_file(sess, &wrapper, tmpdir.as_ref(), rmeta_link::FILENAME)) + } else { + None + }; + let trailing_metadata = match flavor { RlibFlavor::Normal => { let (metadata, metadata_position) = @@ -320,6 +338,11 @@ fn link_rlib<'a>( // If it is possible however, placing the metadata object first improves // performance of getting metadata from rlibs. ab.add_file(&metadata); + // Place the rmeta-link member immediately after metadata so consumers + // can find it without iterating the whole archive. + if let Some(file) = &metadata_link_file { + ab.add_file(file); + } None } MetadataPosition::Last => Some(metadata), @@ -383,7 +406,7 @@ fn link_rlib<'a>( packed_bundled_libs.push(wrapper_file); } else { let path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess); - ab.add_archive(&path, Box::new(|_| false)).unwrap_or_else(|error| { + ab.add_archive(&path, None).unwrap_or_else(|error| { sess.dcx().emit_fatal(errors::AddNativeLibrary { library_path: path, error }) }); } @@ -400,7 +423,7 @@ fn link_rlib<'a>( tmpdir.as_ref(), true, ) { - ab.add_archive(&output_path, Box::new(|_| false)).unwrap_or_else(|error| { + ab.add_archive(&output_path, None).unwrap_or_else(|error| { sess.dcx() .emit_fatal(errors::AddNativeLibrary { library_path: output_path, error }); }); @@ -434,6 +457,11 @@ fn link_rlib<'a>( // Basically, all this means is that this code should not move above the // code above. ab.add_file(&trailing_metadata); + // Place the rmeta-link member immediately after metadata so consumers can + // find it without iterating the whole archive. + if let Some(file) = &metadata_link_file { + ab.add_file(file); + } } // Add all bundled static native library dependencies. @@ -488,14 +516,16 @@ fn link_staticlib( let bundled_libs: FxIndexSet<_> = native_libs.filter_map(|lib| lib.filename).collect(); ab.add_archive( path, - Box::new(move |fname: &str| { - // Ignore metadata files, no matter the name. - if fname == METADATA_FILENAME { + Some(Box::new(move |fname: &str, metadata_link| { + // Ignore metadata and rmeta-link files. + if fname == METADATA_FILENAME || fname == rmeta_link::FILENAME { return true; } - // Don't include Rust objects if LTO is enabled - if lto && looks_like_rust_object_file(fname) { + // Don't include Rust objects if LTO is enabled. + if lto + && metadata_link.is_some_and(|m| m.rust_object_files.iter().any(|f| f == fname)) + { return true; } @@ -505,7 +535,7 @@ fn link_staticlib( } false - }), + })), ) .unwrap(); @@ -516,7 +546,7 @@ fn link_staticlib( for filename in relevant_libs.iter() { let joined = tempdir.as_ref().join(filename.as_str()); let path = joined.as_path(); - ab.add_archive(path, Box::new(|_| false)).unwrap(); + ab.add_archive(path, None).unwrap(); } all_native_libs.extend(crate_info.native_libraries[&cnum].iter().cloned()); @@ -3146,7 +3176,6 @@ fn add_static_crate( let bundled_lib_file_names = bundled_lib_file_names.clone(); sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| { - let canonical_name = name.replace('-', "_"); let upstream_rust_objects_already_included = are_upstream_rust_objects_already_included(sess); let is_builtins = sess.target.no_builtins || !crate_info.is_no_builtins.contains(&cnum); @@ -3154,15 +3183,13 @@ fn add_static_crate( let mut archive = archive_builder_builder.new_archive_builder(sess); if let Err(error) = archive.add_archive( cratepath, - Box::new(move |f| { - if f == METADATA_FILENAME { + Some(Box::new(move |f, metadata_link| { + if f == METADATA_FILENAME || f == rmeta_link::FILENAME { return true; } - let canonical = f.replace('-', "_"); - let is_rust_object = - canonical.starts_with(&canonical_name) && looks_like_rust_object_file(f); + metadata_link.is_some_and(|m| m.rust_object_files.iter().any(|rf| rf == f)); // If we're performing LTO and this is a rust-generated object // file, then we don't need the object file as it's part of the @@ -3182,7 +3209,7 @@ fn add_static_crate( } false - }), + })), ) { sess.dcx() .emit_fatal(errors::RlibArchiveBuildFailure { path: cratepath.clone(), error }); diff --git a/compiler/rustc_codegen_ssa/src/back/mod.rs b/compiler/rustc_codegen_ssa/src/back/mod.rs index 8d1adb9993038..17f6faa942c49 100644 --- a/compiler/rustc_codegen_ssa/src/back/mod.rs +++ b/compiler/rustc_codegen_ssa/src/back/mod.rs @@ -9,6 +9,7 @@ pub mod link; pub(crate) mod linker; pub mod lto; pub mod metadata; +pub mod rmeta_link; pub(crate) mod rpath; pub mod symbol_export; pub mod write; diff --git a/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs b/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs new file mode 100644 index 0000000000000..68b23ca9ac5cb --- /dev/null +++ b/compiler/rustc_codegen_ssa/src/back/rmeta_link.rs @@ -0,0 +1,56 @@ +//! Late-metadata archive member that lists which rlib entries are Rust object files, +//! and potentially other data collected and used when building or linking a rlib. +//! See . + +use std::path::Path; + +use object::read::archive::ArchiveFile; +use rustc_serialize::opaque::mem_encoder::MemEncoder; +use rustc_serialize::opaque::{MAGIC_END_BYTES, MemDecoder}; +use rustc_serialize::{Decodable, Encodable}; + +use super::metadata::search_for_section; + +pub(crate) const FILENAME: &str = "lib.rmeta-link"; +pub(crate) const SECTION: &str = ".rmeta-link"; + +pub struct RmetaLink { + pub rust_object_files: Vec, +} + +impl RmetaLink { + pub(crate) fn encode(&self) -> Vec { + let mut encoder = MemEncoder::new(); + self.rust_object_files.encode(&mut encoder); + let mut data = encoder.finish(); + data.extend_from_slice(MAGIC_END_BYTES); + data + } + + pub(crate) fn decode(data: &[u8]) -> Option { + let mut decoder = MemDecoder::new(data, 0).ok()?; + let rust_object_files = Vec::::decode(&mut decoder); + Some(RmetaLink { rust_object_files }) + } +} + +/// Reads the link-time metadata from an already-parsed archive. +pub fn read(archive: &ArchiveFile<'_>, archive_data: &[u8], rlib_path: &Path) -> Option { + for entry in archive.members() { + let entry = entry.ok()?; + if entry.name() == FILENAME.as_bytes() { + let data = entry.data(archive_data).ok()?; + let section_data = search_for_section(rlib_path, data, SECTION).ok()?; + return RmetaLink::decode(section_data); + } + } + None +} + +/// Like [`read`], but parses the archive from raw bytes. +/// +/// Use this when the caller's `ArchiveFile` comes from a different version of the `object` crate. +pub fn read_from_data(archive_data: &[u8], rlib_path: &Path) -> Option { + let archive = ArchiveFile::parse(archive_data).ok()?; + read(&archive, archive_data, rlib_path) +} diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 1c266382d0279..22290d672c611 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -35,7 +35,7 @@ use rustc_middle::util::Providers; use rustc_serialize::opaque::{FileEncoder, MemDecoder}; use rustc_serialize::{Decodable, Decoder, Encodable, Encoder}; use rustc_session::Session; -use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT}; +use rustc_session::config::{CrateType, OutputFilenames, OutputType}; use rustc_session::cstore::{self, CrateSource}; use rustc_session::lint::builtin::LINKER_MESSAGES; use rustc_span::Symbol; @@ -272,23 +272,6 @@ pub fn provide(providers: &mut Providers) { providers.queries.global_backend_features = |_tcx: TyCtxt<'_>, ()| vec![]; } -/// Checks if the given filename ends with the `.rcgu.o` extension that `rustc` -/// uses for the object files it generates. -pub fn looks_like_rust_object_file(filename: &str) -> bool { - let path = Path::new(filename); - let ext = path.extension().and_then(|s| s.to_str()); - if ext != Some(OutputType::Object.extension()) { - // The file name does not end with ".o", so it can't be an object file. - return false; - } - - // Strip the ".o" at the end - let ext2 = path.file_stem().and_then(|s| Path::new(s).extension()).and_then(|s| s.to_str()); - - // Check if the "inner" extension - ext2 == Some(RUST_CGU_EXT) -} - const RLINK_VERSION: u32 = 1; const RLINK_MAGIC: &[u8] = b"rustlink"; 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 6/6] 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::; | ^